当 Item 更改时通知 ObservableCollection

发布于 2024-12-21 02:56:28 字数 3440 浏览 1 评论 0原文

我在此链接上发现

ObservableCollection 没有注意到当其中的 Item 更改时(即使使用 INotifyPropertyChanged)

一些通知 Observablecollection 项目已更改的技术。此链接中的 TrulyObservableCollection 似乎就是我正在寻找的。

public class TrulyObservableCollection<T> : ObservableCollection<T>
where T : INotifyPropertyChanged
{
    public TrulyObservableCollection()
    : base()
    {
        CollectionChanged += new NotifyCollectionChangedEventHandler(TrulyObservableCollection_CollectionChanged);
    }

    void TrulyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (Object item in e.NewItems)
            {
                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
        if (e.OldItems != null)
        {
            foreach (Object item in e.OldItems)
            {
                (item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
    }

    void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
        OnCollectionChanged(a);
    }
}

但是当我尝试使用它时,我没有收到有关集合的通知。我不确定如何在我的 C# 代码中正确实现此功能:

XAML :

    <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding MyItemsSource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
        <DataGrid.Columns>
            <DataGridCheckBoxColumn Binding="{Binding MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        </DataGrid.Columns>
    </DataGrid>

ViewModel :

public class MyViewModel : ViewModelBase
{
    private TrulyObservableCollection<MyType> myItemsSource;
    public TrulyObservableCollection<MyType> MyItemsSource
    {
        get { return myItemsSource; }
        set 
        { 
            myItemsSource = value; 
            // Code to trig on item change...
            RaisePropertyChangedEvent("MyItemsSource");
        }
    }

    public MyViewModel()
    {
        MyItemsSource = new TrulyObservableCollection<MyType>()
        { 
            new MyType() { MyProperty = false },
            new MyType() { MyProperty = true },
            new MyType() { MyProperty = false }
        };
    }
}

public class MyType : ViewModelBase
{
    private bool myProperty;
    public bool MyProperty
    {
        get { return myProperty; }
        set 
        {
            myProperty = value;
            RaisePropertyChangedEvent("MyProperty");
        }
    }
}

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChangedEvent(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
            PropertyChanged(this, e);
        }
    }
}

当我运行程序时,我将 3 个复选框设置为 false、true、false,如属性初始化中所示。 但是当我更改其中一个 ckeckbox 的状态时,程序会通过 item_PropertyChanged 但不会在 MyItemsSource 属性代码中。

I found on this link

ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)

some techniques to notify a Observablecollection that an item has changed. the TrulyObservableCollection in this link seems to be what i'm looking for.

public class TrulyObservableCollection<T> : ObservableCollection<T>
where T : INotifyPropertyChanged
{
    public TrulyObservableCollection()
    : base()
    {
        CollectionChanged += new NotifyCollectionChangedEventHandler(TrulyObservableCollection_CollectionChanged);
    }

    void TrulyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (Object item in e.NewItems)
            {
                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
        if (e.OldItems != null)
        {
            foreach (Object item in e.OldItems)
            {
                (item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
    }

    void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
        OnCollectionChanged(a);
    }
}

But when I try to use it, I don't get notifications on the collection. I'm not sure how to correctly implement this in my C# Code:

XAML :

    <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding MyItemsSource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
        <DataGrid.Columns>
            <DataGridCheckBoxColumn Binding="{Binding MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        </DataGrid.Columns>
    </DataGrid>

ViewModel :

public class MyViewModel : ViewModelBase
{
    private TrulyObservableCollection<MyType> myItemsSource;
    public TrulyObservableCollection<MyType> MyItemsSource
    {
        get { return myItemsSource; }
        set 
        { 
            myItemsSource = value; 
            // Code to trig on item change...
            RaisePropertyChangedEvent("MyItemsSource");
        }
    }

    public MyViewModel()
    {
        MyItemsSource = new TrulyObservableCollection<MyType>()
        { 
            new MyType() { MyProperty = false },
            new MyType() { MyProperty = true },
            new MyType() { MyProperty = false }
        };
    }
}

public class MyType : ViewModelBase
{
    private bool myProperty;
    public bool MyProperty
    {
        get { return myProperty; }
        set 
        {
            myProperty = value;
            RaisePropertyChangedEvent("MyProperty");
        }
    }
}

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChangedEvent(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
            PropertyChanged(this, e);
        }
    }
}

