实施 CollectionChanged

发布于 2024-10-10 07:54:52 字数 262 浏览 2 评论 0原文

我已将 CollectionChanged eventhandler(onCollectionChanged) 添加到 ObservableCollection 属性之一。

我发现仅在向集合添加项目或删除项目的情况下才会调用 onCollectionChanged 方法,但在编辑集合项目的情况下则不会调用。

我想知道如何在单个集合中发送新添加、删除和编辑的项目的列表/集合。

谢谢。

I have added CollectionChanged eventhandler(onCollectionChanged) to one of the ObservableCollection property.

I have found out that onCollectionChanged method gets invoked only in case of add items or remove items to the collection, but not in the case of collection item gets edited.

I would like to know how to send the list/collection of newly added, removed and edited items in a single collection.

Thanks.

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

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

发布评论

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

评论(8

望她远 2024-10-17 07:54:52

您必须向每个项目添加一个 PropertyChanged 侦听器(必须实现 INotifyPropertyChanged),以获取有关编辑可观察列表中的对象的通知。

public ObservableCollection<Item> Names { get; set; }
public List<Item> ModifiedItems { get; set; }

public ViewModel()
{
   this.ModifiedItems = new List<Item>();

   this.Names = new ObservableCollection<Item>();
   this.Names.CollectionChanged += this.OnCollectionChanged;
}

void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
    {
        foreach(Item newItem in e.NewItems)
        {
            ModifiedItems.Add(newItem);

            //Add listener for each item on PropertyChanged event
            newItem.PropertyChanged += this.OnItemPropertyChanged;         
        }
    }

    if (e.OldItems != null)
    {
        foreach(Item oldItem in e.OldItems)
        {
            ModifiedItems.Add(oldItem);

            oldItem.PropertyChanged -= this.OnItemPropertyChanged;
        }
    }
}

void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    Item item = sender as Item;
    if(item != null)
       ModifiedItems.Add(item);
}

也许您必须检查 ModifedItems-List 中是否已存在某个项目(使用 List 的方法 Contains(object obj)),并且仅在该方法的结果为 false 时才添加新项目。

Item必须实现INotifyPropertyChanged。请参阅此示例了解具体操作方法。正如 Robert Rossney 所说,如果您有这样的要求,您也可以使用 IEditableObject 来实现这一点。

You have to add a PropertyChanged listener to each item (which must implement INotifyPropertyChanged) to get notification about editing objects in a observable list.

public ObservableCollection<Item> Names { get; set; }
public List<Item> ModifiedItems { get; set; }

public ViewModel()
{
   this.ModifiedItems = new List<Item>();

   this.Names = new ObservableCollection<Item>();
   this.Names.CollectionChanged += this.OnCollectionChanged;
}

void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
    {
        foreach(Item newItem in e.NewItems)
        {
            ModifiedItems.Add(newItem);

            //Add listener for each item on PropertyChanged event
            newItem.PropertyChanged += this.OnItemPropertyChanged;         
        }
    }

    if (e.OldItems != null)
    {
        foreach(Item oldItem in e.OldItems)
        {
            ModifiedItems.Add(oldItem);

            oldItem.PropertyChanged -= this.OnItemPropertyChanged;
        }
    }
}

void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    Item item = sender as Item;
    if(item != null)
       ModifiedItems.Add(item);
}

