WPF Problems with Custom Control - wpf

I have a Custom Control in WPF
public class MyClass: Control, INotifyPropertyChanged
{
private Boolean _hasData;
public Boolean HasData
{
get { return _hasData};
set
{
_hasData = value;
OnPropertyChanged("HasData");
this.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
}
}
#region INotifyPropertyChanged members
// code
#endregion
}
Now here's the thing: should i use a Control Template or a Data Template?
purpose of custom control: showing data that i recieved from a service.
I tried a Custom Template, but the properties of the control aren't bound/connected with the properties of XAML code. The DataContext of my Control Template is the control itself (MyClass).
<ControlTemplate TargetType="{x:Type controls:MyClass}">
<Grid Visibility="{Binding Visibility, UpdateSourceTrigger=PropertyChanged}"}">
<TextBlock Text="Contains Data"/>
</Grid>
</ControlTemplate>
If i check the DataContext (which is the Myclass class), the visibility is Visible or Collapsed. the Visibility of the control (Myclass XAML) won't bind to the DataContext Visibility.
Also, if i set the visibility in the constructor to Collapsed, then it remains being on Collapsed. i also tried triggers and an extra Boolean property Show that is binded to the Grid Visibility (with a converter of course).
What should i do now? i just want that some Control properties like Visibility in MyClass Control have the same value as class MyClass.

Related

wp8 Binding CheckBox to CustomMessageBox via DataTemplate

I make my own Control.
Inside that I want to define data template to use it in Custom message box.
In code I open this dialog but can't set start value to check box inside it.
Please help me - how to correctly bind cbVoiceAttChecked variable to CustomMessageBox via DataTemplate named VoiceTemplate
XAML:
<UserControl x:Class="myProj.RDPControl"
...
>
<UserControl.Resources>
<DataTemplate x:Key="VoiceTemplate" >
<StackPanel Margin="32,0,0,0">
<CheckBox x:Name="cbVoiceAtt" Content="..." IsChecked="{Binding cbVoiceAttChecked}"/>
... /*Other checkboxes*/
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
... Here is main control - works perfectly
</Grid>
In code
public partial class RDPControl : UserControl
{
public RDPControl()
{
InitializeComponent();
//this.DataContext = this;
}
public bool cbVoiceAttChecked { get; set; }
....
private void VoiceButton_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
cbVoiceAttChecked = true; // This value binding to temlate!!!
CustomMessageBox messageBox = new CustomMessageBox()
{
Caption = "...",
Message = "...",
ContentTemplate = (DataTemplate)(this.Resources["VoiceTemplate"]), // Use template from xaml
DataContext = this, // I want to use cbVoiceAttChecked variable to bind to dialog
LeftButtonContent = "yes",
RightButtonContent = "no"
};
...
messageBox.Show();
}
You need to either change your cbVoiceAttChecked property into a DependencyProperty, or implement the INotifyPropertyChanged interface in your RDPControl class.
You can find out more about the INotifyPropertyChanged Interface in the INotifyPropertyChanged Interface on MSDN and about DependencyPropertys in the DependencyProperty Class and Dependency Properties Overview pages on MSDN.
Of course, it all depends on what you are doing with the ContentTemplate object inside your RDPControl class. As you did not show that, I cannot confirm that making the above change will fix your problem.

Checkbox at MainWindow that sets values to controls located on underlaying UserControls

