Notification of Collection Update for IDataErrorInfo Validation - wpf

I would like to notify the binding system in WPF of a change in a collection's item so that a validation through IDataErrorInfo gets reevaluated whenever an item inside a collection changes.
I have a custom list type which implements INotifyCollectionChanged (and works properly). But somehow the validation logic is never called, because (or at least I am assuming) that this notification does not reach the right place. Is this scenario even possible? What have I missed?
[Edit]
So basically the "architecture" is the following:
MVVM base class implements IDataErrorInfo and you can register DataValidators with lambdas in the derived MVVM classes, such as:
RegisterDataValidator(() => People, () => (People.Count == 0) ? "At least one person must be specified" : null);
The indexer on the base class checks the registered validator and returns the returned by it.
I have a SmartBindingList<T> where T: INotifyPropertyChange which is basically a list that when items are added to it, registers the items' PropertyChangedEvent and reacts to these events by firing the CollectionChanged event on the class itself:
private void OnSubPropertyChanged (object sender, PropertyChangedEventArgs e)
{
if (sender is T1)
{
if (CollectionChanged != null)
{
NotifyCollectionChangedEventArgs eventArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender);
CollectionChanged(this, eventArgs);
}
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(myPropertyName));
}
}
}
So all this works nicely, but when the code runs on the CollectionChanged(this, eventArgs) line, nothing happens in terms of validation. It should be wired up correctly, because when I add something to the collection, it works perfectly. What am I missing?

This is a bit of stab in the dark without some example code, but try raising your OnPropertyChanged notification for the properties that have changed. This should cause validation to be re-evaluated.

Related

Wpf detect when notifypropertychanged has been fired

I would like for my ui to perform some functions whenever the bound data has been modified.
Is it possible for the view to execute some code after the notifychange event has been called (due to changes in the underlying model)
If your model implements INotifyPropertyChanged, you can subscibe to PropertyChanged event of it.
model.PropertyChanged += new PropertyChangedEventHandler(Model_PropertyChanged);
void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
}
}
and in the handler you can check which property is changed and do your work accordingly

C#: ObservableCollection - why virtual “CollectionChanged” event?