Maybe you have to check if some item is already in the ModifedItems-List (with List's method Contains(object obj)) and only add a new item if the result of that method is false.

The class Item must implement INotifyPropertyChanged. See this example to know how. As Robert Rossney said you can also make that with IEditableObject - if you have that requirement.

微暖i 2024-10-17 07:54:52

ItemsControl 侦听 CollectionChanged 来管理其在屏幕上呈现的项目集合的显示。 ContentControl 侦听 PropertyChanged 来管理屏幕上呈现的特定项目的显示。一旦你理解了这一点,就很容易在你的头脑中区分这两个概念。

这些界面都不会跟踪项目是否被编辑。属性更改不是编辑 - 也就是说,它们不一定代表用户发起的对对象状态的某种更改。例如,一个对象可能有一个由计时器不断更新的 ElapsedTime 属性; UI 需要收到这些属性更改事件的通知,但它们当然不代表对象底层数据的更改。

跟踪对象是否被编辑的标准方法是首先使该对象实现IEditableObject。然后,您可以在对象类的内部决定哪些更改构成编辑(即要求您调用 BeginEdit)以及哪些更改不构成编辑。然后,您可以实现一个布尔值 IsDirty 属性,该属性在调用 BeginEdit 时设置,并在 EndEditCancelEdit 被调用时清除。叫。 (我真的不明白为什么该属性不是 IEditableObject 的一部分;我还没有实现不需要它的可编辑对象。)

当然,没有必要实现它如果您不需要它,则可以使用第二级抽象 - 您当然可以监听PropertyChanged事件,并假设该对象在引发时已被编辑。这实际上取决于您的要求。

An ItemsControl listens to CollectionChanged to manage the display of the collection of items it's presenting on the screen. A ContentControl listens to PropertyChanged to manage the display of the specific item that it's presenting on the screen. It's pretty easy to keep the two concepts separate in your mind once you understand this.

Tracking whether or not an item is edited isn't something either of these interfaces does. Property changes aren't edits - that is, they don't necessarily represent some kind of user-initiated change to the state of the object. For instance, an object might have an ElapsedTime property that's being continuously updated by a timer; the UI needs to be notified of these property-change events, but they certainly don't represent changes in the object's underlying data.

The standard way to track whether or not an object is edited is to first make that object implement IEditableObject. You can then, internally to the object's class, decide what changes constitute an edit (i.e. require you to call BeginEdit) and what changes don't. You can then implement a boolean IsDirty property that gets set when BeginEdit is called and cleared when EndEdit or CancelEdit is called. (I really don't understand why that property isn't part of IEditableObject; I haven't yet implemented an editable object that didn't require it.)

Of course, there's no need to implement that second level of abstraction if you don't need it - you can certainly listen PropertyChanged event and just assume that the object has been edited if it gets raised. It really depends on your requirements.

恋你朝朝暮暮 2024-10-17 07:54:52

我对“此答案”的编辑被拒绝!
所以我把我的编辑放在这里:

void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
    foreach(Item newItem in e.NewItems)
    {
        ModifiedItems.Add(newItem);

        //Add listener for each item on PropertyChanged event
        if (e.Action == NotifyCollectionChangedAction.Add)
            newItem.PropertyChanged += this.ListTagInfo_PropertyChanged;
        else if (e.Action == NotifyCollectionChangedAction.Remove)
            newItem.PropertyChanged -= this.ListTagInfo_PropertyChanged;
    }
}

// MSDN: OldItems:Gets the list of items affected by a Replace, Remove, or Move action.  
//if (e.OldItems != null) <--- removed
}

My edit to 'this answer' is rejected!
So I put my edit here:

void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
    foreach(Item newItem in e.NewItems)
    {
        ModifiedItems.Add(newItem);

        //Add listener for each item on PropertyChanged event
        if (e.Action == NotifyCollectionChangedAction.Add)
            newItem.PropertyChanged += this.ListTagInfo_PropertyChanged;
        else if (e.Action == NotifyCollectionChangedAction.Remove)
            newItem.PropertyChanged -= this.ListTagInfo_PropertyChanged;
    }
}

// MSDN: OldItems:Gets the list of items affected by a Replace, Remove, or Move action.  
//if (e.OldItems != null) <--- removed
}
银河中√捞星星 2024-10-17 07:54:52

认为用实现INotifyPropertyChanged的项目填充ObservableCollection将导致CollectionChanged事件在项目引发时触发它的 PropertyChanged 通知。

再想一想,我认为您需要使用 BindingList 来获取单个项目更改,以这种开箱即用的方式传播。

