C# 列出NotifyPropertyChange
BindingList和ObservableCollection可以很好地保持数据更新,并在其中一个对象发生更改时发出通知。但是,当通知一个属性即将更改时,我认为这些选项不是很好 要解决这个问题,我现在要做的是(我警告说这一点也不优雅),在列表的类型对象上实现INotifyPropertyChanging,然后将其绑定到保存列表PropertyChanging事件的对象,或者类似于以下内容:C# 列出NotifyPropertyChange,c#,collections,C#,Collections,BindingList和ObservableCollection可以很好地保持数据更新,并在其中一个对象发生更改时发出通知。但是,当通知一个属性即将更改时,我认为这些选项不是很好 要解决这个问题,我现在要做的是(我警告说这一点也不优雅),在列表的类型对象上实现INotifyPropertyChanging,然后将其绑定到保存列表PropertyChanging事件的对象,或者类似于以下内容: // this object will be the type of the BindingList p
// this object will be the type of the BindingList
public class SomeObject : INotifyPropertyChanging, INotifyPropertyChanged
{
private int _intProperty = 0;
private string _strProperty = String.Empty;
public int IntProperty
{
get { return this._intProperty; }
set
{
if (this._intProperty != value)
{
NotifyPropertyChanging("IntProperty");
this._intProperty = value;
NotifyPropertyChanged("IntProperty");
}
}
}
public string StrProperty
{
get { return this._strProperty; }
set
{
if (this._strProperty != value)
{
NotifyPropertyChanging("StrProperty");
this._strProperty = value;
NotifyPropertyChanged("StrProperty");
}
}
}
#region INotifyPropertyChanging Members
public event PropertyChangingEventHandler PropertyChanging;
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public void NotifyPropertyChanging(string propertyName)
{
if (this.PropertyChanging != null)
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
public void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ObjectThatHoldsTheList : INotifyPropertyChanging, INotifyPropertyChanged
{
public BindingList<SomeObject> BindingList { get; set; }
public ObjectThatHoldsTheList()
{
this.BindingList = new BindingList<SomeObject>();
}
// this helps notifie Changing and Changed on Add
private void AddItem(SomeObject someObject)
{
// this will tie the PropertyChanging and PropertyChanged events of SomeObject to this object
// so it gets notifies because the BindingList does not notify PropertyCHANGING
someObject.PropertyChanging += new PropertyChangingEventHandler(someObject_PropertyChanging);
someObject.PropertyChanged += new PropertyChangedEventHandler(someObject_PropertyChanged);
this.NotifyPropertyChanging("BindingList");
this.BindingList.Add(someObject);
this.NotifyPropertyChanged("BindingList");
}
// this helps notifies Changing and Changed on Delete
private void DeleteItem(SomeObject someObject)
{
if (this.BindingList.IndexOf(someObject) > 0)
{
// this unlinks the handlers so the garbage collector can clear the objects
someObject.PropertyChanging -= new PropertyChangingEventHandler(someObject_PropertyChanging);
someObject.PropertyChanged -= new PropertyChangedEventHandler(someObject_PropertyChanged);
}
this.NotifyPropertyChanging("BindingList");
this.BindingList.Remove(someObject);
this.NotifyPropertyChanged("BindingList");
}
// this notifies an item in the list is about to change
void someObject_PropertyChanging(object sender, PropertyChangingEventArgs e)
{
NotifyPropertyChanging("BindingList." + e.PropertyName);
}
// this notifies an item in the list has changed
void someObject_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyPropertyChanged("BindingList." + e.PropertyName);
}
#region INotifyPropertyChanging Members
public event PropertyChangingEventHandler PropertyChanging;
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public void NotifyPropertyChanging(string propertyName)
{
if (this.PropertyChanging != null)
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
public void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
//此对象将是BindingList的类型
公共类SomeObject:INotifyPropertyChanged、INotifyPropertyChanged
{
私有int _intProperty=0;
私有字符串_strProperty=string.Empty;
公共int属性
{
获取{返回此。_intProperty;}
设置
{
if(this.\u intProperty!=值)
{
NotifyPropertyChange(“IntProperty”);
这是。_intProperty=value;
NotifyPropertyChanged(“IntProperty”);
}
}
}
公共字符串StrProperty
{
获取{返回此属性。\ strProperty;}
设置
{
if(此属性!=值)
{
通知财产变更(“StrProperty”);
这是。\ u strProperty=值;
NotifyPropertyChanged(“StrProperty”);
}
}
}
#区域索引属性更改成员
公共事件属性更改EventHandler属性更改;
#端区
#区域INotifyProperty更改成员
公共事件属性更改事件处理程序属性更改;
#端区
public void notifyPropertyChangeg(字符串propertyName)
{
if(this.PropertyChanging!=null)
PropertyChange(这是新的PropertyChangingEventArgs(propertyName));
}
public void NotifyPropertyChanged(字符串propertyName)
{
if(this.PropertyChanged!=null)
PropertyChanged(这是新的PropertyChangedEventArgs(propertyName));
}
}
保存列表的公共类对象:INotifyPropertyChanged、INotifyPropertyChanged
{
公共绑定列表绑定列表{get;set;}
持有列表()的公共对象
{
this.BindingList=新的BindingList();
}
//这有助于通知更改和添加时更改
私有void附加项(SomeObject SomeObject)
{
//这会将SomeObject的PropertyChanged和PropertyChanged事件绑定到此对象
//因此它会收到通知,因为BindingList不会通知PropertyChange
someObject.PropertyChange+=新的PropertyChangingEventHandler(someObject\u PropertyChange);
someObject.PropertyChanged+=新的PropertyChangedEventHandler(someObject\u PropertyChanged);
此.notifyPropertyChange(“绑定列表”);
this.BindingList.Add(someObject);
此.NotifyPropertyChanged(“BindingList”);
}
//这有助于通知更改和删除时更改
私有void DeleteItem(SomeObject SomeObject)
{
if(this.BindingList.IndexOf(someObject)>0)
{
//这将取消处理程序的链接,以便垃圾收集器可以清除对象
someObject.PropertyChange-=新的PropertyChangingEventHandler(someObject\u PropertyChange);
someObject.PropertyChanged-=新的PropertyChangedEventHandler(someObject\u PropertyChanged);
}
此.notifyPropertyChange(“绑定列表”);
this.BindingList.Remove(someObject);
此.NotifyPropertyChanged(“BindingList”);
}
//这将通知列表中的项目即将更改
void someObject\u PropertyChanging(对象发送方,PropertyChangingEventArgs e)
{
NotifyPropertyChange(“BindingList.+e.PropertyName”);
}
//这将通知列表中的项目已更改
void someObject\u PropertyChanged(对象发送方,PropertyChangedEventArgs e)
{
NotifyPropertyChanged(“BindingList.+e.PropertyName”);
}
#区域索引属性更改成员
公共事件属性更改EventHandler属性更改;
#端区
#区域INotifyProperty更改成员
公共事件属性更改事件处理程序属性更改;
#端区
public void notifyPropertyChangeg(字符串propertyName)
{
if(this.PropertyChanging!=null)
PropertyChange(这是新的PropertyChangingEventArgs(propertyName));
}
public void NotifyPropertyChanged(字符串propertyName)
{
if(this.PropertyChanged!=null)
PropertyChanged(这是新的PropertyChangedEventArgs(propertyName));
}
}
对不起,我知道这是很多代码,这让我回到我的主要观点,实现这一点需要很多代码。所以我的问题是,有人知道更好、更短、更优雅的解决方案吗
感谢您的时间和建议。您可以创建一个包装器类,该类实现ICustomTypeDescriptor。此包装器还将实现必要的接口(例如INotifyPropertyChanging),拦截底层对象上的属性读/写,并且您将能够调用由包装器实现的NotifyPropertyChanging()和NotifyPropertyChanged()方法。数据使用者将使用与使用原始对象相同的包装对象 但是,如果您不是一名经验丰富的开发人员,那么实现这样一个包装器并不容易 下面是这种包装器的可能但尚未完成的实现。它已经支持INotifyPropertyChanged,并且很容易理解如何实现INotifyPropertyChanged
public class Wrapper : ICustomTypeDescriptor, INotifyPropertyChanged, IEditableObject, IChangeTracking
{
private bool _isChanged;
public object DataSource { get; set; }
public Wrapper(object dataSource)
{
if (dataSource == null)
throw new ArgumentNullException("dataSource");
DataSource = dataSource;
}
#region ICustomTypeDescriptor Members
public AttributeCollection GetAttributes()
{
return new AttributeCollection(
DataSource.GetType()
.GetCustomAttributes(true)
.OfType<Attribute>()
.ToArray());
}
public string GetClassName()
{
return DataSource.GetType().Name;
}
public string GetComponentName()
{
return DataSource.ToString();
}
public TypeConverter GetConverter()
{
return new TypeConverter();
}
public EventDescriptor GetDefaultEvent()
{
return null;
}
public PropertyDescriptor GetDefaultProperty()
{
return null;
}
public object GetEditor(Type editorBaseType)
{
return Activator.CreateInstance(editorBaseType);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(DataSource, attributes);
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(DataSource);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return GetProperties();
}
private IEnumerable<PropertyDescriptor> _Properties;
public IEnumerable<PropertyDescriptor> Properties
{
get
{
if (_Properties == null)
_Properties = TypeDescriptor.GetProperties(DataSource)
.Cast<PropertyDescriptor>()
.Select(pd => new WrapperPropertyDescriptor(pd) as PropertyDescriptor)
.ToList();
return _Properties;
}
}
public PropertyDescriptorCollection GetProperties()
{
return new PropertyDescriptorCollection(Properties.ToArray());
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
#endregion ICustomTypeDescriptor
#region ToString, Equals, GetHashCode
public override string ToString()
{
return DataSource.ToString();
}
public override bool Equals(object obj)
{
var wrapper = obj as Wrapper;
if (wrapper == null)
return base.Equals(obj);
else
return DataSource.Equals(wrapper.DataSource);
}
public override int GetHashCode()
{
return DataSource.GetHashCode();
}
#endregion
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (String.IsNullOrEmpty(propertyName))
throw new ArgumentNullException("propertyName");
_isChanged = true;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public IDictionary<string, object> MakeDump()
{
var result = new Dictionary<String, object>();
foreach (var item in Properties)
result[item.Name] = item.GetValue(this);
return result;
}
#region IEditableObject Members
private IDictionary<string, object> LastDump;
public void BeginEdit()
{
LastDump = MakeDump();
}
public void CancelEdit()
{
if (LastDump != null)
{
foreach (var item in Properties)
item.SetValue(this, LastDump[item.Name]);
_isChanged = false;
}
}
public void EndEdit()
{
AcceptChanges();
}
#endregion IEditableObject
#region IChangeTracking
public void AcceptChanges()
{
LastDump = null;
_isChanged = false;
}
public bool IsChanged
{
get { return _isChanged; }
}
#endregion IChangeTracking
}
public class WrapperPropertyDescriptor : PropertyDescriptor
{
private Wrapper _wrapper;
private readonly PropertyDescriptor SourceDescriptor;
public WrapperPropertyDescriptor(PropertyDescriptor sourceDescriptor) :
base(sourceDescriptor)
{
if (sourceDescriptor == null)
throw new ArgumentNullException("sourceDescriptor");
SourceDescriptor = sourceDescriptor;
}
public override Type ComponentType
{
get
{
return SourceDescriptor.ComponentType;
}
}
public override bool IsReadOnly
{
get
{
return SourceDescriptor.IsReadOnly;
}
}
public override Type PropertyType
{
get
{
return SourceDescriptor.PropertyType;
}
}
public override object GetValue(object component)
{
var wrapper = component as Wrapper;
if (wrapper == null)
throw new ArgumentException("Unexpected component", "component");
var value = SourceDescriptor.GetValue(wrapper.DataSource);
if (value == null)
return value;
var type = value.GetType();
// If value is user class or structure it should
// be wrapped before return.
if (type.Assembly != typeof(String).Assembly)
{
if (typeof(IEnumerable).IsAssignableFrom(type))
throw new NotImplementedException("Here we should construct and return wrapper for collection");
if (_wrapper == null)
_wrapper = new Wrapper(value);
else
_wrapper.DataSource = value;
return _wrapper;
}
return value;
}
public override void SetValue(object component, object value)
{
var wrapper = component as Wrapper;
if (wrapper == null)
throw new ArgumentException("Unexpected component", "component");
var actualValue = value;
var valueWrapper = value as Wrapper;
if (valueWrapper != null)
actualValue = valueWrapper.DataSource;
// Make dump of data source's previous values
var dump = wrapper.MakeDump();
SourceDescriptor.SetValue(wrapper.DataSource, actualValue);
foreach (var item in wrapper.Properties)
{
var itemValue = item.GetValue(wrapper);
if (!itemValue.Equals(dump[item.Name]))
wrapper.OnPropertyChanged(item.Name);
}
}
public override void ResetValue(object component)
{
var wrapper = component as Wrapper;
if (wrapper == null)
throw new ArgumentException("Unexpected component", "component");
SourceDescriptor.ResetValue(wrapper.DataSource);
}
public override bool ShouldSerializeValue(object component)
{
var wrapper = component as Wrapper;
if (wrapper == null)
throw new ArgumentException("Unexpected component", "component");
return SourceDescriptor.ShouldSerializeValue(wrapper.DataSource);
}
public override bool CanResetValue(object component)
{
var wrapper = component as Wrapper;
if (wrapper == null)
throw new ArgumentException("Unexpected component", "component");
return SourceDescriptor.CanResetValue(wrapper.DataSource);
}
}
公共类包装器:ICustomTypeDescriptor、INotifyPropertyChanged、IEditableObject、IChangeTracking
{
私人住宅被改变;
公共对象数据源{get;set;}
公共包装器(对象数据源)
{
如果(数据源==null)
抛出新的ArgumentNullException(“数据源”);
数据源=数据源;
}
#区域ICustomTypeDescriptor成员
IList<Customer> customers = CustomerRepository.GetAllCustomers();
IList<Wrapper> wrappedCustomers = customers.Select(c => new Wrapper(c)).ToList();
/* If you don't like LINQ in the line above you can use foreach to transform
list of Customer object to a list of Wrapper<Customer> objects */
comboBoxCustomers.DataSource = wrappedCustomers;
// or
dataGridViewCustomers.DataSource = wrappedCustomers;