ObservableCollection
プロパティの1つにCollectionChanged eventhandler(onCollectionChanged)
を追加しました。
onCollectionChanged
メソッドは、コレクションにアイテムを追加または削除する場合にのみ呼び出されるが、コレクションアイテムが編集される場合には呼び出されないことがわかりました。
新しく追加、削除、編集されたアイテムのリスト/コレクションを単一のコレクションに送信する方法を知りたい。
ありがとう。
監視可能なリスト内のオブジェクトの編集に関する通知を取得するには、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にあるかどうかをチェックする必要があります(リストのメソッドContains(object obj)で)、そのメソッドの結果がfalse。
クラスItem
はINotifyPropertyChanged
を実装する必要があります。方法については、この 例 を参照してください。ロバート・ロスニーが言ったように、あなたはIEditableObject
でそれを作ることもできます-その要件があれば。
ItemsControl
は、CollectionChanged
をリッスンして、画面に表示するアイテムのコレクションの表示を管理します。 ContentControl
はPropertyChanged
をリッスンして、画面に表示されている特定のアイテムの表示を管理します。これを理解すれば、2つの概念を頭の中で別々に保つのは非常に簡単です。
アイテムが編集されているかどうかを追跡することは、これらのインターフェースのいずれでもありません。プロパティの変更は編集ではありません。つまり、オブジェクトの状態に対するユーザーが開始した何らかの種類の変更を必ずしも表しているわけではありません。たとえば、オブジェクトには、タイマーによって継続的に更新されるElapsedTime
プロパティがあります。 UIにはこれらのプロパティ変更イベントを通知する必要がありますが、オブジェクトの基になるデータの変更を表すものではありません。
オブジェクトが編集されているかどうかを追跡する標準的な方法は、最初にそのオブジェクトにIEditableObject
を実装させることです。その後、オブジェクトのクラスの内部で、どの変更が編集を構成するか(つまり、BeginEdit
を呼び出す必要がある)および変更しない編集を決定できます。その後、IsDirty
が呼び出されたときに設定され、BeginEdit
またはEndEdit
が呼び出されたときにクリアされるブールCancelEdit
プロパティを実装できます。 (そのプロパティがIEditableObject
の一部ではない理由を本当に理解していません。それを必要としない編集可能なオブジェクトをまだ実装していません。)
もちろん、必要ない場合はその第2レベルの抽象化を実装する必要はありません。PropertyChanged
イベントを確実にリッスンし、オブジェクトが発生した場合にオブジェクトが編集されたと仮定することができます。それは本当にあなたの要件に依存します。
' 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
}
INotifyCollectionChanged
は、INotiftyPropertyChanged
と同じものではありません。基礎となるオブジェクトのプロパティを変更しても、コレクションが変更されたことを示唆するものではありません。
この動作を実現する1つの方法は、追加されたオブジェクトに問い合わせてINotifyPropertyChanged.PropertyChanged
イベントに登録するカスタムコレクションを作成することです。その後、アイテムが削除されたときに適切に登録解除する必要があります。
このアプローチの1つの注意点は、オブジェクトが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));
}
}
I 思考ObservableCollection
にINotifyPropertyChanged
を実装するアイテムを追加すると、アイテムがCollectionChanged
通知を発生させたときにPropertyChanged
イベントが発生します。
考え直して、_BindingList<T>
_を使用して、個々のアイテムの変更をすぐにこの方法で伝播させる必要があると思います。
それ以外の場合は、各アイテムの変更通知を手動でサブスクライブし、CollectionChanged
イベントを発生させる必要があります。独自の派生_ObservableCollection<T>
_を作成する場合は、インスタンス化時とAdd()
およびInsert()
でサブスクライブし、Remove()
、RemoveAt()
およびClear()
。それ以外の場合は、CollectionChanged
イベントをサブスクライブし、イベント引数から追加および削除されたアイテムを使用してサブスクライブ/サブスクライブ解除できます。
この制限に対する最も簡単な解決策は、RemoveAt(index)
を使用してアイテムを削除し、InsertAt(index)
を使用して同じインデックスに変更されたアイテムを追加すると、ObservableCollectionがビューに通知することです。
次のコードを使用します。
-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でボタンがクリックされた後に、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;
-
winformsでは、BindingList
が標準的な方法です。 WPF&Silverlightでは、通常ObservableCollection
で作業を続けることができず、各アイテムでPropertyChanged
をリッスンする必要があります