Breakpoint (and extra code) not being hit in set block of data-bound property, though property value is being updated - wpf

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.

Related

Bind window model value to User Control Dependency Property

I have a simple user control that has One Dependency Property (the control is the model of itself)
The property is not directly bound to anything inside the user control, but I need to Bind its value to the Model of the window (or user control or whatever) where I put my user control.
If I set manually the User control Property Value, the property is modified correctly so I can assume the dependency property in the user control is working.
If I set the value to the Property binding it to my window model like this
<lctrl:InfoIconControl Grid.Row="0" Name="InfoIconTest" IconType="{Binding Path=IconTypeValue}"/>
Where IconTypeValue is a property of the window model, when I set the value of the window model property it does not change inside my user control. I presume I did something wrong but at the moment I have no clue.
Two possibilties come to mind as likely:
Your "model" (you mean viewmodel?) does not implement INotifyPropertyChanged and/or you're not firing the PropertyChanged when IconTypeValue changes its value.
You've done something like this.DataContext = this inside your UserControl and now the Binding is not working because it is looking for the IconTypeValue property inside your control, instead of looking for it in the "model".
Solution to option 1 is easy: implement the interface and make sure you fire the event when the property changes.
Solution to option 2 is simply removing any setting of DataContext inside your UserControl, and instead rely on relative Bindings (RelativeSource, ElementName, etc.) in your control's XAML. Or if you gotta set the DataContext of something, do NOT set the UserControl's one. Instead, set the DataContext of a container INSIDE the UserControl.
In your case, since you're using a viewmodel for your UserControl, using it as DataContext makes sense. But if you wanna support binding to the DependencyProperties of your UserControl, you're then gonna have to set your viewmodel as DataContext of something else... For instance, the first Grid in your XAML.
Just name the Grid:
<Grid x:Name="LayoutRoot">
And set your viewmodel as its DataContext:
InfoIconControlModel mModel;
public InfoIconControl()
{
InitializeComponent();
mModel = new InfoIconControlModel();
LayoutRoot.DataContext = mModel; // this.DataContext = mModel; <-- DON'T DO THIS
}
After that, the Bindings will begin to work. But you've made another typical mistake: you're only calling SetIcon from the CLR setter of your propertty.
public InfoIconType IconType
{
get
{
return (InfoIconType)this.GetValue(IconTypeProperty);
}
set
{
this.SetValue(IconTypeProperty, value);
this.SetIcon(); // <-- This won't work with Binding
}
}
Instead, you must also call it from the DependencyPropertyChanged callback (that you had already defined, on the other hand):
/// <summary>
/// Icon Type dependency Property
/// </summary>
public static readonly DependencyProperty IconTypeProperty = DependencyProperty.Register(
FLD_IconType, typeof(InfoIconType), typeof(InfoIconControl), new PropertyMetadata(InfoIconType.ICPlus, IconTypePropertyChanged));
///<summary>
///
///</summary>
private static void IconTypePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
InfoIconControl ic = sender as InfoIconControl;
ic.SetIcon(); // <-- This will work with Binding
}

Binding to an initially NULL property in the ViewModel doesn't rebind