When i run the program, i have the 3 checkbox to false, true, false as in the property initialisation.
but when i change the state of one of the ckeckbox, the program go through item_PropertyChanged but never in MyItemsSource Property code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

仙气飘飘 2024-12-28 02:56:28

您注释为 // Code to trig on item change... 的位置只会在集合对象发生更改时触发,例如当它设置为新对象或设置为 null 时。

使用您当前的 TrulyObservableCollection 实现,要处理集合的属性更改事件,请向 MyItemsSourceCollectionChanged 事件注册一些内容,

public MyViewModel()
{
    MyItemsSource = new TrulyObservableCollection<MyType>();
    MyItemsSource.CollectionChanged += MyItemsSource_CollectionChanged;

    MyItemsSource.Add(new MyType() { MyProperty = false });
    MyItemsSource.Add(new MyType() { MyProperty = true});
    MyItemsSource.Add(new MyType() { MyProperty = false });
}


void MyItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    // Handle here
}

我个人真的不喜欢这种实现。您将引发一个 CollectionChanged 事件,该事件表示只要属性发生更改,整个集合就已重置。当然,只要集合中的项目发生变化,它就会使 UI 更新,但我发现这对性能不利,而且它似乎没有办法识别更改的属性,这是关键信息之一我通常在 PropertyChanged 上执行某些操作时需要。

我更喜欢使用常规 ObservableCollection 并将 PropertyChanged 事件连接到 CollectionChanged 上的项目。如果您的 UI 已正确绑定到 ObservableCollection 中的项目,那么当集合中项目的属性发生更改时,您不需要告诉 UI 进行更新。

public MyViewModel()
{
    MyItemsSource = new ObservableCollection<MyType>();
    MyItemsSource.CollectionChanged += MyItemsSource_CollectionChanged;

    MyItemsSource.Add(new MyType() { MyProperty = false });
    MyItemsSource.Add(new MyType() { MyProperty = true});
    MyItemsSource.Add(new MyType() { MyProperty = false });
}

void MyItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
        foreach(MyType item in e.NewItems)
            item.PropertyChanged += MyType_PropertyChanged;

    if (e.OldItems != null)
        foreach(MyType item in e.OldItems)
            item.PropertyChanged -= MyType_PropertyChanged;
}

void MyType_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "MyProperty")
        DoWork();
}

The spot you have commented as // Code to trig on item change... will only trigger when the collection object gets changed, such as when it gets set to a new object, or set to null.

With your current implementation of TrulyObservableCollection, to handle the property changed events of your collection, register something to the CollectionChanged event of MyItemsSource

public MyViewModel()
{
    MyItemsSource = new TrulyObservableCollection<MyType>();
    MyItemsSource.CollectionChanged += MyItemsSource_CollectionChanged;

    MyItemsSource.Add(new MyType() { MyProperty = false });
    MyItemsSource.Add(new MyType() { MyProperty = true});
    MyItemsSource.Add(new MyType() { MyProperty = false });
}


void MyItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    // Handle here
}

Personally I really don't like this implementation. You are raising a CollectionChanged event that says the entire collection has been reset, anytime a property changes. Sure it'll make the UI update anytime an item in the collection changes, but I see that being bad on performance, and it doesn't seem to have a way to identify what property changed, which is one of the key pieces of information I usually need when doing something on PropertyChanged.

I prefer using a regular ObservableCollection and just hooking up the PropertyChanged events to it's items on CollectionChanged. Providing your UI is bound correctly to the items in the ObservableCollection, you shouldn't need to tell the UI to update when a property on an item in the collection changes.

public MyViewModel()
{
    MyItemsSource = new ObservableCollection<MyType>();
    MyItemsSource.CollectionChanged += MyItemsSource_CollectionChanged;

    MyItemsSource.Add(new MyType() { MyProperty = false });
    MyItemsSource.Add(new MyType() { MyProperty = true});
    MyItemsSource.Add(new MyType() { MyProperty = false });
}

void MyItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
        foreach(MyType item in e.NewItems)
            item.PropertyChanged += MyType_PropertyChanged;

    if (e.OldItems != null)
        foreach(MyType item in e.OldItems)
            item.PropertyChanged -= MyType_PropertyChanged;
}

void MyType_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "MyProperty")
        DoWork();
}
混浊又暗下来 2024-12-28 02:56:28

一个简单的解决方案是使用 BindingList 而不是 ObservableCollection 。事实上,BindingList 中继了项目更改通知。因此,对于绑定列表,如果该项目实现了接口 INotifyPropertyChanged,那么您只需使用 ListChanged 事件

另请参阅这个答案。

A simple solution is to use BindingList<T> instead of ObservableCollection<T> . Indeed the BindingList relay item change notifications. So with a binding list, if the item implements the interface INotifyPropertyChanged then you can simply get notifications using the ListChanged event.

See also this SO answer.

绝對不後悔。 2024-12-28 02:56:28

我通过使用静态操作解决了这个问题


public class CatalogoModel 
{
    private String _Id;
    private String _Descripcion;
    private Boolean _IsChecked;

    public String Id
    {
        get { return _Id; }
        set { _Id = value; }
    }
    public String Descripcion
    {
        get { return _Descripcion; }
        set { _Descripcion = value; }
    }
    public Boolean IsChecked
    {
        get { return _IsChecked; }
        set
        {
           _IsChecked = value;
            NotifyPropertyChanged("IsChecked");
            OnItemChecked.Invoke();
        }
    }

    public static Action OnItemChecked;
} 

public class ReglaViewModel : ViewModelBase
{
    private ObservableCollection<CatalogoModel> _origenes;

    CatalogoModel.OnItemChecked = () =>
            {
                var x = Origenes.Count;  //Entra cada vez que cambia algo en _origenes
            };
}

I solved this case by using static Action


public class CatalogoModel 
{
    private String _Id;
    private String _Descripcion;
    private Boolean _IsChecked;

    public String Id
    {
        get { return _Id; }
        set { _Id = value; }
    }
    public String Descripcion
    {
        get { return _Descripcion; }
        set { _Descripcion = value; }
    }
    public Boolean IsChecked
    {
        get { return _IsChecked; }
        set
        {
           _IsChecked = value;
            NotifyPropertyChanged("IsChecked");
            OnItemChecked.Invoke();
        }
    }

    public static Action OnItemChecked;
} 

public class ReglaViewModel : ViewModelBase
{
    private ObservableCollection<CatalogoModel> _origenes;

    CatalogoModel.OnItemChecked = () =>
            {
                var x = Origenes.Count;  //Entra cada vez que cambia algo en _origenes
            };
}
云雾 2024-12-28 02:56:28

您可以使用扩展方法以通用方式获取有关集合中项目属性更改的通知。

public static class ObservableCollectionExtension
{
    public static void NotifyPropertyChanged<T>(this ObservableCollection<T> observableCollection, Action<T, PropertyChangedEventArgs> callBackAction)
        where T : INotifyPropertyChanged
    {
        observableCollection.CollectionChanged += (sender, args) =>
        {
            //Does not prevent garbage collection says: http://stackoverflow.com/questions/298261/do-event-handlers-stop-garbage-collection-from-occuring
            //publisher.SomeEvent += target.SomeHandler;
            //then "publisher" will keep "target" alive, but "target" will not keep "publisher" alive.
            if (args.NewItems == null) return;
            foreach (T item in args.NewItems)
            {
                item.PropertyChanged += (obj, eventArgs) =>
                {
                    callBackAction((T)obj, eventArgs);
                };
            }
        };
    }
}

public void ExampleUsage()
{
    var myObservableCollection = new ObservableCollection<MyTypeWithNotifyPropertyChanged>();
    myObservableCollection.NotifyPropertyChanged((obj, notifyPropertyChangedEventArgs) =>
    {
        //DO here what you want when a property of an item in the collection has changed.
    });
}

You could use an extension method to get notified about changed property of an item in a collection in a generic way.

