我已将CollectionChanged eventhandler(onCollectionChanged)
添加到ObservableCollection
属性之一。
我发现仅在添加项目或将项目移除到集合的情况下才会调用onCollectionChanged
方法,但是在收集项目被编辑的情况下则不会。
我想知道如何在一个集合中发送新添加,删除和编辑的项目的列表/集合。
感谢。
答案 0 :(得分:45)
您必须为每个项目(必须实现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的方法包含(object obj))并且只有在该方法的结果为 false时才添加新项目强> 的
班级Item
必须实施INotifyPropertyChanged
。请参阅此example以了解具体方法。正如罗伯特罗斯尼所说,如果你有这个要求,你也可以用IEditableObject
来做到这一点。
答案 1 :(得分:10)
ItemsControl
侦听CollectionChanged
以管理其在屏幕上显示的项目集合的显示。 ContentControl
会监听PropertyChanged
以管理其在屏幕上显示的特定项目的显示。一旦你理解了这两个概念,就很容易将这两个概念分开。
跟踪某个项是否被编辑不是这些接口中的任何一个。属性更改不是编辑 - 也就是说,它们不一定表示某种用户启动的对象状态更改。例如,一个对象可能具有ElapsedTime
属性,该属性由计时器不断更新;需要通知UI这些属性更改事件,但它们肯定不代表对象基础数据的更改。
跟踪对象是否被编辑的标准方法是首先使该对象实现IEditableObject
。然后,您可以在对象的类内部,决定哪些更改构成编辑(即要求您调用BeginEdit
),哪些更改不会。然后,您可以实现一个布尔IsDirty
属性,该属性在调用BeginEdit
时设置,并在调用EndEdit
或CancelEdit
时清除。 (我真的不明白为什么该属性不属于IEditableObject
;我还没有实现一个不需要它的可编辑对象。)
当然,如果你不需要它,就没有必要实现第二级抽象 - 你当然可以监听PropertyChanged
事件,并假设对象在被引发时已被编辑。这实际上取决于您的要求。
答案 2 :(得分:4)
我对'this answer'的修改被拒绝了! 所以我把我的编辑放在这里:
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
}
答案 3 :(得分:2)
INotifyCollectionChanged
与INotiftyPropertyChanged
不同。更改基础对象的属性不会以任何方式表明集合已更改。
实现此行为的一种方法是创建一个自定义集合,该集合将在添加后询问对象并注册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));
}
}
答案 4 :(得分:1)
我认为使用ObservableCollection
项填充INotifyPropertyChanged
会导致CollectionChanged
事件在项目提升PropertyChanged
时触发通知。
第二个想法,我认为您需要使用BindingList<T>
来使单个项目更改以这种方式传播开箱即用。
否则,您需要手动订阅每个项目的更改通知并引发CollectionChanged
事件。请注意,如果您要创建自己的派生ObservableCollection<T>
,则必须在实例化和Add()
和Insert()
订阅,并取消订阅Remove()
,{{ 1}}和RemoveAt()
。否则,您可以订阅Clear()
事件并使用事件参数中添加和删除的项目来订阅/取消订阅。
答案 5 :(得分:0)
在winforms中,BindingList
是标准做法。在WPF&amp; Silverlight,您通常无法使用ObservableCollection
并且需要在每个项目上收听PropertyChanged
答案 6 :(得分:0)
使用以下代码:
-my型号:
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中单击按钮后激活一个值
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;
-
答案 7 :(得分:0)
我发现此限制的最简单的解决方案是使用RemoveAt(index)
删除项目,然后使用InsertAt(index)
在同一索引上添加修改的项目,因此ObservableCollection将通知视图。