I have a UserControl that contains a Telerik RadDataForm. The form's ItemsSource is bound to a property on the UserControl's ViewModel:
<telerik:RadDataForm
ItemsSource="{Binding Path=viewModel.items, RelativeSource={RelativeSource AncesterType=local:MyUserControl}}"
/>
Where viewModel is:
public partial class MyUserControl: UserControl
{
public MyUserControlVM viewModel
{ get { return this.DataContext as MyUserControlVM; } }
}
Within the viewmodel, items is a fairly ordinary collection:
public class MyUserControlVM : MyViewModelBase
{
private ObservableCollection<AnItem> items_;
public ObservableCollection<AnItem> items
{
get { return this.items_; }
set
{
this.items_ = value;
notifyPropertyChanged("items");
}
}
...
}
And where, of course, MyViewModelBase implements INotifyPropertyChanged.
The user control has an items dependency property, and when it is set, it sets the matching property on the view model:
public partial class MyUserControl : UserControl
{
public ObservableCollection<AnItem> items
{
get { return GetValue itemsProperty as ObservableCollection<AnItem>; }
set { SetValue(itemsProperty, value); }
}
public static readonly DependencyProperty itemsProperty =
DependencyProperty.Register("items",
typeof(ObservableCollection<AnItem>),
typeof(MyUserControl), new PropertyMetadata(
new PropertyChangedCallback(itemsPropertyChanged)));
private static void itemsPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
MyUserControl myUserControl = d as MyUserControl;
ObservableCollection<AnItem> items =
e.NewValue as ObservableCollection<AnItem>;
if (myUserControl != null && myUserControl.viewModel != null)
myUserControl.viewModel.items = items;
}
}
All of which seems pretty straightforward, if a bit tedious.
The problem is that the items dependency property on MyUserControl is bound to a property of the current item of another collection, and that the current item is initially null, and so when MyUserControl is initially loaded, its items property is null. And hence, so is the items property on MyUserControlVM that the RadDataForm is binding to.
Later, when an item in that outer collection is made current, the items dependency property on MyUserControl is set, and that sets the items property on MyUserControlVM. And MyUserControlVM calls notifyPropertyChanged so that listeners will be informed of the change. But this last is not working.
Afterwards, if I examine RadDataForm, its ItemsSource property is still null.
It's like the RadDataForm isn't listening for the propertychanged event, because what it was bound to was initially null. In similar circumstances where the bound property is not null at the start, this pattern works fine as the current item changes from one item to another, but it doesn't seem to work from having no current item to having one.
So, any ideas as to how to make this work? I can't, given the circumstances, make it so that items always has a value when the form loads - it is always going to be null, at the start. How do I get the RadDataForm to notice when the property becomes non-null?
When I want to reference something at the root of my UserControl (a custom property, for instance, or as in your case, the DataContext), I usually give my UserControl a Name. Then I use this name together with the ElementName property on the Binding to set it up.
<UserControl
...
Name="TheControl">
<Grid>
<TextBlock Text={Binding Path=DataContext.items, ElementName=TheControl}" />
</Grid>
</UserControl>
Due to the viewModel property, you can use that and DataContext interchangeably.
However, in your case it might actually be simpler. First, there's a typo in your code. It should be AncestorType (with an 'o'). Second, you might want to try setting up the binding using only {Binding Path=items} since I believe that your control already inherits the correct DataContext. (Not sure about that last one, though.)
If the problem persists, and you suspect that it does in fact has something to do with the items property returning null initially, you could always initialize the items_ with an empty collection to avoid the null.
private ObservableCollection<AnItem> items_ = new ObservableCollection<AnItem>();

ComboBox not changing when bound value of SelectedItem changes