Why CollectionChanged event is virtual in ObservableCollection? We have virtual OnCollectionChanged method, which should be enough to override event call right?
I don't see any usage of this, and also virtual events are evil. ungainly usage of virtual events can bring a lot of logical issues, but however virtual events exists even in framework.
Is this just bad design or anyone use this in real-word?
We can debate about base classes and design, but here's a not direct/scholastic answer, but more of an example. I personally find it great that I could extend ObservableCollection and override OnCollectionChanged. ObservableCollection is very chatty, every time you add/remove items it bombards the UI thread with property changed messages and slows it down (in the datagrid, for example, every binding in it to be updated). So, as far as I know many people extend the ObservableCollection to suppress such notifications until they are done adding items. Just because WPF controls DataGrids/ListViews etc.. respond to CollectionChanged this works.
here's the usage, I refresh my data and instead of adding one item at a time, I populate a List then I reset the ObservableCollection with it just once which speeds up UI responsiveness enormously:
private void OnExecuteRefreshCompleted(IEnumerable<MyObject> result)
{
UiUtilities.OnUi(() => { _myObservableCollectionField.Reset(result, true);
});
here's my extended class:
public class ObservableCollectionExtended<T> : ObservableCollection<T>
{
private bool _suppressNotification;
//without virtual , I couldn't have done this override
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_suppressNotification)
base.OnCollectionChanged(e);
}
public void Clear(bool suppressNotificationUntillComplete)
{
_suppressNotification = suppressNotificationUntillComplete;
Clear();
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void ClearItems(bool suppressNotificationUntillComplete)
{
_suppressNotification = suppressNotificationUntillComplete;
base.ClearItems();
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void AddRange(IEnumerable<T> list, bool suppressNotificationUntillComplete)
{
if (list == null)
throw new ArgumentNullException("list");
_suppressNotification = suppressNotificationUntillComplete;
foreach (T item in list)
Add(item);
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>
/// clears old items, and new ones
/// </summary>
/// <param name="list"></param>
/// <param name="suppressNotificationUntillComplete"></param>
public void Reset(IEnumerable<T> list, bool suppressNotificationUntillComplete)
{
if (list == null)
throw new ArgumentNullException("list");
_suppressNotification = suppressNotificationUntillComplete;
Clear();
foreach (T item in list)
Add(item);
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
I am afraid denis didn't answer the quesiton "Why CollectionChanged event is virtual?" but rather the question "Why OnCollectionChanged() method is virtual?". The first question is more properly answered by Jon. You may be interested in different handling of subscribers like I did some time ago.
Let's have two different scenarios:
First scenario:
I want to raise CollectionChanged event and be sure that any delegate that is invoked within this event (list of delegates), doesn't interrupt the invoking of the following delegates in case of an exception. In other words, an event is made of a list of delegates. If I have let's say 10 subscribers (delegates) and the 3rd delegate raises an exception, I may continue invoking the rest of delegates. Standard implementation interrupts invoking.
Second scenario:
I may want to let some subscribers be prioritized (they receive the event earlier) even if they do the subscription later than others. In "add" event I can move some specific subscribers lower or higher in my custom list of delegates which is later used to raise the event..

is it correct to use OnPropertyChanged event to ask application to do something?

My MVVM application contains two views:
AllStrategiesView
StrategyView
When user click certain strategy in AllStrategiesView StrategyView with this strategy is created. I use such code to notify application that StrategyView should be created:
.............
public void OpenStrategyView()
{
OnPropertyChanged("OpenStrategy");
}
.................
private void OnWorkspacePropertyChanged(object sender, PropertyChangedEventArgs e)
{
const string openStrategyString = "OpenStrategy";
if (e.PropertyName == openStrategyString)
{
AllStrategiesViewModel vm = (sender as AllStrategiesViewModel);
OpenStrategy(vm.SelectedStrategy);
}
}
However another part of the program shows error message because there are no such property "OpenStrategy":
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
The question is:
Is it right or wrong to use OnPropertyChanged to notify application that something need to be done? Should I rewrite my code to not to use OnPropertyChanged or should I disable VerifyPropertyName code?
This is bad practice. The PropertyChanged event on INotifyPropertyChanged should be used to notify subscribers that a property on the object instance has changed. This is typically used in WPF to notify the UI that it needs to update itself with the new property value.
In MVVM, you should use some kind of commanding or alternative viewmodel/view communication mechanism to invoke verbs (methods) on your view model from the view. The commanding provided by WPF has limitations, so I would recommend using an MVVM framework and the mechanisms that they provide.
Well it depends on what you want it to do. In your case it looks like you have a property "Workspace" which indicates which VM you should be looking at. This doesn't seem too bad of a usage IMHO.
If you were doing something completely unrelated to the property that was changed then it might work, but it's certainly not what I'd expect it to do (see Principle of Least Astonishment). OnPropertyChanged is intended to indicate that a property that has been bound to has changed and should be re-fetched.
You can of course just have another event on your ViewModel, like:
public event Action<String> OpenStrategy;
One more thing... This code is completely redundant:
const string openStrategyString = "OpenStrategy";
if (e.PropertyName == openStrategyString)
the following is exactly the same, from the compiler's perspective, and much more readable:
if (e.PropertyName == "OpenStrategy")
There's nothing wrong in asking your application to do something in the PropertyChanged event, however do not raise a PropertyChanged event just to ask the application to do something.
PropertyChanged is used to indicate that a property has changed, and should be used for that only.
Devdigital's answer gives a good example, that the UI uses the PropertyChange notification to know when it should update. Other objects can also subscribe to receive change notifications, and they should only be notified when a value changes, not when you want to run some application code.
Using your example, I would rewrite it like this:
public void OpenStrategyView()
{
OpenStrategy(this.SelectedStrategy);
}
private void OnWorkspacePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedStrategy")
{
OpenStrategyView();
}
}

WPF: How do I hook into a ListView's ItemsSource CollectionChanged notification?

I have a ListView that is databound to an ObservableCollection ...
<ListView x:Name="List1" ItemsSource="{Binding MyList}" />
I can't seem to find any event that are triggered when the collection changes, so I'm thinking that somehow I need to hook into the collectionchanged notification somehow? I'm not really sure how to do that.
Basically, when the collection changes I want to do additional work beyond what the ListView already does in updating it's list.
By default the ItemsSource is of type IEnumerable. You need to first cast to a type that has access to the CollectionChanged event, then add a handler for that event.
((INotifyCollectionChanged)List1.ItemsSource).CollectionChanged +=
new NotifyCollectionChangedEventHandler(List1CollectionChanged);
public void List1CollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
{
// Your logic here
}
Note: I cast it to INotifyCollectionChanged in my example, but you can really cast it to any object that implements that. Though, as a best practice, you should cast to the most generic type that gives you access to the methods/properties/events you need. So, while you can cast it to an ObservableCollection, you don't need to. INotifyCollectionChanged contains the event you need and if you ever decide to use some other type of collection that implements it, this will continue to work, whereas casting to an ObservableCollection means that if you one day decide that you're list is now of type MyOwnTypeOfObservableCollectionNotDerivedFromObservableCollection than this will break. ;)
P.S. This should go in the xaml code-behind.
you are going to have to attach the handler to your list. Or, use a CollectionView and hook the changed event there.
in your codebehind, do like this:
MyList.CollectionChanged += new NotifyCollectionChangedEventHandler( this.MyCollectionChanged );
private void SortCollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
{
Debug.WriteLine( "Changed" );
}
An ObservableCollection{T} exposes the INotifyCollectionChanged.CollectionChanged event. When binding to an ItemsSource the data binding engine handles the propogation of changes from the source to the items control, but if you need to perform additional processing you can attach a handler to the CollectionChanged event and use the NotifyCollectionChangedEventArgs it provides.
Assuming you have a public property on your view model named MyList:
public ObservableCollection<T> MyList
{
get
{
if(_viewModelMyList == null)
{
_viewModelMyList = new ObservableCollection<T>;
_viewModelMyList.CollectionChanged += (o, e) =>
{
// code to process change event can go here
if(e.Action == NotifyCollectionChangedAction.Add)
{
}
};
}
return _viewModelMyList;
}
}
private ObservableCollection<T> _viewModelMyList;

Event handler that will be called when an item is added in a listbox

Is there an event handler that will be called when an item is added in a listbox in WPF?
Thanks!
The problem is that the INotifyCollectionChanged interface which contains the event handler is explicitly implemented, which means you have to first cast the ItemCollection before the event handler can be used:
public MyWindow()
{
InitializeComponent();
((INotifyCollectionChanged)mListBox.Items).CollectionChanged +=
mListBox_CollectionChanged;
}
private void mListBox_CollectionChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
// scroll the new item into view
mListBox.ScrollIntoView(e.NewItems[0]);
}
}
Ref.
Josh's advice about the observable collection should also be considered.
Take a different approach. Create an ObservableCollection (which does have such an event) and set the ItemsSource of the ListBox to this collection. In other words, in WPF you should think about the problem differently. The control isn't necessarily what is being modified ... the collection behind it is.
UPDATE
Based on your comment to Mitch's answer which indicates your binding source is actually an XML document, I suggest looking into hooking up to the XObject.Changed event of the XML document/element/etc. This will give you change information about the XML structure itself - not the ItemCollection which is an implementation detail you shouldn't need to consider. For example, ItemCollection (or any INotifyCollectionChanged) doesn't guarantee an individual event for every change. As you noted, sometimes you'll just get a generic reset notification.

Resources