How to listen to CollectionChanged event and execute some method - wpf

My viewmodel has two Collections, one is MainCollection and other is DerivedCollection. They are displayed using a control, so that when user interacts with the mouse, items can be added or removed from MainCollection, and DerivedCollection should be refreshed accordingly.
The first part (updating MainCollection) happens automatically via data-binding, but I don' know how can I hook RefreshDerivedCollection method to MainCollection.PropertyChanged event.
Both collections and the method live in the same viewmodel.

You can subscribe to MainCollection.CollectionChanged and refresh derived collection there:
MainCollection.CollectionChanged += this.OnMainCollectionChanged;
and
void OnMainCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// TODO: Handle main collection change here.
}

Related

Silverlight Data Binding to Collection within Collection

Hopefully someone can help me out with this. I am creating a Silverlight app which is used for editing images. A user has a project which contain layers which contain elements. (Elements are text and image elements).
I have a class which represents the project. It contains an ObservableCollection<Layer> and each Layer has an ObservableCollection<Element>. Element is an abstract class. There is a TextElement and ImageElement class which inherit from Element.
My problem is the UI never gets updated when I change an element inside the collection. I am using INotifyPropertyChanged on all my properties and I am catching CollectionChanged on the collections but still no go. The CollectionChanged event for ObservableCollection<Element> never gets hit on an update of one of its elements.
This is the code I had originally had:
void Elements_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
this.NotifyChange("Elements");
}
If anyone can help I would be very grateful.
ObservableCollection<> publishes CollectionChanged when items are added or removed, but it does NOT do so when an item inside the collection changes.
In order to get around this, you could subscribe to the CollectionChanged event and then subscribe to the INotifyPropertyChanged of added elements inside the CollectionChanged handler:
elementCollection.CollectionChanged += (s, a) =>
{
foreach (Element element in a.NewItems)
element.PropertyChanged += ElementChanged;
};
Then you can update the UI within the ElementChanged method.

How to know when binding is completed?

When I set the .ItemSource() property on a DataGrid to a Collection, the call returns fast, but the actual binding happens afterwards. Since I want to display a waiting cursor, I need to detect when the actual binding has finished. Is there any event for this?
Anything based on ItemsControl uses an ItemContainerGenerator to generate its items in the background. You can access the ItemContainerGenerator property of the DataGrid and hook up the StatusChanged event to determine when it's done. If you're using virtualization and scroll, this will fire again so you need to handle that if necessary in your case.
I waited for my DataGrid's Loaded event to fire, and I did a BeginInvoke, like this:
private void SubjectsList_Loaded(object sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => ColorMyRows()));
}
More details available in my answer here: https://stackoverflow.com/a/44464630/2101117
Your best bet is to hook into OnPropertyChanged event in your Window or User Control. This event is fired every time a property is updated. Then check for the actual property you wish to observe and take action.
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if ("YOUR_PROPERTY_NAME".Equals(e.Property.ToString()))
{
// Take action
}
base.OnPropertyChanged(e);
}

Reflect property change from one ViewModel to another

I have this WPF/MVVM Application that a TabControl with a bunch of tabs. When the app loads, data for all tabs are loaded. There are some calculations that are made on Tab1 that's dependent on values from Tab2. what's happening is, when I enter / change data on tab2, it simply doesn't reflect on Tab1 when I click on Tab1. under the hood, the calculations are made properly but it doesn't reflect on tab1. I have to go to the main tab to re-load all the data to reflect changes. Any ideas how to implement this?
You need to implement INotifyPropertyChanged on your view model data properties. Then, have your view model subscribe to the event (the Initialize() method is called by the view model constructor):
private void Initialize()
{
// Subscribe to events
this.PropertyChanging += OnPropertyChanging;
this.PropertyChanged += OnPropertyChanged;
this.Books.CollectionChanging += OnBooksCollectionChanging;
}
The view model handler for the event can then update any properties that need to be updated:
void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch(e.PropertyName)
{
case "FirstProperty":
this.SomeOtheProperty = whatever;
break;
case "Another property":
this.YetAnotherProperty = somethingElse;
break;
}
}
That should get the job done.
If two different ViewModels need to show the same data/value they should bind to the same ViewModel.
I think that adding a binding between ViewModels is bad because it introduces a lot of dependencies.
If a property of a single ViewModel is dependent on an other property of the same ViewModel you could use property changed notification as mentioned in David's answer.

Silverlight DataBinding Loading Animation

Is there an event somewhere in the Silverlight control model that is raised once an item is databound? I am binding at design time to a large amount of data and would like to display an animation until the databinding is complete.
There is no specific event that is fired when databinding is completed. Your best bet would probably be to key off of the FrameworkElement.LayoutUpdated event. This is the last event in the lifecycle before a control is ready for user interaction. However, this event will continue to be raised many more times due to property changes, size changes, and explicit calls to UpdateLayout() or InvalidateArrange(). Therefore you will have to add some extra logic to make sure that the LayoutUpdated event warrants stopping/hiding your animation, such as only doing it the first time or if you are sure the event was fired due to a change in databinding.
If the control is actually your own custom control and you are binding to custom DependencyProperties on that control then you could raise your own event on the PropertyChangedCallbacks for each of the properties to signal that they have been updated via databinding.
Here's what I do:
private object lastDataContext;
private void MyClass_Loaded(object sender, RoutedEventArgs e)
{
if (DataContext != lastDataContext)
{
perform_onetime_operation();
lastDataContext = DataContext;
}
}
That way perform_onetime_operation will get called not just the first time databinding happens, but any time that the DataContext changes meaning that data is re-bound.

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