I have a ComboBox that has its ItemSource and SelectedItem properties bound to a view model. And I have the following block of code that is the callback for a data query against a DomainContext:
/// <summary>
/// Stores (readonly) - Stores available for ship to store.
/// </summary>
public ObservableCollection<StoreEntity> Stores
{
get { return _stores; }
private set { _stores = value; RaisePropertyChanged("Stores"); }
}
/// <summary>
/// SelectedStore - Currently selected store.
/// </summary>
public StoreEntity SelectedStore
{
get { return _selectedStore; }
set { _selectedStore = value; RaisePropertyChanged("SelectedStore"); }
}
/// <summary>
/// When stores are completely loaded.
/// </summary>
/// <param name="a_loadOperations"></param>
protected void OnStoresLoaded(LoadOperation<StoreEntity> a_loadOperations)
{
Stores.AddRange(a_loadOperations.Entities);
SelectedStore = a_loadOperations.Entities.FirstOrDefault();
}
In this example, Stores is a ObservableCollection<StoreEntity> (AddRange is an extention method) and is bound to ItemSource, and SelectedStore is a StoreEntity and is bound to SelectedItem.
The problem here is that the ComboBox is not changing its selection to reflect the change in SelectedItem.
Edits:
I've even tried the following, though I think that a_loadOperation.Entities is already a realized set:
/// <summary>
/// When stores are completely loaded.
/// </summary>
/// <param name="a_loadOperations"></param>
protected void OnStoresLoaded(LoadOperation<StoreEntity> a_loadOperations)
{
var entities = a_loadOperations.Entities.ToArray();
Stores.AddRange(entities);
SelectedStore = entities.First();
}
Thanks
If you are trying to get a change to your viewmodel (specifically the SelectedStore property) to be reflected in your combo box, you could:
Confirm the binding worked. Check it was set up properly in the XAML, and check the Output window to see if there is a message saying the Binding failed
Confirm your DataContext is set properly (it probably is since you are getting the combo box `ItemsSource` from `Stores`
Subscribe to your PropertyChanged event and confirm that it is being raised when the property changes
If that doesn't work, it may be a bug with the ComboBox. I have seen cases where the order of specifying the properties in XAML makes the difference (ex: you should set ItemsSource first and SelectedItem second). I have also seen a binding fail until I added Mode=TwoWay (even though in your example you are trying to get the binding to update from your view model to your UI). Try confirming that your ComboBox XAML is like this:
<ComboBox ItemsSource="{Binding Stores}" SelectedItem="{Binding SelectedStore, Mode=TwoWay}" />
Order shouldn't matter since XAML is declarative, but I have personally seen it matter with ComboBoxes in Silverlight.
I had SelectedItem bound to Stores instead of SelectedStore. Ooops!

How does data binding avoid recursive update in WPF?

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.

wpf ComboBox Async binding to items source problem

i have a mvvm app that the main window is a tab control.
i use the itemssource to bind items to the combo box,
everything works fine until i go to another tab and for some reason the selected item of the combo box getting the null value, any ideas ?
the binding is twoway updatesource onpropertychanged and the property is type of observablecollection
I had same problem before. And the solution is that make sure Itemsource attribute of comboBox in XAML has not been declared before SelectedValue attribute. It should work then.
It is just necessary to work on a ObservableCollection and to load only as AddRange (not use 'Add')
When we load the data as:
foreach (object item in items)
{
MyList.Add(item); // Where MyList is a ObservableCollection
}
Then after adding of the first element the ObservableCollection call OnCollectionChanged.
Then the ComboBox will try to select the SelectedValue and return 'null' when can't find this element.
It can solve a problem:
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
namespace System.Collections.ObjectModel
{
/// <summary>
/// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
/// <summary>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class.
/// </summary>
public ObservableCollectionEx()
: base() { }
/// <summary>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection.
/// </summary>
/// <param name="collection">collection: The collection from which the elements are copied.</param>
/// <exception cref="System.ArgumentNullException">The collection parameter cannot be null.</exception>
public ObservableCollectionEx(IEnumerable<T> collection)
: base(collection) { }
/// <summary>
/// Adds the elements of the specified collection to the end of the ObservableCollection(Of T).
/// </summary>
public void AddRange(IEnumerable<T> collection)
{
//
// Add the items directly to the inner collection
//
foreach (var data in collection)
{
this.Items.Add(data);
}
//
// Now raise the changed events
//
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
}
I have an mvvm app with almost exactly the same scenario. The main window has a tab control. There is a tab containing a combobox. The combobox itemsource is bound to an IList (in the view model) and the Selected value is bound to a property in the view model implementing INotifyPropertyChanged.
<ComboBox ItemsSource="{Binding AllowedJudges}"
SelectedValue="{Binding SelectedJudge, UpdateSourceTrigger=PropertyChanged}" >
When selecting another tab, the view model's property bound to the SelectedValue mysteriously gets set to null. I'm able to handle it by not allowing the SelectedValue-bound property to be set to null:
public Judge SelectedJudge
{
get { return selectedJudge; }
set
{
if(selectedJudge==value || value==null) return;
selectedJudge = value;
OnPropertyChanged("SelectedJudge");
updateViewData();
}
}
However, it's not clear to me why a tab pane becoming invisible implies a value in a combobox there becomes deselected....
If for some Reason the BindingSource of the ItemsSource does no longer contain the SeletedItem (because it's re-initialized or whatever), then the SelectedItem can be reset to default, i.e. null.
From your example I can't tell why this should happen, but that maybe because you just missed adding some more environmental code.
It`s may be problem of your viewmodel ierarchy.
For example, if both ends of your Binding are dependency properties and ownertype property is not tied to a particular class (for example, set the parent class), this dependency property will be used by all the inheritors together. Bad design

Resources