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

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.

Related

How do I get the UIElement when Collection changes?

I have a wpf Treeview which has a dynamic itemssource. The User can add and remove items at runtime.
I'm missing an event which gives me the currently added UIElement that was added to the treeviews itemsSource. So I guess I need to switch to OnCollectionChanged.
This is what I have:
// MyItemViewModel is a viewmodel for a TreeViewItem
// MyCollection is bound to hte Treeview's ItemsSource
public class MyCollection : ObservableCollection<MyItemViewModel>
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
// i like to have the UIelement which was added to the collection
// (e.NewItems contains the FrameworkElement's DataContext)
break;
}
}
}
Im following MVVM, as good as I can, and don't want to hold any view elements in the viewmodel.
I like to have an event that is fired when an item is added, which provides the new added UIElement in its sender or EventArgs.
I already tried ItemContainerGenerator class, but it's not useful inside a viewmodel since it requires already a UIElement Control.
You seem to be looking at this problem from the wrong direction... in MVVM, you can pretty much forget about the UI for the most part. So, instead of thinking how to get hold of the item that the user added into the collection control in the UI, think about accessing the data object that you added to the data collection in the view model that is data bound to the UI collection control in response to an ICommand that was initiated by the user.
So to me, it sounds like you need to implement an ICommand that is connected to a Button in the UI, where you add the new item into the data bound collection rather than any event. In this way, you'll always know the state of all of your data items.

WPF ListView with ItemsSource: how to know when model adds item

Question: What is "proper" way of letting the user control's "view model" (.xaml.cs file) know that a ListViewItem has been added to a ListView? Note that this post is addresses a different problem.
Details:
I have a UserControl which contains a ListView and a DataContext:
The ListView has an ItemsSource={Binding ActionLogEntries}
ActionLogEntries is an ObservableCollection property in the DataContext
The data context adds items to the ListView when certain things happen.
But there isn't a ListView.ItemAdded event. There is a CollectionChanged event on ObservableCollection in data context but the view model's handler of this event could get called before the item is added to the ListView so this doesn't seem like a good strategy.
FYI: This came up because when items are added to the ListView, it doesn't automatically scroll to the newly added item, which is behavior I have to add. Presumably I'd use ScrollIntoView after that.
So there are at least two ways of skinning this cat:
do as explained by Clemens in comment to my question
do as in this post by WPF Mentor
Solution 1 seems more natural for the event subscription, since you don't need to cast; also IntelliSense doesn't show class members of implemented interfaces without cast, so for Solution 2 you have to remember to look at what interfaces are implemented and check for events there too. Here is what the subscription looks like for each solution:
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
// Solution 1, subscription:
xActionListView.ItemContainerGenerator.ItemsChanged +=
new ItemsChangedEventHandler(ActionLog_ItemsChanged);
// Solution 2, subscription:
((INotifyCollectionChanged)xActionListView.Items).CollectionChanged +=
new NotifyCollectionChangedEventHandler(ActionListView_CollectionChanged);
}
But solution 2 has easier to use event arg in handler:
// Solution 1, handler:
private void ActionLog_ItemsChanged(object sender, ItemsChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
// Solution 1, scroll the new item into view
xActionListView.ScrollIntoView(
xActionListView.Items[e.Position.Index + e.Position.Offset]);
}
}
// Solution 2, handler:
private void ActionListView_CollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
// Solution 2, scroll the new item into view
xActionListView.ScrollIntoView(e.NewItems[0]);
}
}
It looks like in some circumstances, one solution may be more appropriate than the other: the event data may be easier to use in one or the other based on what data you need.

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);
}

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;

Resources