否则,您需要手动订阅每个项目的更改通知并引发 CollectionChanged 事件。请注意,如果您要创建自己的派生 ObservableCollection,则必须在实例化时以及 Add()Insert() 上进行订阅,并通过 Remove()RemoveAt()Clear() 取消订阅。否则,您可以订阅 CollectionChanged 事件,并使用事件参数中添加和删除的项目来订阅/取消订阅。

I think that populating the ObservableCollection with items that implement INotifyPropertyChanged will cause the CollectionChanged event to fire when an item raises its PropertyChanged notification.

On second thought, I think you need to use BindingList<T> to get individual item changes to propagate in this way out-of-the-box.

Otherwise, you'll need to manually subscribe to each item's change notifications and raise the CollectionChanged event. Note that if you're creating your own, derived ObservableCollection<T>, you'll have to subscribe at instantiation and on Add() and Insert(), and unsubscribe on Remove(), RemoveAt() and Clear(). Otherwise, you can subscribe to the CollectionChanged event and use the added and removed items from the event args to subscribe/unsubscribe.

萌逼全场 2024-10-17 07:54:52

INotifyCollectionChangedINotiftyPropertyChanged 不同。更改底层对象的属性并不以任何方式表明集合已更改。

实现此行为的一种方法是创建一个自定义集合,该集合将在添加对象后询问该对象并注册 INotifyPropertyChanged.PropertyChanged 事件;当一个项目被删除时,它需要适当地取消注册。

使用此方法的一个警告是当您的对象嵌套 N 层深时。为了解决这个问题,您需要使用反射来询问每个属性,以确定它是否可能是另一个实现 INotifyCollectionChanged 的​​集合或需要遍历的其他容器。

这是一个未经测试的基本示例......

    public class ObservableCollectionExt<T> : ObservableCollection<T>
    {
        public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;

        protected override void SetItem(int index, T item)
        {
            base.SetItem(index, item);

            if(item is INotifyPropertyChanged)
                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
        }

        protected override void ClearItems()
        {
            for (int i = 0; i < this.Items.Count; i++)
                DeRegisterINotifyPropertyChanged(this.IndexOf(this.Items[i]));

            base.ClearItems();
        }

        protected override void InsertItem(int index, T item)
        {
            base.InsertItem(index, item);
            RegisterINotifyPropertyChanged(item);
        }

        protected override void RemoveItem(int index)
        {
            base.RemoveItem(index);
            DeRegisterINotifyPropertyChanged(index);
        }

        private void RegisterINotifyPropertyChanged(T item)
        {
            if (item is INotifyPropertyChanged)
                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
        }

        private void DeRegisterINotifyPropertyChanged(int index)
        {
            if (this.Items[index] is INotifyPropertyChanged)
                (this.Items[index] as INotifyPropertyChanged).PropertyChanged -= OnPropertyChanged;
        }

        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            T item = (T)sender;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, item)); 
        }
    }

INotifyCollectionChanged is not one in the same with INotiftyPropertyChanged. Changing properties of underlying objects does not in any way suggest the collection has changed.

One way to achieve this behavior is to create a custom collection which will interrogate the object once added and register for the INotifyPropertyChanged.PropertyChanged event; it would then need to de-register appropriately when an item is removed.

One caveat with this approach is when your objects are nested N levels deep. To solve this you will need to essentially interrogate each property using reflection to determine if it is perhaps yet another collection implementing INotifyCollectionChanged or other container which will need to be traversed.

Here is a rudimentary un-tested example...

    public class ObservableCollectionExt<T> : ObservableCollection<T>
    {
        public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;

        protected override void SetItem(int index, T item)
        {
            base.SetItem(index, item);

            if(item is INotifyPropertyChanged)
                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
        }

        protected override void ClearItems()
        {
            for (int i = 0; i < this.Items.Count; i++)
                DeRegisterINotifyPropertyChanged(this.IndexOf(this.Items[i]));

            base.ClearItems();
        }

        protected override void InsertItem(int index, T item)
        {
            base.InsertItem(index, item);
            RegisterINotifyPropertyChanged(item);
        }

        protected override void RemoveItem(int index)
        {
            base.RemoveItem(index);
            DeRegisterINotifyPropertyChanged(index);
        }

        private void RegisterINotifyPropertyChanged(T item)
        {
            if (item is INotifyPropertyChanged)
                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
        }

        private void DeRegisterINotifyPropertyChanged(int index)
        {
            if (this.Items[index] is INotifyPropertyChanged)
                (this.Items[index] as INotifyPropertyChanged).PropertyChanged -= OnPropertyChanged;
        }

        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            T item = (T)sender;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, item)); 
        }
    }