In MainWindow class I have checkbox that controls property used by many objects like grids, listviews, etc in UserControls
<CheckBox Content="Show objects ID" Name="showID" IsChecked="False" />
than there is property defined,
public Visibility ShowObjectIDasVisibility
{
get { return showID.IsChecked.Equals(true) ? Visibility.Visible : Visibility.Collapsed; }
}
I have some more like this to return boolean, width depending on what should be used on target control.
I managed to bind controls located in UserControl objects to use this property like this:
<TextBlock Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=ShowObjectIDasVisibility}" />
But it works only ones, while creating this TextBlock, than I can toggle checkbox as many times I like, and the TextBlock will stay visible or not depending on first value.
How should I do this properly? Thanks.
Instead of INotifyPropertyChanged interface you can use DependencyProperty:
public Visibility ShowObjectIDasVisibility
{
get { return (Visibility)GetValue(ShowObjectIDasVisibilityProperty); }
set { SetValue(ShowObjectIDasVisibilityProperty, value); }
}
public static readonly DependencyProperty ShowObjectIDasVisibilityProperty =
DependencyProperty.Register("ShowObjectIDasVisibility", typeof(Visibility), typeof(MainWindow), new PropertyMetadata(Visibility.Collapsed));
Now, to show/hide your TextBlock you need to change ShowObjectIDasVisibility value.
For example, you can do it by adding to checkbox Click="OnShowID_Click and in code behind
private void OnShowID_Click(object sender, RoutedEventArgs e)
{
ShowObjectIDasVisibility = ShowObjectIDasVisibility == System.Windows.Visibility.Visible ? System.Windows.Visibility.Collapsed : System.Windows.Visibility.Visible;
}
if your binding is correct. you just need to make sure that your code class is implementing INotifyPropertyChanged interface in class binded to view and you are raising RaisePropertyChanged event in every checkbox state change. For more details look at example here.

Passing WPF user control selection to host control

I have a WPF user control with a list box. I want to pass the selected item in the list box to the calling control through binding. How can I achieve this?
You can expose a new property for SelectedItem on your user control and bind it to the child control ListBox.
Code for your user control (I inherited from Control though):
public class CustomListControl : Control
{
static CustomListControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomListControl), new FrameworkPropertyMetadata(typeof(CustomListControl)));
SelectedItemProperty = ListBox.SelectedItemProperty.AddOwner(typeof(CustomListControl));
}
public static readonly DependencyProperty SelectedItemProperty;
public Object SelectedItem
{
get { return this.GetValue(SelectedItemProperty); }
set { this.SetValue(SelectedItemProperty, value); }
}
}
And add the binding from the inner ListBox to your UserControl in the Generic.xaml markup:
<ListBox
SelectedItem="{Binding RelativeSource={RelativeSource AncestorLevel=1, AncestorType={x:Type local:CustomListControl},Mode=FindAncestor},Path=SelectedItem, Mode=TwoWay}"
</ListBox>

WPF ObservableCollection in xaml

I have created an ObservableCollection in the code behind of a user control. It is created when the window loads:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Entities db = new Entities();
ObservableCollection<Image> _imageCollection =
new ObservableCollection<Image>();
IEnumerable<library> libraryQuery =
from c in db.ElectricalLibraries
select c;
foreach (ElectricalLibrary c in libraryQuery)
{
Image finalImage = new Image();
finalImage.Width = 80;
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri(c.url);
logo.EndInit();
finalImage.Source = logo;
_imageCollection.Add(finalImage);
}
}
I need to get the ObservableCollection of images which are created based on the url saved in a database. But I need a ListView or other ItemsControl to bind to it in XAML file like this:
But I can't figure it out how to pass the ObservableCollection to the ItemsSource of that control. I tried to create a class and then create an instance of a class in xaml file but it did not work. Should I create a static resource somehow>
Any help will be greatly appreciated.
Firstly, the ObservableCollection is a local variable. What you need to do is have it as a private global variable and expose it with a public property. You can use the INotifyPropertyChanged interface to have the image data update automagically when the actual collection itself changes.
In your XAML, you then need to set the DataContext to self, and you can then directly bind your public property to the ItemsSource. You may want to use an ItemTemplate for displaying the items in a custom manner.
Cheers,
Adam
Example as requested:
In C#:
public MyWindowClass
{
public ObservableCollection<image> MyImageCollection
{
get;
set;
}
}
In XAML:
<UserControl
...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
...
<ListBox ItemsSource="{Binding MyImageCollection}" ItemTemplate="*yourtemplateresource*" />
...
</UserControl>
Now, the reason that I mentioned using INotifyPropertyChanged is that if you try:
MyImageCollection = new ObservableCollection<image>();
The items in the listbox will not automatically update. With an ObservableCollection, however, you do not need to implement INotifyPropertyChanged for basic addition and removal of list items.
You have to set the DataContext of the UserControl to your collection:
DataContext = _imageCollection
You can do that in the UserControl_Loaded() method.
Next you need to bind the ItemsSource of the ListView in the XAML:
<ListView ItemsSource="{Binding}"/>
The {Binding} is equivalent to {Binding .} which binds to the DataContext of the UserControl. If you need "more stuff" in your DataContext you can instead create a class like this:
class ViewModel : INotifyPropertyChanged {
public ObservableCollection Images { get { ... } }
...
}
Use this class for the DataContext:
DataContext = new ViewModel();
And replace the binding to bind to the Images property:
<ListView ItemsSource="{Binding Images}"/>
Then you can add another property to ViewModel:
class ViewModel : INotifyPropertyChanged {
public ObservableCollection Images { get { ... } }
public String Message { get { ... } set { ... } }
...
}
And bind it to a control:
<TextBlock Text="{Binding Message}"/>
Remember to fire the PropertyChanged event when the Message property is changed in ViewModel. This will update the UI when view-model properties are changed by code.

