WPF: DependencyProperty of custom control fails when using several instances of the control - wpf

I've built a custom control in WPF that inherits from ListBox. In this I have implementet my own property that is a BindingList. To make this property bindable I've implemeneted it as a DependencyProperty:
public BindingList<CheckableListItem> CheckedItems
{
get
{
return (BindingList<CheckableListItem>)GetValue(MultiComboBox.CheckedItemsProperty);
}
set
{
SetValue(MultiComboBox.CheckedItemsProperty, value);
}
}
public static readonly DependencyProperty CheckedItemsProperty;
I register this DependencyProperty in a static constructor inside my custom control:
CheckedItemsProperty = DependencyProperty.Register("CheckedItems",
typeof(BindingList<CheckableListItem>),
typeof(MultiComboBox),
new FrameworkPropertyMetadata(new BindingList<CheckableListItem>()));
(MultiComboBox is the name of my custom control. CheckableListItem is a simple class I've written just for this purpose).
This BindingList is then updated inside the custom control (never outside) as the user interacts with it.
When I use my custom control in XAML I bind to the CheckItems property with the mode "OneWayToSource". I'm using the MVVM pattern and the property in the ViewModel that I'm binding to is also a BindingList. The ViewModel never affects this list, it just reacts at the changes that the custom control make to the list. The property in the ViewModel looks like this:
private BindingList<CheckableListItem> _selectedItems;
public BindingList<CheckableListItem> SelectedItems
{
get
{
return _selectedItems;
}
set
{
if (value != _selectedItems)
{
if (_selectedItems != null)
{
_selectedItems.ListChanged -= SelectedItemsChanged;
}
_selectedItems = value;
if (_selectedItems != null)
{
_selectedItems.ListChanged += SelectedItemsChanged;
}
OnPropertyChanged("SelectedItems");
}
}
}
As you can see I'm listening to changes made to the list (these changes always occur inside my custom control), and in the "SelectedItemsChanged"-method I update my Model accordingly.
Now...this works great when I have one of these controls in my View. However, if I put two (or more) of them in the same View strange things start to happen. This will of course mean that I'll have two lists with selected items in my ViewModel. But if do something in the View that changes one of the lists, both lists are affected! That is, the event handlers for the event ListChanged is triggered for both list if changes are made to any one of them!
Does anyone recognize this problem and/or have a solution to it? What is wrong with my implementation?
My first though is that the DependencyProperty is static. Normally that means shared between all instances. But I guess DependencyProperties work in some other "magical" way so that might not be the problem.
Any tips or hints is appreciated!

I had a similar problem with a collection-type dependency property. My solution was taken from the MSDN article on Collection-Type Dependency Properties. It was adding the following line
SetValue(OperatorsPropertyKey, new List<ListBoxItem>()); //replace key and type
in the constructor of my control because it seems that a collection-type dependency property constructor is being called only once no matter how many instances your control containing this collection has (static eh).

This sounds like you bound both/all the Views to the same ViewModel. That would explain that changes to one cause changes in the other.

Related

WPF MVVM ViewModel containing ViewModels not updating

I'm very new to WPF and MVVM, and it's been causing me a lot of headaches. Due to issues with navigation, I decided to just have all my content visible at once. I thought I would create a new ViewModel (MainViewModel) to contain my two other ViewModels (StudentViewModel and AddStudentsViewModel).
MainViewModel contains something like this:
private StudentViewModel _studentVM;
private AddStudentsViewModel _addStudentsVM;
public StudentViewModel StudentVM
{
get { return _studentVM; }
set
{
if (_studentVM != value)
{
_studentVM = value;
NotifyPropertyChanged("StudentVM");
}
}
}
(public AddStudentsViewModel AddStudentsVM exists as well, I'm just trying to keep this short)
I have successfully bound StudentVM and AddStudentsVM to my main View, as I can programmatically set values during the initialization phase and when debugging, I can see my button clicks are being redirected to the correct methods. It even seems like I am successfully adding students to objects, however my main View isn't reflecting these changes.
Am I missing something in MainViewModel? Or is it not possible for a ViewModel to see the changes in any other ViewModels inside it?
If your view is bound to some property inside StudentViewModel through nested navigation into the property StudentVM like StudentVM.Property, then to reflect changes on StudentVM.Property unto your view would require that you notify the view that StudentVM (not StudentVM.Property) has changed.
So it depends on how you defined your binding and on which property you're raising the PropertyChanged event.
A viewmodel which contains two other viewmodels. Just think about it for a second. It's not a good idea.
Anyway, you have to implement INotifyPropertyChanged in your containing viewmodels as well. Not only in the containing MainViewModel.
Maybe that is the fault?

How do you call identically named properties on different types which don't share an interface?

I have a DataTemplate that needs to set the IsSelected property on an ItemsControl's container (such as TreeViewItem, ListViewItem or ComboBoxItem). However, it doesn't know the type of the container until it's passed in to it. Since IsSelected isn't part of a common base class or interface, nor is it a common dependency property registered with AddOwner to the various classes (Duh, MS!!! WTF not?!!), I ended up with this mess...
if (container is TreeViewItem) {
(container as TreeViewItem).IsSelected = true;
return;
}
if (container is ListBoxItem) {
(container as ListBoxItem).IsSelected = true;
return;
}
if (container is ComboBoxItem) {
(container as ComboBoxItem).IsSelected = true;
return;
}
...which not only is verbose, but requires me to modify it if I ever use a different ItemsControl that uses different container types! Not good!
Sure I could enhance it a little by putting this logic in extension methods (damn C# for not having extension properties!!) called IsContainerSelected and SetContainerSelected and putting them on UIElement, then moving the above code inside there, but it's just making the outside neater. The inside is still a mess.
My only other thought is to use reflection and look for an IsSelected property and use that if found, but I'm always leery of doing things like that. However, since there isn't a common interface or base class, I'm not really sure I have a choice here.
For context, I'm sharing a complex data template between several different ItemsControls and the template itself has controls that can receive focus such as checkbox and textbox. However, when those controls receive focus via the mouse, the underlying container item doesn't get selected and whatever was selected before remains so.
My workaround is to use an attached behavior that utilizes the preview events to intercept the focus before it happens and set the underlying item accordingly, which works great when I've hard-coded TreeViewItem or ListBoxItem, etc., but I don't want to hard-code the type since the control shouldn't really care. So that's the part that breaks down.
Ugh!!! Why didn't MS just register the same attached property or at least create an ISelectableContainer interface?!!
I have read your answer, and it does make sense - in your case, IsSelected may obviously be part of the ViewModel, and that seems to be the best solution in your case.
But you asked for further explanation about C# dynamic features. C# 4.0 now has some dynamic functionalities, which allow us to create code that would only be possible in languages like Python, Ruby or JavaScript. This, of course, has its cost - a dynamic abuse would not only make code slower, but also more confusing - because you would lose compile-time errors and IntelliSense.
I have written a simple example so you may understand it better:
public class ClassOne
{
public int SameProperty { get; set; }
}
public class ClassTwo
{
public int SameProperty { get; set; }
}
public class ClassThree
{
public string SameProperty { get; set; }
}
public partial class Form1 : Form
{
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
dynamic wrapper = new ClassOne();
wrapper.SameProperty = 5;
wrapper = new ClassTwo();
wrapper.SameProperty = 15;
wrapper = new ClassThree();
wrapper.SameProperty = "Now it is a string!";
// And now a run-time error...
wrapper.AnotherProperty = "And this won't work...";
}
}
As you can see, wrapper has no definite type whatsoever - a dynamic reference will allow any kind of method or property invocation, since the actual binding will only be made during run-time, not compile-time.
Of course, this example is very naive, but sometimes dynamic code may be useful - it is a good option to avoid explicit reflection, or to avoid long if...else statements based on type (like your snippet above).
I'm not sure that I fully understand your problem, but you could try adding an IsSelected boolean to your model and then binding that property against the Item control it's contained in. That way, you just have to worry about setting that property in the model, regardless of the container.
Per #mdm20's answer, he suggested modifying the ViewModel, which is of course normally what you want to do. However this is a purely view-related issue (keyboard navigation-related) and isn't reflected in the ViewModel at all, nor in this case should it be.
But that gave me an idea! Since I'm using a custom control to render the item in whichever items control (via its data template) it's being added to, that control naturally does have multiple instances (all of which are pointing to the same ViewModel instance), which is what I want!
Therefore, rather than adding the IsSelected to the ViewModel, I added it to the user control itself, then I just bind to that within the data template for the respective ItemsControl which I do know about. I can then set the IsSelected property in the code-behind for the user control as needed (i.e. during the preview mouse events, etc.) and the underlying ItemsControl responds appropriately! Works great and keeps the ViewModel clean since neither the model, nor the viewmodel need to know about it. The IsSelected remains purely in the UI which is where in this particular case it should be!

WPF Exposing a calculated property for binding (as DependencyProperty)

I have a complex WPF control that for some reasons (ie. performance) is not using dependency properties but simple C# properties (at least at the top level these are exposed as properties).
The goal is to make it possible to bind to some of those top level properties -> I guess I should declare them as DPs.(right? or is there some other way to achieve this? )
I started reading on MSDN about DependencyProperties and DependencyObjects and found an example:
public class MyStateControl : ButtonBase
{
public MyStateControl() : base() { }
public Boolean State
{
get { return (Boolean)this.GetValue(StateProperty); }
set { this.SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(
"State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false));
}
If I'm right - this code enforces the property to be backed up by DependencyProperty which restricts it to be a simple property with a store(from functional point of view, not technically) instead of being able to calculate the property value each time getter is called and setting other properties/fields each time setter is called.
What can I do about that? Is there any way I could make those two worlds meet at some point?
//edit
I guess I have to tell you a little more about what I want to do and what my limitations are. So:
I have TabControl that is bound to a collection of ViewModel(I'm using MVVM pattern) objects. Every tab is meant to be an editor for one object of that collection. Objects can be of different types so I have multiple definitions each with a different DataType property. Now I have that complex WPF Control that I want to use as a part of one of those DataTemplates. If I use usual TextBox I can simply bind to its Text property, but I can't do the same with Text property of that custom control simply because its Text property is not a dependency property.
In this scenario I have :
no direct access to the control itself nor to its events
no code behind file that I can use to do that kind of thing
I can see however a dirty solution -
In the Window class I would have to subscribe to CollectionChanged event of the collection that is bound to the TabControl.
Whenever an item is added to that collection use ItemContainerGenerator to obtain a copy of I suppose TabItem and use it to find the right copy of 'complex control'
Regiter items handlers to 'complex controls' events to do the job
This is wrong because:
this is agains MVVM - I have to play with tab control to do the job instead of doing it in the ViewModel class
this couples in an unwanted way the view and viewmodel
I think you are mixing up Dependency Properties and implementing INotifyPropertyChanged on your classes.
You don't need your property to be a dependency property, you just need your class to implement INotifyPropertyChanged and call OnPropertyChanged whenever the state of your object changes in a way that would affect the value you want to expose to binding.
So let's say you have a property Sum that you want to bind to. The Sum property simple adds two other properties (or fields, or whatever) together. When anything happens that affects the Sum calculation, you want to notify that the Sum value has changed, so the any controls bound to Sum get updated.
public int Sum => Value1 + Value2;
public int Value1
{
set
{
// changing this affects "Sum", so I need to notify that the binding should update
_value1 = value;
OnPropertyChanged("Sum");
}
}
public int Value2
{
set
{
// changing this affects "Sum", so I need to notify that the binding should update
_value2 = value;
OnPropertyChanged("Sum");
}
}
It seems to me that you've been saddled with a WPF user control that was built by someone who didn't intend it to be used with data binding. I would assume that this is for one of two reasons: a) there's some logical reason that you shouldn't be able to bind to this property, or b) the original author of this control didn't know what he was doing. You haven't provided enough information for me to know which of those two conditions is the one you're really working under.
But in general, the reason you can't expose calculated properties for binding is that calculated properties generally don't have a setter. It doesn't make sense to set a property whose value is calculated.
If there are other properties whose values need to be updated when this one changes, the right approach (or at least the one consonant with the design of dependency properties) is to handle those updates in the dependency property's callback function, which is kind of what the callback function is for.

WPF - CanExecute doesn't fire when raising Commands from a UserControl

I've got a button strip usercontrol that I want to have on most of my forms.
I've added the commands as follows ...
public ICommand Create
{
get
{
return buttonCreate.Command;
}
set
{
buttonCreate.Command = value;
}
}
I've set these as dependency properties so I can bind to them ...
public static readonly DependencyProperty CreateCommandProperty =
DependencyProperty.Register(
"Create",
typeof(ICommand),
typeof(StandardButtonStrip),
new PropertyMetadata((ICommand)null));
I'm then binding my usercontrol to a command ...
<commoncontrols:StandardButtonStrip HorizontalAlignment="Stretch" Create="{Binding CreateCommand}" />
I'm setting up the command as follows ...
_viewModel.CreateCommand = new DelegateCommand<object>(OnCreateCommand, CanCreate);
but despite the fact that i'm always returning true on my CanCreate method the button is disabled ... if I put a break point on return true it never fires!
public bool CanCreate(object parm)
{
return true;
}
I've tried this to see if it will refresh the binding, but no joy!
_viewModel.CreateCommand.RaiseCanExecuteChanged();
I think the problem is down to the user control and how I'm passing the Command up as a property, but not sure ...
The way this looks, you have a dependency property on a view model. If you are really using MVVM, that is definetly not the way to go about it (not because of religious adherence to a pattern, but because it's not the optimal way).
First of all, is your view model a DependencyObject?
If it is, you should downgrade it to a class which implements INotifyPropertyChanged. Why? Because the Button's Command property is a DependencyProperty itself (inherited from ButtonBase) and supports databinding already.
If it isn't, then a dependency property on it will not work, which is fine, because you shouldn't have dependency properties on your view model in the first place.
What you should do, is have the view model as the DataContext for your control (I'm guessing you already have that set up). Then change your view model's CreateCommand to a plain ICommand, and bind the createButton's Command property like this (in your default StandardButtonStrip style)
<Button Name="createButton" HorizontalAlignment="Stretch" Command="{Binding CreateCommand}" />
This way, it will still be reusable, you just need to ensure that any view model you associate with your user control has a property CreateCommand of type ICommand (and that view model will be inherited down to the button control by default - one of the nicest things the wpf guys thought up).
So to recap, you should do it the other way around, more or less.
Hope this was helpful, cheers.
One caveat to the accepted answer -
Using a delegate command I could only get this to work if I created a new
Command<T> : DelegateCommand<T> and hooked up the Command Manager.
event EventHandler ICommand.CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
Have you overridden any of the user control's functionality?
One common problem is overriding a method without calling the base's implementation. For example, if you've overridden OnContentChanged() without calling base.OnContentChanged(), you may have accidentally supressed the firing of the ContentChanged event.

WPF Collections and Databinding

I am new to WPF and trying to wrap my head around WPF's framework, what it does and does not do for you.
To clarify this, I would like to know what is the difference between this:
public List<MyCustomObject> MyCustomObjects
{
get { return (List<MyCustomObject>)GetValue(MyCustomObjectsProperty); }
set { SetValue(MyCustomObjectsProperty, value); }
}
public static readonly DependencyProperty MyCustomObjectsProperty =
DependencyProperty.Register("MyCustomObjects", typeof(List<MyCustomObject>),
typeof(Main), new UIPropertyMetadata(new List<MyCustomObject>()));
and this:
public ObservableCollection<MyCustomObject> MyCustomObjects { get; set; }
public Main ()
{
MyCustomObjects = new ObservableCollection<<MyCustomObject>();
}
Ok, we must put some order into things, there's a few concepts mixed in together here.
First of all, you're asking what the difference is between a field-backed property and a dependency property. Google would be your best friend, however I recommend this blog post by WPF's vanguard Josh Smith: Overview of dependency properties in WPF
In short: dependency properties support the richness that is WPF: Styling, animation, binding, metadata, and more.
Secondly, you're asking what the difference is between a List and an ObservableCollection. Well the latter provides change notifications (in the forms of events) on any change to the collection (addition, removal, change of order, clearing, etc.), and the former does not. You can read more about that here: The ObservableCollection Class
In short: ObservableCollection provides change notifications which are required for the UI to automatically reflect changes in the view model.
In addition to Aviad and Reed's answers, I would like to point out a serious bug in your first code sample :
public static readonly DependencyProperty MyCustomObjectsProperty =
DependencyProperty.Register("MyCustomObjects", typeof(List<MyCustomObject>),
typeof(Main), new UIPropertyMetadata(new List<MyCustomObject>()));
The new List<MyCustomObject>() used as the default value will be created only once, so by default all instances of your type will share the same List<MyCustomObject> instance, which is probably not what you want... The only sensible default value here is null
In the first case, you're setting up a Dependency Property containing a List<T> instance.
In the second, you're making a normal CLR property, but having it setup as an ObservableCollection<T>.
For WPF Data Binding, there are some differences here.
Typically, you want all of your properties in the DataContext (which is the object that, by default, things "bind" to) to either implement INotifyPropertyChanged or to be a Dependency Property. This lets the binding framework know when changes are made to that object. Normally, though, you'd only use a Dependency Property if your working with a custom control - it's usually a better idea to have your object to which your data bound be a separate class, assigned to the DataContext. (For details here, see Josh Smith on MVVM or my recent detailed post on MVVM...)
However, with a collection, you typically also want the binding system to know when the items within the collection change (ie: an item is added). ObservableCollection<T> handles this by implementing INotifyCollectionChanged.
By using the second approach (using an ObservableCollection<T>), your UI can tell when items were added or removed from the collection - not just when a new collection is assigned. This lets things work automatically, like a ListBox adding elements when a new item is added to your collection.
1:
You're using a dependency property to "tell" the framework when that property is changed. This will have the following consequences for your binding:
MyCustomObjects.Add(new MyCustomObject()); //Wont update the view through databinding
MyCustomObjects = new List<MyCustomObject>(); //Will update the view through databinding
You could gain the same databinding functionality by implementing INotifyPropertyChanged on which ever class exposes the property, but dependency properties a capable of much more than just notifying about changes. These are rather advanced features though, which you aren't likely to come across in your average joe app :)
2:
You're using an observable collection, which implements INotifyCollectionChanged for you, to tell the databinding whenever the content of the collection has changed. This will have the opposite consequences than #1:
MyCustomObjects.Add(new MyCustomObject()); //Will update the view through databinding
MyCustomObjects = new ObservableCollection<MyCustomObject>(); //Won't update the view through databinding

Resources