public static class ObservableCollectionExtension
{
    public static void NotifyPropertyChanged<T>(this ObservableCollection<T> observableCollection, Action<T, PropertyChangedEventArgs> callBackAction)
        where T : INotifyPropertyChanged
    {
        observableCollection.CollectionChanged += (sender, args) =>
        {
            //Does not prevent garbage collection says: http://stackoverflow.com/questions/298261/do-event-handlers-stop-garbage-collection-from-occuring
            //publisher.SomeEvent += target.SomeHandler;
            //then "publisher" will keep "target" alive, but "target" will not keep "publisher" alive.
            if (args.NewItems == null) return;
            foreach (T item in args.NewItems)
            {
                item.PropertyChanged += (obj, eventArgs) =>
                {
                    callBackAction((T)obj, eventArgs);
                };
            }
        };
    }
}

public void ExampleUsage()
{
    var myObservableCollection = new ObservableCollection<MyTypeWithNotifyPropertyChanged>();
    myObservableCollection.NotifyPropertyChanged((obj, notifyPropertyChangedEventArgs) =>
    {
        //DO here what you want when a property of an item in the collection has changed.
    });
}
-小熊_ 2024-12-28 02:56:28

这里的所有解决方案都是正确的,但它们缺少一个重要的场景,其中方法 Clear(),它不会在 NotifyCollectionChangedEventArgs 对象中提供 OldItems

这是完美的 ObservableCollection

public delegate void ListedItemPropertyChangedEventHandler(IList SourceList, object Item, PropertyChangedEventArgs e);
public class ObservableCollectionEX<T> : ObservableCollection<T>
{
    #region Constructors
    public ObservableCollectionEX() : base()
    {
        CollectionChanged += ObservableCollection_CollectionChanged;
    }
    public ObservableCollectionEX(IEnumerable<T> c) : base(c)
    {
        CollectionChanged += ObservableCollection_CollectionChanged;
    }
    public ObservableCollectionEX(List<T> l) : base(l)
    {
        CollectionChanged += ObservableCollection_CollectionChanged;
    }

    #endregion



    public new void Clear()
    {
        foreach (var item in this)            
            if (item is INotifyPropertyChanged i)                
                i.PropertyChanged -= Element_PropertyChanged;            
        base.Clear();
    }
    private void ObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.OldItems != null)
            foreach (var item in e.OldItems)                
                if (item != null && item is INotifyPropertyChanged i)                    
                    i.PropertyChanged -= Element_PropertyChanged;


        if (e.NewItems != null)
            foreach (var item in e.NewItems)                
                if (item != null && item is INotifyPropertyChanged i)
                {
                    i.PropertyChanged -= Element_PropertyChanged;
                    i.PropertyChanged += Element_PropertyChanged;
                }
            }
    }
    private void Element_PropertyChanged(object sender, PropertyChangedEventArgs e) => ItemPropertyChanged?.Invoke(this, sender, e);


    public ListedItemPropertyChangedEventHandler ItemPropertyChanged;

}

All the solutions here are correct,but they are missing an important scenario in which the method Clear() is used, which doesn't provide OldItems in the NotifyCollectionChangedEventArgs object.

this is the perfect ObservableCollection .

public delegate void ListedItemPropertyChangedEventHandler(IList SourceList, object Item, PropertyChangedEventArgs e);
public class ObservableCollectionEX<T> : ObservableCollection<T>
{
    #region Constructors
    public ObservableCollectionEX() : base()
    {
        CollectionChanged += ObservableCollection_CollectionChanged;
    }
    public ObservableCollectionEX(IEnumerable<T> c) : base(c)
    {
        CollectionChanged += ObservableCollection_CollectionChanged;
    }
    public ObservableCollectionEX(List<T> l) : base(l)
    {
        CollectionChanged += ObservableCollection_CollectionChanged;
    }

    #endregion



    public new void Clear()
    {
        foreach (var item in this)            
            if (item is INotifyPropertyChanged i)                
                i.PropertyChanged -= Element_PropertyChanged;            
        base.Clear();
    }
    private void ObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.OldItems != null)
            foreach (var item in e.OldItems)                
                if (item != null && item is INotifyPropertyChanged i)                    
                    i.PropertyChanged -= Element_PropertyChanged;


        if (e.NewItems != null)
            foreach (var item in e.NewItems)                
                if (item != null && item is INotifyPropertyChanged i)
                {
                    i.PropertyChanged -= Element_PropertyChanged;
                    i.PropertyChanged += Element_PropertyChanged;
                }
            }
    }
    private void Element_PropertyChanged(object sender, PropertyChangedEventArgs e) => ItemPropertyChanged?.Invoke(this, sender, e);


    public ListedItemPropertyChangedEventHandler ItemPropertyChanged;

}
明明#如月 2024-12-28 02:56:28

