C# 列出NotifyPropertyChange

C# 列出NotifyPropertyChange,c#,collections,C#,Collections,BindingList和ObservableCollection可以很好地保持数据更新,并在其中一个对象发生更改时发出通知。但是,当通知一个属性即将更改时,我认为这些选项不是很好 要解决这个问题,我现在要做的是(我警告说这一点也不优雅),在列表的类型对象上实现INotifyPropertyChanging,然后将其绑定到保存列表PropertyChanging事件的对象,或者类似于以下内容: // this object will be the type of the BindingList p

BindingList和ObservableCollection可以很好地保持数据更新,并在其中一个对象发生更改时发出通知。但是,当通知一个属性即将更改时,我认为这些选项不是很好

要解决这个问题,我现在要做的是(我警告说这一点也不优雅),在列表的类型对象上实现INotifyPropertyChanging,然后将其绑定到保存列表PropertyChanging事件的对象,或者类似于以下内容:

// 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;