伴梦长久 2024-10-17 07:54:52

在 winforms 中,BindingList 是标准做法。在 WPF 和Silverlight,您通常会陷入使用 ObservableCollection 的状态,并且需要监听每个项目的 PropertyChanged

in winforms, BindingList is standard practice. in WPF & Silverlight, you are usually stuck working with ObservableCollection and need to listen for PropertyChanged on each item

淡淡の花香 2024-10-17 07:54:52

使用以下代码:

-my Model:

 public class IceCream: INotifyPropertyChanged
{
    private int liczba;

    public int Liczba
    {
        get { return liczba; }
        set { liczba = value;
        Zmiana("Liczba");
        }
    }

    public IceCream(){}

//in the same class implement the below-it will be responsible for track a changes

    public event PropertyChangedEventHandler PropertyChanged;

    private void Zmiana(string propertyName) 
    {
        if (PropertyChanged != null) 
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

在我的类 PersonList 中实现方法,负责在 AppBarControl 中单击按钮后主动增加 1 的值,

async private void Add_Click(object sender, RoutedEventArgs e)
    {
        List<IceCream> items = new List<IceCream>();
        foreach (IceCream item in IceCreamList.SelectedItems)
        {
            int i=Flavors.IndexOf(item);
            Flavors[i].Liczba =item.Liczba+ 1;
            //Flavors.Remove(item);

            //item.Liczba += 1;

           // items.Add(item);
           // Flavors.Add(item);
        }

        MessageDialog d = new MessageDialog("Zwiększono liczbę o jeden");
        d.Content = "Zwiększono liczbę o jeden";
        await d.ShowAsync();


        IceCreamList.SelectedIndex = -1;
    }
}

我希望这对某人有用
注意:

private ObservableCollection<IceCream> Flavors;

-

Use the following code:

-my Model:

 public class IceCream: INotifyPropertyChanged
{
    private int liczba;

    public int Liczba
    {
        get { return liczba; }
        set { liczba = value;
        Zmiana("Liczba");
        }
    }

    public IceCream(){}

//in the same class implement the below-it will be responsible for track a changes

    public event PropertyChangedEventHandler PropertyChanged;

    private void Zmiana(string propertyName) 
    {
        if (PropertyChanged != null) 
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

And in my class PersonList implement method responsible for active increasing the value of one after button click in AppBarControl

async private void Add_Click(object sender, RoutedEventArgs e)
    {
        List<IceCream> items = new List<IceCream>();
        foreach (IceCream item in IceCreamList.SelectedItems)
        {
            int i=Flavors.IndexOf(item);
            Flavors[i].Liczba =item.Liczba+ 1;
            //Flavors.Remove(item);

            //item.Liczba += 1;

           // items.Add(item);
           // Flavors.Add(item);
        }

        MessageDialog d = new MessageDialog("Zwiększono liczbę o jeden");
        d.Content = "Zwiększono liczbę o jeden";
        await d.ShowAsync();


        IceCreamList.SelectedIndex = -1;
    }
}

I hope that it will be useful for someone to
Note that:

private ObservableCollection<IceCream> Flavors;

-

焚却相思 2024-10-17 07:54:52

我发现针对此限制的最简单的解决方案是使用 RemoveAt(index) 删除项目,然后使用 InsertAt(index) 在同一索引上添加修改后的项目,从而ObservableCollection 将通知视图。

The easiest solution I found to this limitation, is to remove the item using RemoveAt(index) then add the modified item on the same index using InsertAt(index) and thus the ObservableCollection will notify the View.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文