web-dev-qa-db-ja.com

CollectionChangedの実装

ObservableCollectionプロパティの1つにCollectionChanged eventhandler(onCollectionChanged)を追加しました。

onCollectionChangedメソッドは、コレクションにアイテムを追加または削除する場合にのみ呼び出されるが、コレクションアイテムが編集される場合には呼び出されないことがわかりました。

新しく追加、削除、編集されたアイテムのリスト/コレクションを単一のコレクションに送信する方法を知りたい。

ありがとう。

23
WhoIsNinja

監視可能なリスト内のオブジェクトの編集に関する通知を取得するには、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

クラスItemINotifyPropertyChangedを実装する必要があります。方法については、この を参照してください。ロバート・ロスニーが言ったように、あなたはIEditableObjectでそれを作ることもできます-その要件があれば。

46
Arxisos

ItemsControlは、CollectionChangedをリッスンして、画面に表示するアイテムのコレクションの表示を管理します。 ContentControlPropertyChangedをリッスンして、画面に表示されている特定のアイテムの表示を管理します。これを理解すれば、2つの概念を頭の中で別々に保つのは非常に簡単です。

アイテムが編集されているかどうかを追跡することは、これらのインターフェースのいずれでもありません。プロパティの変更は編集ではありません。つまり、オブジェクトの状態に対するユーザーが開始した何らかの種類の変更を必ずしも表しているわけではありません。たとえば、オブジェクトには、タイマーによって継続的に更新されるElapsedTimeプロパティがあります。 UIにはこれらのプロパティ変更イベントを通知する必要がありますが、オブジェクトの基になるデータの変更を表すものではありません。

オブジェクトが編集されているかどうかを追跡する標準的な方法は、最初にそのオブジェクトにIEditableObjectを実装させることです。その後、オブジェクトのクラスの内部で、どの変更が編集を構成するか(つまり、BeginEditを呼び出す必要がある)および変更しない編集を決定できます。その後、IsDirtyが呼び出されたときに設定され、BeginEditまたはEndEditが呼び出されたときにクリアされるブールCancelEditプロパティを実装できます。 (そのプロパティがIEditableObjectの一部ではない理由を本当に理解していません。それを必要としない編集可能なオブジェクトをまだ実装していません。)

もちろん、必要ない場合はその第2レベルの抽象化を実装する必要はありません。PropertyChangedイベントを確実にリッスンし、オブジェクトが発生した場合にオブジェクトが編集されたと仮定することができます。それは本当にあなたの要件に依存します。

10
Robert Rossney

' 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
}
4
Behzad Ebrahimi

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)); 
        }
    }
2
Aaron McIver

I 思考ObservableCollectionINotifyPropertyChangedを実装するアイテムを追加すると、アイテムがCollectionChanged通知を発生させたときにPropertyChangedイベントが発生します。

考え直して、_BindingList<T>_を使用して、個々のアイテムの変更をすぐにこの方法で伝播させる必要があると思います。

それ以外の場合は、各アイテムの変更通知を手動でサブスクライブし、CollectionChangedイベントを発生させる必要があります。独自の派生_ObservableCollection<T>_を作成する場合は、インスタンス化時とAdd()およびInsert()でサブスクライブし、Remove()RemoveAt()およびClear()。それ以外の場合は、CollectionChangedイベントをサブスクライブし、イベント引数から追加および削除されたアイテムを使用してサブスクライブ/サブスクライブ解除できます。

1
Jay

この制限に対する最も簡単な解決策は、RemoveAt(index)を使用してアイテムを削除し、InsertAt(index)を使用して同じインデックスに変更されたアイテムを追加すると、ObservableCollectionがビューに通知することです。

0
LightTechnician

次のコードを使用します。

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

-

0

winformsでは、BindingListが標準的な方法です。 WPF&Silverlightでは、通常ObservableCollectionで作業を続けることができず、各アイテムでPropertyChangedをリッスンする必要があります

0
Robert Levy