我知道已经晚了,但这也许对其他人有帮助。我创建了一个类 NotifyObservableCollection,它解决了当项目的属性发生更改时缺少对项目本身的通知的问题。用法就像ObservableCollection一样简单。

public class NotifyObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
    private void Handle(object sender, PropertyChangedEventArgs args)
    {
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, null));
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null) {
            foreach (object t in e.NewItems) {
                ((T) t).PropertyChanged += Handle;
            }
        }
        if (e.OldItems != null) {
            foreach (object t in e.OldItems) {
                ((T) t).PropertyChanged -= Handle;
            }
        }
        base.OnCollectionChanged(e);
    }

添加或删除项目时,该类会将项目 PropertyChanged 事件转发到集合 PropertyChanged 事件。

用法:

public abstract class ParameterBase : INotifyPropertyChanged
{
    protected readonly CultureInfo Ci = new CultureInfo("en-US");
    private string _value;

    public string Value {
        get { return _value; }
        set {
            if (value == _value) return;
            _value = value;
            OnPropertyChanged();
        }
    }
}

public class AItem {
    public NotifyObservableCollection<ParameterBase> Parameters {
        get { return _parameters; }
        set {
            NotifyCollectionChangedEventHandler cceh = (sender, args) => OnPropertyChanged();
            if (_parameters != null) _parameters.CollectionChanged -= cceh;
            _parameters = value;
            //needed for Binding to AItem at xaml directly
            _parameters.CollectionChanged += cceh; 
        }
    }

    public NotifyObservableCollection<ParameterBase> DefaultParameters {
        get { return _defaultParameters; }
        set {
            NotifyCollectionChangedEventHandler cceh = (sender, args) => OnPropertyChanged();
            if (_defaultParameters != null) _defaultParameters.CollectionChanged -= cceh;
            _defaultParameters = value;
            //needed for Binding to AItem at xaml directly
            _defaultParameters.CollectionChanged += cceh;
        }
    }


public class MyViewModel {
    public NotifyObservableCollection<AItem> DataItems { get; set; }
}

如果现在 DataItems 中某个项目的属性发生更改,则以下 xaml 将收到通知,尽管它绑定到 Parameters[0] 或项目本身(除了项目的更改属性Value(触发器处的转换器在每次更改时都称为可靠)。

<DataGrid CanUserAddRows="False" AutoGenerateColumns="False" ItemsSource="{Binding DataItems}">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Parameters[0].Value}" Header="P1">
            <DataGridTextColumn.CellStyle>
                <Style TargetType="DataGridCell">
                    <Setter Property="Background" Value="Aqua" />
                    <Style.Triggers>
                        <DataTrigger Value="False">
                            <!-- Bind to Items with changing properties -->
                            <DataTrigger.Binding>
                                <MultiBinding Converter="{StaticResource ParameterCompareConverter}">
                                    <Binding Path="DefaultParameters[0]" />
                                    <Binding Path="Parameters[0]" />
                                </MultiBinding>
                            </DataTrigger.Binding>
                            <Setter Property="Background" Value="DeepPink" />
                        </DataTrigger>
                        <!-- Binds to AItem directly -->
                        <DataTrigger Value="True" Binding="{Binding Converter={StaticResource CheckParametersConverter}}">
                            <Setter Property="FontWeight" Value="ExtraBold" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </DataGridTextColumn.CellStyle>
        </DataGridTextColumn>

I know it's late, but maybe this helps others. I have created a class NotifyObservableCollection, that solves the problem of missing notification to item itself, when a property of the item changes. The usage is as simple as ObservableCollection.

public class NotifyObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
    private void Handle(object sender, PropertyChangedEventArgs args)
    {
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, null));
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null) {
            foreach (object t in e.NewItems) {
                ((T) t).PropertyChanged += Handle;
            }
        }
        if (e.OldItems != null) {
            foreach (object t in e.OldItems) {
                ((T) t).PropertyChanged -= Handle;
            }
        }
        base.OnCollectionChanged(e);
    }