CheckedItems property for custom CheckBoxList control in Silverlight

I need to implement CheckBoxList control with ItemsSource and CheckedItems properties. Items from ItemsSource should be displayed as checked checkboxes if CheckedItems contains these values or unchecked otherwise. Also I need two-way databinding support for CheckedItems property (value of this property should be updated when user clicks on checkboxes).
Here some code which probably can help to understand my problem
XAML:
<UserControl x:Class="Namespace.Controls.CheckBoxList" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ListBox x:Name="LayoutRoot">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
Code behind:
public partial class CheckBoxList : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CheckBoxList), null);
public static readonly DependencyProperty CheckedItemsProperty = DependencyProperty.Register("CheckedItems", typeof(IEnumerable), typeof(CheckBoxList), null);
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public IEnumerable CheckedItems
{
get { return (IEnumerable)GetValue(CheckedItemsProperty); }
set { SetValue(CheckedItemsProperty, value); }
}
public CheckBoxList()
{
InitializeComponent();
LayoutRoot.SetBinding(ItemsControl.ItemsSourceProperty, new Binding("ItemsSource") { Source = this });
}
}
I think that I need to bind ListBox to UserControl with custom converter, which will return collection of items with additional IsChecked property, but it works only in case of one-way data binding.
Looks like I need two-way binding to two properties at one time, but I don't know how to implement it and will appreciate any help with this issue.
Thanks in advance.
First of all you should consider deriving from ListBox rather than UserControl. The ListBox already does most of what you want.
Secondly consider one way binding to an IList. You can then add and remove entires to that IList as the respective items are selected.
Rather than try to bind a CheckBox control in an Item Template you make a copy of the ListBox styles, place them in Generic.xaml as the style of your new control. Then modify the unselected and selected visual states using a checked and unchecked check box as part of the visual appearance.
Now you can attach to the SelectionChanged event and use the Event args AddedItems list to add to the bound IList and the RemovedItems list to remove items from the bound list.
You would need to clear and re-add the set of items to the list box SelectedItems list when either your CheckedItems is assigned or the ItemsSource is changed.
There are probably a number gotchas that you will need to work round but this seems like a more direct path to your goal than starting from scratch with a UserControl base.
Add an observable collection for your list box datasource to your datacontext:
private ObservableCollection<MyItem> _myItems;
public ObservableCollection<MyItem> MyItems
{
get { return _searchByFields; }
set
{
_myItems = value;
}
}
Add a class to hold the data about your checkboxes:
public class MyItem
{
public bool Checked {get; set; }
public string MyItemValue { set ; set; }
}
Then in your data template bind listbox to the collection and your data template checkboxes to the respective MyItem properties:
<UserControl x:Class="Namespace.Controls.CheckBoxList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ListBox x:Name="LayoutRoot"
DataContext="[Dataconext here]"
ItemsSource={Binding MyItems}>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Checked, Mode=TwoWay}"
Content="{Binding MyItemValue}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
Don't forget to set the DataContext of the binding to the appropriate class (you might be doing this in the XAML or the code behind perhaps)

Resources