I have a base DependencyObject class where I have a method that takes an object, gets the properties, and for each property that is a type that implements INotifyPropertyChanged, I add a new PropertyChangedEventHandler. Now in the handler method, it gets the parameters of an object "sender" and the PropertyChangedEventArgs "e". My question is, does anyone know how to dynamically get the property name if sender is a property of a type that implements the INotifyPropertyChanged.
Here is what I'm working with:
public class BaseDependencyObject : DependencyObject, INotifyPropertyChanged
{
public BaseDependencyObject()
{
}
protected void SetValues(Object thisObject, Object entity)
{
try
{
PropertyInfo[] properties = entity.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
var value = property.GetValue(entity, null);
var valueIsEntity = value is System.ServiceModel.DomainServices.Client.Entity;
var thisObjectsProperty = thisObject.GetType().GetProperty(property.Name);
if (thisObjectsProperty != null && value != null)
{
if (valueIsEntity)
{
if (thisObjectsProperty.PropertyType.GetInterface("INotifyPropertyChanged", true) != null)
{
var propertyInstance = Activator.CreateInstance(thisObjectsProperty.PropertyType);
((INotifyPropertyChanged)propertyInstance).PropertyChanged += new PropertyChangedEventHandler(Object_PropertyChanged);
}
SetValues(thisObjectsProperty, value);
}
else if (thisObjectsProperty.PropertyType.GetInterface("ICollection", true) != null
&& thisObjectsProperty.PropertyType.GetGenericArguments().Count() > 0)
{
Type genericType = thisObjectsProperty.PropertyType.GetGenericArguments()[0];
var observableCollection = Activator.CreateInstance(thisObjectsProperty.PropertyType) as IList;
if (observableCollection is INotifyCollectionChanged)
((INotifyCollectionChanged)observableCollection).CollectionChanged += this.Object_CollectionChanged;
if (observableCollection is INotifyPropertyChanged)
((INotifyPropertyChanged)observableCollection).PropertyChanged += new PropertyChangedEventHandler(Object_PropertyChanged);
foreach (var item in (IEnumerable)value)
{
var newItem = Activator.CreateInstance(genericType);
if (newItem != null)
{
SetValues(newItem, item);
observableCollection.Add(newItem);
}
}
}
else
{
thisObjectsProperty.SetValue(thisObject, value, null);
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
protected void Object_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var item in e.NewItems)
{
if (item is INotifyPropertyChanged)
{
((INotifyPropertyChanged)item).PropertyChanged += new PropertyChangedEventHandler(Object_PropertyChanged);
}
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (var item in e.OldItems)
{
if (item is INotifyPropertyChanged)
{
((INotifyPropertyChanged)item).PropertyChanged -= this.Object_PropertyChanged;
}
}
break;
}
}
protected void Object_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.NotifyPropertyChanged(e.PropertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
The SetValues method's first param is the DependencyObject type that will be used in the view model. The second param is the entity that is being returned from the DomainService's Context.LoadOperation.
What my issue boils down to is when the INotifyCollectionChanged.CollectionChanged fires I'm needing to be able to raise the PropertyChanged event with the collection's property name. So if anyone has any advise I would greatly appreciate it. Thanks in advance.
Edit
Figured out how to get the properties name that is firing the event. Here is an edited version of my PropertyChangedEventHandler.
protected void Object_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var properties = this.GetType().GetProperties().Where(x => x.PropertyType == sender.GetType()).ToArray();
foreach (var property in properties)
{
this.NotifyPropertyChanged(property.Name);
}
//this.NotifyPropertyChanged(e.PropertyName);
}
Basically this does what I was looking for, but aparentyly I am still not doing something right. The UIElement is still not updating when the ObservableCollection that is a property of another type is being added to.
Here is an example of my DependencyObjects and ViewModel:
public class LOB : DependencyObject
{
public Int32 ID
{
get { return (Int32)GetValue(IDProperty); }
set
{
SetValue(IDProperty, value);
NotifyPropertyChanged("ID");
}
}
public static readonly DependencyProperty IDProperty =
DependencyProperty.Register("ID", typeof(Int32), typeof(LOB), null);
public ObservableCollection<Group> Groups
{
get { return (ObservableCollection<Group>)GetValue(GroupsProperty); }
set
{
SetValue(GroupsProperty, value);
NotifyPropertyChanged("Groups");
}
}
public static readonly DependencyProperty GroupsProperty =
DependencyProperty.Register("Groups", typeof(ObservableCollection<Group>), typeof(LOB), new PropertyMetadata(null, OnGroupsPropertyChanged));
static void OnGroupsPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != null)
{
((INotifyCollectionChanged)e.NewValue).CollectionChanged += new NotifyCollectionChangedEventHandler(((LOB)obj).Object_CollectionChanged);
((INotifyPropertyChanged)e.NewValue).PropertyChanged += new PropertyChangedEventHandler(((LOB)obj).Object_PropertyChanged);
}
if (e.OldValue != null)
{
((INotifyCollectionChanged)e.OldValue).CollectionChanged -= ((LOB)obj).Object_CollectionChanged;
((INotifyPropertyChanged)e.OldValue).PropertyChanged -= ((LOB)obj).Object_PropertyChanged;
}
}
}
public class Group : DependencyObject
{
public Int32 ID
{
get { return (Int32)GetValue(IDProperty); }
set
{
SetValue(IDProperty, value);
NotifyPropertyChanged("ID");
}
}
public static readonly DependencyProperty IDProperty =
DependencyProperty.Register("ID", typeof(Int32), typeof(Group), null);
public String GroupName
{
get { return (String)GetValue(GroupNameProperty); }
set
{
SetValue(GroupNameProperty, value);
NotifyPropertyChanged("GroupName");
}
}
public static readonly DependencyProperty GroupNameProperty =
DependencyProperty.Register("GroupName", typeof(String), typeof(Group), null);
}
public class MyViewModel : DependencyObject
{
public static readonly DependencyProperty LobCollectionProperty =
DependencyProperty.Register("LobCollection",
typeof(ObservableCollection<LOB>),
typeof(MyViewModel),
new PropertyMetadata(null, LobCollectionPropertyChanged));
public ObservableCollection<LOB> LobCollection
{
get { return (ObservableCollection<MainBusinessLine>)GetValue(LobCollectionPropertyChanged); }
set
{
SetValue(MainBusinessLineCollectionProperty, value);
NotifyPropertyChanged("LobCollection");
}
}
static void LobCollectionPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var viewModel = obj as MyViewModel;
if (viewModel == null)
return;
if (e.OldValue != null)
{
((INotifyCollectionChanged)e.OldValue).CollectionChanged -= viewModel.LobCollection_Changed;
}
if (e.NewValue != null)
{
((INotifyCollectionChanged)e.NewValue).CollectionChanged += viewModel.LobCollection_Changed;
}
}
void LobCollection_Changed(object sender, NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("LobCollection");
}
}
After our conversation above, this is rather moot, but I thought about how I'd implement a base class that fired PropertyChanged events when a collection changed in a property that was defined by the subclass. As I said, it's a bit non-standard, but here's how I'd do it.
class FancyCollectionAndPropertyChangedBase : INotifyPropertyChanged
{
private Dictionary<ICollectionChanged, String> collectionNameLookup = new Dictionary<ICollectionChanged, String>();
protected FancyCollectionAndPropertyChangedBase()
{
this.PropertyChanged += MyPropertyChanged;
}
private void MyPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(this.collectionNameLookup.ContainsValue(e.PropertyName)
{
KeyValuePair<INotifyCollectionChanged, String> oldValue = this.collectionNameLookup.First(kvp => kvp.Value == e.Name);
oldValue.Key -= MyCollectionChanged;
this.collecitonNameLookup.Remove(oldValue.Key);
INotifyCollectionChanged collection = this.GetType().GetProperty(e.PropertyName, BindingFlags.FlattenHierarchy).GetValue(this, null);
collection.CollectionChanged += MyCollectionChanged;
this.collectionNameLookup.Add(collection, e.Name);
}
else if(typeof(INotifyCollectionChanged).IsAssignableFrom(this.GetType().GetProperty(e.PropertyName, BindingFlags.FlattenHierarchy).PropertyType))
{
// Note: I may have gotten the IsAssignableFrom statement, above, backwards.
INotifyCollectionChanged collection = this.GetType().GetProperty(e.PropertyName, BindingFlags.FlattenHierarchy).GetValue(this, null);
collection.CollectionChanged += MyCollectionChanged;
this.collectionNameLookup.Add(collection, e.Name);
}
}
private void MyCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
this.NotifyPropertyChanged(this.collectionNameLookup[sender];
}
}