While Items are added or removed the class forwards the items PropertyChanged event to the collections PropertyChanged event.

usage:

public abstract class ParameterBase : INotifyPropertyChanged
{
    protected readonly CultureInfo Ci = new CultureInfo("en-US");
    private string _value;

    public string Value {
        get { return _value; }
        set {
            if (value == _value) return;
            _value = value;
            OnPropertyChanged();
        }
    }
}

public class AItem {
    public NotifyObservableCollection<ParameterBase> Parameters {
        get { return _parameters; }
        set {
            NotifyCollectionChangedEventHandler cceh = (sender, args) => OnPropertyChanged();
            if (_parameters != null) _parameters.CollectionChanged -= cceh;
            _parameters = value;
            //needed for Binding to AItem at xaml directly
            _parameters.CollectionChanged += cceh; 
        }
    }

    public NotifyObservableCollection<ParameterBase> DefaultParameters {
        get { return _defaultParameters; }
        set {
            NotifyCollectionChangedEventHandler cceh = (sender, args) => OnPropertyChanged();
            if (_defaultParameters != null) _defaultParameters.CollectionChanged -= cceh;
            _defaultParameters = value;
            //needed for Binding to AItem at xaml directly
            _defaultParameters.CollectionChanged += cceh;
        }
    }


public class MyViewModel {
    public NotifyObservableCollection<AItem> DataItems { get; set; }
}

If now a property of an item in DataItems changes, the following xaml will get a notification, though it binds to Parameters[0] or to the item itself except to the changing property Value of the item (Converters at Triggers are called reliable on every change).

<DataGrid CanUserAddRows="False" AutoGenerateColumns="False" ItemsSource="{Binding DataItems}">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Parameters[0].Value}" Header="P1">
            <DataGridTextColumn.CellStyle>
                <Style TargetType="DataGridCell">
                    <Setter Property="Background" Value="Aqua" />
                    <Style.Triggers>
                        <DataTrigger Value="False">
                            <!-- Bind to Items with changing properties -->
                            <DataTrigger.Binding>
                                <MultiBinding Converter="{StaticResource ParameterCompareConverter}">
                                    <Binding Path="DefaultParameters[0]" />
                                    <Binding Path="Parameters[0]" />
                                </MultiBinding>
                            </DataTrigger.Binding>
                            <Setter Property="Background" Value="DeepPink" />
                        </DataTrigger>
                        <!-- Binds to AItem directly -->
                        <DataTrigger Value="True" Binding="{Binding Converter={StaticResource CheckParametersConverter}}">
                            <Setter Property="FontWeight" Value="ExtraBold" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </DataGridTextColumn.CellStyle>
        </DataGridTextColumn>
寻找一个思念的角度 2024-12-28 02:56:28

ObservableCollection 及其派生类在内部引发其属性更改。仅当您将新的 TrulyObservableCollection 分配给 MyItemsSource 属性时,才应触发 setter 中的代码。也就是说,它应该只在构造函数中发生一次。

从那时起,您将从集合中获取属性更改通知,而不是从视图模型中的 setter 中获取。

The ObservableCollection and its derivatives raises its property changes internally. The code in your setter should only be triggered if you assign a new TrulyObservableCollection<MyType> to the MyItemsSource property. That is, it should only happen once, from the constructor.

From that point forward, you'll get property change notifications from the collection, not from the setter in your viewmodel.

浪推晚风 2024-12-28 02:56:28

对此的一个简单解决方案是替换 ObservableCollection 中正在更改的项目,该项目会通知已更改项目的集合。在下面的示例代码片段中,Artists 是 ObservableCollection,artist 是 ObservableCollection 中该类型的项目:

    var index = Artists.IndexOf(artist);
    Artists.RemoveAt(index);
    artist.IsFollowed = true; // change something in the item
    Artists.Insert(index, artist);

One simple solution to this is to replace the item being changed in the ObservableCollection which notifies the collection of the changed item. In the sample code snippet below Artists is the ObservableCollection and artist is an item of the type in the ObservableCollection:

    var index = Artists.IndexOf(artist);
    Artists.RemoveAt(index);
    artist.IsFollowed = true; // change something in the item
    Artists.Insert(index, artist);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文