I am new in WPF and MVVM also. I'm searching for USB devices in my program. But if I connect a new device, it is required to restart program to became visible.
How to do it, that refresh immediately.
Currently I have in my class in which I search device this:
public List<Devices> devices = new List<Devices>();
public void FindDevices() // spremeni v public bool da dobis feedback
{
_deviceList = HidDevices.Enumerate(VendorID, ProductID).ToArray();
... devices.Add(new Devices()
{
DeviceId = nod + 1,
ManufacturerId = deviceManufacturerstring[nod],
ProductId = deviceProductstring[nod],
SerialNumberId = deviceSNstring[nod],
HardwareVersionId = "test4",
FirmwareVersionId = "test5",
DateOfManufaturedId = "test6"
});
On hole for loop I add device to List. I need for loop because I read some data from each device.
I later add this devices in List in ViewModel:
public class Windows1ViewModel : ViewModelBase
{
public ObservableCollection<Devices> devfuck { get; protected set; }
List<Devices> _devicelist;
public List<Devices> Devices
{
get { return _devicelist; }
set { _devicelist = value; }
}
public Windows1ViewModel()
{
USBmiddleware cs = new USBmiddleware();
cs.FindDevices();
devfuck = new ObservableCollection<Devices>();
foreach (var item in cs.devices)
{
devfuck.Add(item);
}
List<Devices> keks = cs.devices;
NotifyPropertyChanged("devfuck");
}
public List<Devices> lvdevices
{
get { return _devicelist; }
set { _devicelist = value; }
}
What to change? Where to add INotifyPropertyChanged? Or how to solve my problem?
Please for help. Thanks!
My ViewModelBase
public class ViewModelBase : INotifyPropertyChanged, IDisposable
{
protected ViewModelBase()
{
}
#region DisplayName
public virtual string DisplayName { get; protected set; }
#endregion // DisplayName
#region Debugging Aides
[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);
}
}
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void NotifyPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
protected virtual void NotifyPropertyChangedAll(object inOjbect)
{
foreach (PropertyInfo pi in inOjbect.GetType().GetProperties())
{
NotifyPropertyChanged(pi.Name);
}
}
public virtual void Refresh()
{
NotifyPropertyChangedAll(this);
}
public void Dispose()
{
this.OnDispose();
}
/// <summary>
/// Child classes can override this method to perform
/// clean-up logic, such as removing event handlers.
/// </summary>
protected virtual void OnDispose()
{
}
~ViewModelBase()
{
string msg = string.Format("{0} ({1}) ({2}) Finalized", this.GetType().Name, this.DisplayName, this.GetHashCode());
System.Diagnostics.Debug.WriteLine(msg);
}
}
You can do it by using a timer which gets called ever so often (as needed). Of course it would be sexier if you could do this using event management...
Thankfully there is a way to do this :
var watcher = new ManagementEventWatcher();
var query = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
watcher.Query = query;
watcher.Start();
as stated here: Detecting USB drive insertion and removal using windows service and c#
You can then subscribe to the event and invoke your
FindDevices()
From there.
You need to implement the INotifyPropertyChanged interface in your Windows1ViewModel class. Then you need to call it from your property setter, or after you set the property as you are currently doing.
Next, you should set up a DispatcherTimer to call your FindDevices method every so often and then update the ObservableCollection property from there.
Related
I have a model which currently looks through a series of different log files and then makes an object for each item in those files and appends them to a list (ListOfLogs). Once the model is done parsing the log files it does a property changed event to notify the VM that the ListOfLogs is ready.
The Viewmodel then handles the property changed event and creates an ObservableCollection from the model's ListOfLogs. The view then binds to that observablecollection.
Now that I have switched from an ObservableCollection to a ICollectionView I get an invalid operation exception since the calling thread doesn't own ListOfLogs object. This makes me thing that the way I expose the List is not following the MVVM pattern
Added Code:
ViewModel.cs:
public class ViewModel : INotifyPropertyChanged {
#region Fields
#endregion // Fields
#region Properties
public Model myModel { get; private set; }
public ObservableCollection<MyObject> collectionView { get; set; }
#endregion // Properties
#region Constructor
public ViewModel() {
myModel = new Model();
myModel.PropertyChanged += propertyChanged;
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion // Constructor
#region Methods
private void propertyChanged(object sender, PropertyChangedEventArgs e) {
switch (e.PropertyName ) {
case "Objects":
// Is there a better way to do this
collectionView = new ObservableCollection<MyObject>(myModel.Objects);
//
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("collectionView"));
break;
default:
Console.WriteLine(string.Format("No case for {0}, ", e.PropertyName));
break;
}
}
Model.cs:
Edit: fixed mistake when invoking the property changed event
namespace TestApp1 {
public class Model : INotifyPropertyChanged {
#region Fields
private IList<MyObject> _Objects;
public event PropertyChangedEventHandler PropertyChanged;
#endregion // Fields
#region Properties
public IList<MyObject> Objects { get => _Objects ?? (_Objects = new List<MyObject>()); private set { if (Objects != value) _Objects = value; } }
#endregion // Properties
#region Constructor
public Model() {
}
#endregion // Constructor
#region Methods
public void LoadObjects() {
// Parse through files normally for now just junk works
Parallel.For(0, 10000, dostuff => {
var myOb = new MyObject(){ dt = DateTime.Now, message = "Message" };
lock (Objects) {
Objects.Add(myOb);
}
});
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Objects"));
}
#endregion // Methods
}
public class MyObject {
public DateTime dt { get; set; }
public string message { get; set; }
public string stuff1 { get; set; }
public string stuff2 { get; set; }
}
}
The problem is, that you are modifying the Objects list while passing it to the constructor of the observable collection. (https://referencesource.microsoft.com/#system/compmod/system/collections/objectmodel/observablecollection.cs,cfaa9abd8b214ecb in the constructor where "copyfrom")
The InvalidOperationException belongs to your Objects.Add() call in the Parallel.For.
private void CopyFrom(IEnumerable<T> collection)
{
IList<T> items = Items;
if (collection != null && items != null)
{
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
items.Add(enumerator.Current);
}
}
}
}
In the delegate of Parallel.For you are using a lock. You could use this as well for the property changed event:
lock(myModel.Objects)
{
collectionView = new ObservableCollection<MyObject>(myModel.Objects);
}
Or add the event raising to the lock in the Parallel.For delegate
lock (Objects)
{
Objects.Add(myOb);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Objects"));
}
Or you could just wait until all items are read and then raise one property changed event after completing the Parallel.For.
I am updating a Datagrid and when a user inputs a number that already exists I want notify the user they the number already exists and then clear the value from the datagrid.
I know why this is happening, but I can't figure out how to stop this or how to make a work around.
This is very simplified code: Using EF code first with MVVM model.
public partial class StaffMasterData
{
public System.Guid Id { get; set; } // ID (Primary key)
public int? StaffNo { get; set; } // StaffNo
public StaffMasterData()
{
InitializePartial();
}
partial void InitializePartial();
}
Entity extension class for StaffMasterData :
public partial class StaffMasterData : INotifyPropertyChanged
{
partial void InitializePartial()
{
Id = Guid.NewGuid();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
And the method to save the data:
public void SaveMasterData(StaffMasterData nwRowData)
{
using (var db = CreateDbContext())
{
//MasterDataBinding is the observableCollection
//the datagrid is being bound to.
var staffNoExists = MasterDataBinding.Any(p => p.StaffNo == nwRowData.StaffNo);
if (!staffNoExists)
{
db.StaffMasterDatas.AddOrUpdate(nwRowData);
db.SaveChanges();
}
else
{
Alerts.Error("Staff Number exists");
nwRowData.StaffNo = null;
}
}
}
And the assinging of the collection changed event:
public class ShiftManagerViewModel : INotifyPropertyChanged
{
private ObservableCollection<StaffMasterData> _mMasterDataBinding = new ObservableCollection<StaffMasterData>();
public ObservableCollection<StaffMasterData> MasterDataBinding
{
get { return _mMasterDataBinding; }
set
{
if (value != _mMasterDataBinding)
{
_mMasterDataBinding = value;
OnPropertyChanged();
}
}
}
public ShiftManagerViewModel()
{
_mMasterDataBinding.CollectionChanged += collectionChanged_Event;
}
private void collectionChanged_Event(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null && e.NewItems.Count > 0)
{
foreach (INotifyPropertyChanged item in e.NewItems.OfType<INotifyPropertyChanged>())
{
item.PropertyChanged += propertyChanged_Event;
}
}
if (e.OldItems != null && e.OldItems.Count > 0)
{
foreach (INotifyPropertyChanged item in e.OldItems.OfType<INotifyPropertyChanged>())
{
item.PropertyChanged -= propertyChanged_Event;
}
}
}
public void propertyChanged_Event(object sender, PropertyChangedEventArgs e)
{
if (sender is StaffMasterData)
{
SaveMasterData((StaffMasterData)sender);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
As it is probably very clear, when running through this line of code nwRowData.StaffNo = null; , it fires the event again as the collection has been modified which then in turn runs through the messageBox code and it pops up twice.
Honestly I have hit a brick wall with this and any point in the right direction would be appreciated.
You could use a flag that determines whether to actually call the SaveMasterData method. Set this flag to false just before you set the StaffNo property to null and then set it back to true immediately afterwards:
private bool _handle = true;
public void SaveMasterData(StaffMasterData nwRowData)
{
using (var db = CreateDbContext())
{
//MasterDataBinding is the observableCollection
//the datagrid is being bound to.
var staffNoExists = MasterDataBinding.Any(p => p.StaffNo == nwRowData.StaffNo);
if (!staffNoExists)
{
db.StaffMasterDatas.AddOrUpdate(nwRowData);
db.SaveChanges();
}
else
{
Alerts.Error("Staff Number exists");
_handle = false;
nwRowData.StaffNo = null;
_handle = true;
}
}
}
public void propertyChanged_Event(object sender, PropertyChangedEventArgs e)
{
if (!_handle && sender is StaffMasterData)
{
SaveMasterData((StaffMasterData)sender);
}
}
I'm trying to fire PropertyChanged via reflection and I'm having some issues.
The following code works:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string m_test = string.Empty;
public string Test
{
get
{
return m_test;
}
set
{
m_test = value;
Notify();
}
}
protected void Notify([CallerMemberName] string name = null)
{
var handler = PropertyChanged;
if (handler != null && name != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public class ViewModel : ViewModelBase
{
private string m_test2 = string.Empty;
public string Test2
{
get
{
return m_test2;
}
set
{
m_test2 = value;
Notify();
}
}
}
However, I have added an extension method to INotifyPropertyChanged that would raise it via reflection.
Instead of Notify() I could call this.Notify() instead, which is defined like so:
/// <summary>
/// Invoke sender's PropertyChanged event via Reflection
/// </summary>
/// <param name="sender">sender of the event</param>
/// <param name="prop">The Property name that has changed</param>
public static void NotifyPropertyChanged(this INotifyPropertyChanged sender, [CallerMemberName] string prop = null)
{
var senderType = sender.GetType();
var methodInfo = senderType.GetField("PropertyChanged", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (methodInfo != null)
{
var delegates = (MulticastDelegate)methodInfo.GetValue(sender);
if (delegates != null)
{
foreach (var handler in delegates.GetInvocationList())
{
handler.Method.Invoke(handler.Target, new object[] { sender, new PropertyChangedEventArgs(prop) });
}
}
}
}
Unfortunately, GetField returns null for ViewModel in the example above.
Is there a way to reflect the parent's event?
I'm thinking of iterating over the base classes, but I'm hoping for a better/easier way.
I think you are going about this the wrong way.
You are breaking the framework's encapsulation of events only being raised by the declaring (owning) instance. By using a publicly available extension method that anyone can call, you are opening a can of worms.
A better solution would be to use a protected method in a base class, as was done in your "the following code works" example.
But if you are really determined on doing it, it can obviously be done.
The extension method below can be used if you want to break the normal protections and encapsulation around events.
public static class ProperyChangedEventExtensions
{
public static void RaisePropertyChanged<T, P>(this T sender, Expression<Func<T, P>> propertyExpression) where T : INotifyPropertyChanged
{
Raise(typeof(T), sender, (propertyExpression.Body as MemberExpression).Member.Name);
}
public static void RaisePropertyChanged(this INotifyPropertyChanged sender, [CallerMemberName] string prop = null)
{
Raise(sender.GetType(), sender, prop);
}
private static void Raise(Type targetType, INotifyPropertyChanged sender, string propName)
{
var evtPropType = targetType.GetField("PropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic);
var evtPropVal = (PropertyChangedEventHandler)evtPropType.GetValue(sender);
evtPropVal(sender, new PropertyChangedEventArgs(propName));
}
}
Usage example (including hopefully some cases that will make you reconsider this approach):
class MyViewModel : INotifyPropertyChanged
{
// The compiler will complain about this:
// Warning 3 The event 'MyNamespace.MyViewModel.PropertyChanged' is never used
public event PropertyChangedEventHandler PropertyChanged;
private string _myProp;
public string MyProp
{
get { return _myProp; }
set
{
_myProp = value;
this.RaisePropertyChanged();
}
}
public readonly int MyImmutableValue;
}
// ...
var vm = new MyViewModel();
vm.PropertyChanged += (sender, evt) => Console.WriteLine("Prop changed {0}", evt.PropertyName);
vm.MyProp = "abc";
vm.RaisePropertyChanged(x => x.MyProp);
vm.RaisePropertyChanged("MyProp");
vm.RaisePropertyChanged("Un Oh. Do we have a problem");
vm.RaisePropertyChanged(x => x.MyImmutableValue);
vm.RaisePropertyChanged("MyImmutableValue");
/// <summary>
/// Invoke sender's PropertyChanged event via Reflection???
/// </summary>
/// <param name="sender">sender of the event</param>
/// <param name="prop">The Property name that has changed</param>
public static void NotifyPropertyChanged(this INotifyPropertyChanged sender, PropertyChangedEventHandler handler, [CallerMemberName] string prop = null)
{
handler(sender, new PropertyChangedEventArgs(prop));
}
use it like this?
class MyViewModel : INotifyPropertyChanged
{
// The compiler will complain about this:
// Warning 3 The event 'MyNamespace.MyViewModel.PropertyChanged' is never used
public event PropertyChangedEventHandler PropertyChanged;
private string _myProp;
public string MyProp
{
get { return _myProp; }
set
{
_myProp = value;
this.Notify(this.PropertyChanged);
}
}
}
I have a object whose data members have INotifypropertychange event implemented.I want to maintain a list of objects separately where I do not want to reflect the property change.How can I do it
Wrapping/proxying example:
class MyItem : INPC
{
public string Name { get { ... } set { this.name = value; raisePropChanged("Name") } } ....
}
var item = new MyItem();
collection.Add(item);
item.Name = "John"; // notifies whoever listens on collection
class MyItemWrapper
{
private MyItem theBrain;
public string Name { get{return theBrain.Name;} set{theBrain.Name = value;}}
}
var item = new MyItem();
var wrapped = new MyItemWrapper { theBrain = item };
collectionOne.Add(item);
collectionTwo.Add(wrapped);
item.Name = "John";
// notifies whoever listens on collectionOne
// but whoever listens on collectionTwo will not get any notification
// since "wrapper" does not notify about anything.
// however, since wrapper forwards everything to 'brain':
var name = wrapped.Name; // == "John"
Call the function GetDeepCopy() to get your object which would not raise INPC.
public class ValidationModel : INotifyPropertyChanged
{
private string _validationName;
public string validationName
{
get { return _validationName; }
set { _validationName = value; NotifyPropertyChanged("ValidationName"); }
}
public ValidationModel GetDeepCopy()
{
var model = new ValidationModel();
model.validationName = validationName;
return model;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyname)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
}
I have a WPF dialog that is bound to a list of ObservableCollection<MyEntity> type. In the dialog, I want the "OK" button to be enabled only if changes are made to the ObservableCollection<MyEntity> list - that includes adding/removing items from the list and modifying the individual items in the list.
For adding/removing items from the list, it is easy - I implemented a handler for the CollectionChanged event.
What I don't know how to do is when an individual item is modified. Say, MyEntity.Name="New Value", what interface does MyEntity class need to implement to make it 'observable'?
MyEntity needs to implement INotifyPropertyChanged, then when a property change occurs you fire the PropertyChanged event. Like this:
public class MyEntity : INotifyPropertyChanged
{
public bool MyFlag
{
get { return _myFlag; }
set
{
_myFlag = value;
OnPropertyChanged("MyFlag");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Two ways to approach this are:
have an event listener internal to the object which then sets an IsDirty flag whenever a property changes. Then OK button is bound to a command (check out the usage of the ICommand interface), and in the CanExecute method of the command you check if any of the objects in the ObservableCollection have been set to dirty. This check can be done with a simple LINQ statement: myCollection.Any(x => x.IsDirty == true)
this method is more clunky and smelly.... have an external object listening for changes (by subscribing to the PropertyChanged event on each object), and that external listener can then enable the OK button (via databinding or by setting it directly).
I like the answer provided by slugster, here is an alternative building on slugster's answer.
If you bind to your OK button using DelegateCommnd you can add event handlers for CollectionChanged and PropertyChanged to change a simple boolean flag to control the state of the OK button.
public class MainViewModel : ViewModelBase
{
public DelegateCommand<object> RunCommand { get; set; }
public DelegateCommand<object> OkCommand { get; set; }
private bool enableOk = false;
private bool setOK = false;
private ObservableCollection<MyEntity> _entites = new ObservableCollection<MyEntity>();
public MainViewModel()
{
_entites.CollectionChanged += (s, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
// handle property changing
foreach (MyEntity item in e.NewItems)
{
((INotifyPropertyChanged)item).PropertyChanged += (s1, e1) => { if (setOK) enableOk = true; };
}
}
// handle collection changing
if (setOK) enableOk = false;
};
MyEntity me1 = new MyEntity { Name = "Name", Information = "Information", Details = "Detials" };
MyEntity me2 = new MyEntity { Name = "Name", Information = "Information", Details = "Detials" };
MyEntity me3 = new MyEntity { Name = "Name", Information = "Information", Details = "Detials" };
_entites.Add(me1);
_entites.Add(me2);
_entites.Add(me3);
// allow collection changes now to start enabling the ok button...
setOK = true;
RunCommand = new DelegateCommand<object>(OnRunCommnad, CanRunCommand);
OkCommand = new DelegateCommand<object>(OnOkCommnad, CanOkCommand);
}
private void OnRunCommnad(object obj)
{
MyEntity me = new MyEntity { Name = "Name", Information = "Information", Details = "Detials" };
// causes ok to become enabled
_entites.Add(me);
MyEntity first = _entites[0];
// causes ok to become enabled
first.Name = "Zamboni";
}
private bool CanRunCommand(object obj)
{
return true;
}
private void OnOkCommnad(object obj)
{
}
private bool CanOkCommand(object obj)
{
return enableOk;
}
}
Here is a version MyEntity (similar to the one provided by slugster):
Only the Name property fires an event in this example...
public class MyEntity : INotifyPropertyChanged
{
private string _name = string.Empty;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public string Information { get; set; }
public string Details { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You should implement INotifyPropertyChanged. You could do it by the following way
(as you can see, this implementation is fully thread safe)
private readonly object _sync = new object();
public event PropertyChangedEventHandler PropertyChanged
{
add { lock (_sync) _propertyChanged += value; }
remove { lock (_sync) _propertyChanged -= value; }
} private PropertyChangedEventHandler _propertyChanged;
protected void OnPropertyChanged(Expression<Func<object>> propertyExpression)
{
OnPropertyChanged(GetPropertyName(propertyExpression));
}
protected string GetPropertyName(Expression<Func<object>> propertyExpression)
{
MemberExpression body;
if (propertyExpression.Body is UnaryExpression)
body = (MemberExpression) ((UnaryExpression) propertyExpression.Body).Operand;
else
body = (MemberExpression) propertyExpression.Body;
return body.Member.Name;
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = _propertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
Following the implementation I described above, you can notify about your changes by two ways
1) The first way
public int MyProperty
{
get { return _myProperty; }
set
{
if (value != __myProperty)
{
_subVersion = value;
OnPropertyChanged(MyPropertyPropertyName);
}
}
} private int _myProperty; const string MyPropertyPropertyName = "MyProperty";
2) And the second way
public int MyProperty
{
get { return _myProperty; }
set
{
if (value != _myProperty)
{
_subVersion = value;
OnPropertyChanged(() => MyProperty);
}
}
} private int _myProperty;
Another solution could be a custom observable collection that requires items to implement INotifyPropertyChanged. The user must attach a handler to the OnItemPropertyChanged event, which will be called whenever the property of an item in the collection is changed.
public class ObservableCollectionEnhanced<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
public ObservableCollectionEnhanced()
: base()
{ }
public ObservableCollectionEnhanced(IEnumerable<T> collection)
: base(collection)
{
foreach (T item in Items)
item.PropertyChanged += OnItemPropertyChanged;
}
public ObservableCollectionEnhanced(List<T> list)
: base(list)
{
foreach (T item in Items)
item.PropertyChanged += OnItemPropertyChanged;
}
public event System.ComponentModel.PropertyChangedEventHandler ItemPropertyChanged;
public void OnItemPropertyChanged(Object sender, PropertyChangedEventArgs e)
{
if (null != ItemPropertyChanged)
ItemPropertyChanged(sender, e);
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
item.PropertyChanged += OnItemPropertyChanged;
}
protected override void RemoveItem(int index)
{
T item = this.Items[index];
item.PropertyChanged -= OnItemPropertyChanged;
base.RemoveItem(index);
}
protected override void SetItem(int index, T item)
{
T oldItem = Items[index];
base.SetItem(index, item);
oldItem.PropertyChanged -= OnItemPropertyChanged;
item.PropertyChanged += OnItemPropertyChanged;
}
}
Configure the handler as follows:
public void OnItemPropertyChanged(Object sender, PropertyChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Update called on {0}", sender);
}
...
collection.ItemPropertyChanged += OnItemPropertyChanged;