I am studying binding in WPF, then I have this question:
let's say a dependency property is binded to a property of an object which implements INotifyPropertyChanged interface.
when the binding target update the source, then the source's property get updated.
since the property setter of the source object changed, it will in turn notify the listener-the binding target, which will result in recursive update.
How is this avoided in WPF?
Updates to the source caused by a property-changed event don't trigger binding, and property-changed events that occur while a binding is updating the source are ignored.
Here's an easy way to demonstrate this. Create this class:
public class TestClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _Property;
public int Property
{
get { return _Property; }
set
{
if (value < 1000) // just to be on the safe side
{
_Property = value + 1;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Property"));
}
}
}
}
}
Now create a window containing two TextBoxes whose Text is bound to the Property property on an instance of this class. Make both use two-way binding, with UpdateSourceTrigger set to PropertyChanged.
Whenever you type a number into the either bound TextBox, the other TextBox will display the next number. The binding on the first TextBox is ignoring the PropertyChanged event that the source is raising because that event is happening during binding. The second text box does get updated by the first PropertyChanged event, but it doesn't update the source with its new value.
If you update the property in code, (e.g. in a button-click event), both TextBoxes will display the same value - e.g. if you set the property to 20, both will display 21. The property-changed event fires when the property is set to 20, the current value of 21 is displayed, and the bindings don't update the source.
Related
I found a difference on property binding between UserControl and a normal Control.
For example, assuming that the markup contains the following usercontrol:
<myCtrl:DemoControl Level="{Binding Alarm.AlarmLevel}" />
"Level" is an int dependency property created in "Control". "Alarm" is an object of type Inotifypropertychanged, with a field AlarmLevel.
public bool AlarmLevel
{
get
{
return this._alarmLevel;
}
set
{
this._alarmLevel = value;
NotifyPropertyChanged("AlarmLevel");
}
}
Inside the usercontrol, I did the following:
LevelProperty = DependencyProperty.Register("Level", typeof(int), typeof(DemoControl), new UIPropertyMetadata(0, isLevelChanged));
The strange thing is that when assign AlarmLevel to a value, if the value changes, the usercontrol property got updated. While if value remains the same, no update. BUT IN BOTH CASES, "NotifyPropertyChanged" gets called !
For example, if AlarmLevel==1,
Alarm.AlarmLevel = 2; // the "isLevelChanged" got called
Alarm.AlarmLevel = 1; // the "isLevelChanged" not called
I remember that with the normal control, whenever PropertyChanged is called, the property gets updated. Anybody knows why? Many thanks!
There is a bug in your AlarmLevel setter. It should be:
if (_alarmLevel != value)
{
this._alarmLevel = value;
NotifyPropertyChanged("AlarmLevel");
}
You should only raise the INotifyPropertyChanged when the value actually changes. When you use INotifyPropertyChanged, the change check is your responsibility. When you you dependency properties, the WPF framework does the check for you.
That's why your code is half working :).
I am using a custom control derived from a listbox, but with added features. One of the key features is the addition of a bindable SelectedItems property on the control, so the view model can keep track of the multiple selections made in the control. The binding does work - when you select items in the control, the view model's property is updated. However, I would like to add INotifyDataErrorInfo validation to the view model, so I implemented the interface and added a call to my validation method in the set block of the data-bound property in the viewmodel. For some reason that set block is never being called, even though I am updating the control in the view, and am verifying that the view model's property value is actually being changed correctly to match the control.
I know that when I use binding with standard WPF controls, such as a TextBox, the set block of the source (view model) property is called when the target (view) property changes. Is there a reason it wouldn't be called here?
The custom control I am using is found here. This is my property on the viewmodel (I have the console output there just to ensure the code isn't being called):
private ObservableCollection<Car> _testListSelections;
public ObservableCollection<Car> testListSelections
{
get
{
return _testListSelections;
}
set
{
Console.WriteLine("Value changed.");
_testListSelections = value;
OnPropertyChanged("testListSelections");
Validate();
}
}
This is my XAML (note that I didn't need to use Mode=TwoWay here as I am using an ObservableCollection, but I did try specifying Mode=TwoWay and the set block still didn't get hit):
<src:MultiComboBox SelectionMode="Multiple"
VerticalAlignment="Center"
ItemsSource="{Binding testList}"
SelectedItems="{Binding testListSelections, ValidatesOnNotifyDataErrors=True}"/>
This is the SelectedItems property on the custom control (the author overrode the base read-only SelectedItems property in order to allow binding):
/// <summary>
/// The SelectedItems dependency property. Access to the values of the items that are
/// selected in the selectedItems box. If SelectionMode is Single, this property returns an array
/// of length one.
/// </summary>
public static new readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register("SelectedItems", typeof(IList), typeof(BindableListBox),
new FrameworkPropertyMetadata(
(d, e) =>
{
// When the property changes, update the selected values in the selectedItems box.
(d as BindableListBox).SetSelectedItemsNew(e.NewValue as IList);
}));
/// <summary>
/// Get or set the selected items.
/// </summary>
public new IList SelectedItems
{
get
{
return GetValue(SelectedItemsProperty) as IList;
}
set { SetValue(SelectedItemsProperty, value); }
}
You should perform the validation in the OnCollectionChanged event of the list.
The SelectedItems list should be set only once, and then changes are made to the same list.
You can then check if the operation is Add, Remove or Reset, and perform validation accordingly.
I have a simple Datagrid binded to an ObservableCollection from the ViewModel. This ObservableCollection is composed by a Custom Type, say ObservableCollection.
The ComplexType only have 2 properties, and only one is editable on the screen. The other one is a bool type that depends on the first.
When I edit the first property, it gets reflected to the ComplexType and it also change the second property. But the second property is not changed on the screen.
How can I update the second property on the screen?
Try this:
public class ComplexType:INotifyPropertyChanged
{
private object someProperty1;
public object SomeProperty1
{
get{return someProperty1;}
set
{
someProperty1=value;
SomeProperty2=somefunc(someProperty1);
If(PropertyChanged!=null){PropertyChanged(this, new PropertyChangedEventArgs(SomeProperty1));}
}
}
private object someProperty2;
public object SomeProperty2
{
get{return someProperty2;}
set
{
someProperty2=value;
If(PropertyChanged!=null){PropertyChanged(this, new PropertyChangedEventArgs(SomeProperty2));}
}
public event PropertyChangedEventHandler PropertyChanged;
}
An observable collection provides notification only when items are added, removed, or the whole collection is refreshed. You need to make sure that each property either raises the PropertyChanged event or is a dependency property if you want your UI to refresh when it changes.
in my Silverlight 4 Application I have an ObservableCollection that I data bind to two different listboxes. The listboxitems showing the content of the MyClass-Object. When I add an item to the ObservableCollection, the new item is displayed in both listboxes properly.
I have set the Binding Mode to two way, so that editing the listboxitems will update the model automatically. This works so far. My problem is, that the content of the other listbox doesn't update with the updated model. Adding a new item will properly show up on the other listbox, but the updates of the content (which I checked happens) won't.
Any ideas how to achieve: The content of the other listbox should update automatically, when the I update the content in one listbox.
Thanks in advance,
Frank
To expand on what luke said your class needs to implement INotifyPropertyChanged and your properties need to throw the PropertyChanged event in their setters.
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged; // This may be named wrong
private string _myString = null;
public string MyString
{
get { return _myString; }
set
{
if(value == _myString)
return;
_myString = value;
var eh = PropertyChanged;
if(eh != null)
eh(this, new PropertyChangedEventArgs("MyString"));
}
}
}
The MyString property will notify the UI that it has changed which will trigger the binding to update.
you need to make sure that your objects in the observable collection implement INotifyPropertyChanged and they should post change events when your content properties change.
I have a WPF Toolkit DataGrid bound to an ObservableCollection of Car in my view model. Car has a PropertyChanged event and the setters of each of its two string properties fire the event. I also have the grid's SelectedItem property bound to a property in the view model of type Car, also called SelectedItem.
On the same window as the grid, I have buttons for add, modify and delete. Add and modify open a dialog window with two textboxes, one for each Car property. Delete just shows a confirm dialog then does the delete.
For add and delete, I add or delete an item from the ObservableCollection and the grid updates itself as expected. However, for modify it does not. At first, my Car did not use PropertyChanged and after some searching I found that it needed to for the grid to update when an individual item's properties changed. But now that I am using PropertyChanged, the grid still doesn't update.
I've tried changing the values of the SelectedItem in my view model as well as directly changing the item on the collection.
What am I doing wrong?
Make sure you're implementing INotifyPropertyChanged and not just raising a PropertyChanged event. Also, when raising PropertyChanged, you must pass "this" as the sender, otherwise WPF will ignore the event.
Below is a simple base class that implements INotifyPropertyChanged.
public class Person : INotifyPropertyChanged {
private string name;
public string Name {
get { return name; }
set {
if (name != value) {
name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) {
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Sounds like the classic problem with ObservableCollection. ObservableCollection only notifies of additions, deletions, etc. on it's self. It will NOT notify of changes to properties of whatever you have stored in it. This is why your add/delete operations work as expected.
What you should do is use a CollectionView and bind to that:
ObservableCollection<MyObject> myCollection = new ObservableCollection<MyObject>();
ICollectionView view = CollectionViewSource.GetDefaultView(myCollection);
using this method also has the benifit that grouping and sorting are built into the view.