Знайте, когда в ObservableCollections глубиной 3 уровня элемент изменяется, и какой - PullRequest
1 голос
/ 25 января 2020

Предисловие:

Вопрос здесь кажется мне довольно сложным. Я не знаю, возможно ли это. Пожалуйста, если вам интересно, прочитайте вводный текст, а затем код в предоставленной ссылке (вопрос на форуме по-французски, но код читабелен), обязательно поймите это, потому что я go довольно глубже, чем после. Проблема касается отслеживания изменений в иерархии классов моделей и широко использует события и делегатов. Приятного чтения!

Я создаю приложение, которое управляет супермаркетом, и многие клиенты получают доступ к акциям разных рынков (например, расположенных в разных местах).

Супермаркет содержит на многих рынках каждый рынок содержит (ObservableCollection) некоторые свойства (например, название) и ObservableCollection полок, каждая полка содержит (все еще один тип коллекции) некоторые продукты и некоторые свойства, и, наконец, каждый продукт содержит некоторые свойства.

Моя проблема в том, что когда кто-то меняет, например, TextBox, отображающий имя первого рынка, благодаря привязке данных, модель (первый рынок в супермаркете ObservableCollection) изменяется; и я хотел бы знать об изменении в проекте MVVM решения, и я хотел бы затем вызвать веб-сервис, который изменит базу данных, в которой хранится модель.

Поскольку у меня нет предыдущего значения модели при изменении TextBox, а затем при изменении модели, я не могу сравнить «старую» модель с «новой», чтобы увидеть различия. Вторая идея состоит в том, чтобы отслеживать изменения (каждый класс в модели реализует INotifyPropertyChanged), и для этого я нашел подсказку: см. этот пост на французском языке (код в ответе) , который показывает, как ObservableCollection может вызвать событие, когда один из его элементов изменен (и событие отслеживает измененный элемент). При условии, что элемент имеет уникальный идентификатор, я могу отслеживать измененные элементы, а затем вызывать веб-службу для изменения базы данных с изменением только элемента.

Но для этого мне нужно изменить модель ( заменив все ObservableCollections на MyObservableCollections), и я думаю, что это не рекомендуется, модель не должна содержать код.

Зная, что я использую MVVM (а проект MVVM не имеет ссылки на проект WPF), что является наилучшей практикой в ​​этом случае? Должен ли я изменить модель? Это очень сложно сделать?

спасибо

РЕДАКТИРОВАТЬ


Я закодировал вещь, но у меня есть некоторые проблемы.

Чтобы иметь Вот четкие идеи: 2 нижних уровня иерархии модели:

Продукты в каждом супермаркете:

[DataContract]
public class ProduitMagasin : ElementSupermarche
{
    [Key]
    [DataMember]
    public int ProduitMagasinId { set; get; }

    private string _Nom;
    [DataMember]
    public string Nom
    {
        set
        {
            if (value == _Nom) return;
            _Nom = value;
            OnPropertyChanged();
        }
        get => _Nom;
    }


    private int _Quantite;
    [DataMember]
    public int Quantite
    {
        set
        {
            if (value == _Quantite) return;
            _Quantite = value;
            OnPropertyChanged();
        }
        get => _Quantite;
    }




}

ничего особенного, за исключением того, что ElementSupermarche реализует INotifyPropertyChanged.

Эти продукты находятся в ObservableCollectionTrack, который является моей улучшенной версией MyObservableCollection в приведенной выше ссылке http.

здесь находится класс полок:

[DataContract]
public class Rayon : ElementSupermarche
{
    public delegate void ItemPropertyChangedHandler(object Sender, ObservableCollectionTrackPropertyChangedEventArgs e);
    public event ItemPropertyChangedHandler CollectionPropertyChanged;

    [Key]
    [DataMember]
    public int RayonId { set; get; }

    private string _Nom;
    [DataMember]
    public string Nom
    {
        set
        {
            if (value == _Nom) return;
            _Nom = value;
            OnPropertyChanged();
        }
        get => _Nom;
    }

    [DataMember]
    public virtual ObservableCollectionTrack<ProduitMagasin> Produits { set; get; }

    //voir si il est appelé par new Rayon(){params}
    public Rayon()
    {
        Produits.CollectionPropertyChanged += new ObservableCollectionTrack<ProduitMagasin>.ItemPropertyChangedHandler(TrackHandler);



    }

    private void TrackHandler(object Sender, ObservableCollectionTrackPropertyChangedEventArgs e)
    {
        CollectionPropertyChanged(Sender, e);
    }



}

Вот класс ObservableCollectionTrack:

public class ObservableCollectionTrack<T> : ObservableCollection<T>
    where T : INotifyPropertyChanged
{

    public delegate void ItemPropertyChangedHandler(object Sender, ObservableCollectionTrackPropertyChangedEventArgs e);
    public event ItemPropertyChangedHandler CollectionPropertyChanged;

    public ObservableCollectionTrack()
        : base()
    {
        base.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ObservableCOllectionTrack_CollectionChanged);
    }

    void ObservableCOllectionTrack_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
        {
            foreach (T item in e.OldItems)
            {
                //Removed items
                item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
                item.CollectionPropertyChanged += new ObservableCollectionTrack<ProduitMagasin>.ItemPropertyChangedHandler(TrackHandler);   <------------------------
            }
        }

        else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
        {
            foreach (T item in e.NewItems)
            {
                //Added items
                item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
                item.CollectionPropertyChanged += new ObservableCollectionTrack<ProduitMagasin>.ItemPropertyChangedHandler(TrackHandler);
            }
        }
    }

    void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (CollectionPropertyChanged != null)
        {
            CollectionPropertyChanged(this, new ObservableCollectionTrackPropertyChangedEventArgs(sender, e));
        }
    }

    private void TrackHandler(object Sender, ObservableCollectionTrackPropertyChangedEventArgs e)
    {
        CollectionPropertyChanged(Sender, e);
    }


}

//Arguments de l'évébement CollectionPropertyChanged de la classe ObservableCollectionTrack
public class ObservableCollectionTrackPropertyChangedEventArgs : PropertyChangedEventArgs
{
    public Object Item { get; set; }

    public ObservableCollectionTrackPropertyChangedEventArgs(Object Item, string propertyName)
        : base(propertyName)
    {
        this.Item = Item;
    }

    public ObservableCollectionTrackPropertyChangedEventArgs(Object Item, PropertyChangedEventArgs e)
        : base(e.PropertyName)
    {
        this.Item = Item;
    }


}

Идея состоит в том, что коллекция отслеживает события PropertyChanged, вызванные ее элементами, а затем вызывает CollectionPropertyChanged. Но есть проблема: пожалуйста, следуйте за мной по потоку: продукт изменился; он вызывает PropertyChanged, который вызывает в переменной Produits событие CollectionPropertyChanged; Затем экземпляр Rayon инициирует событие CollectionPropertyChanged (оно повторно отправляет то же самое событие), но экземпляр Rayon находится в самой коллекции ObservableCollectionTrack, поэтому делается вывод, что для повторного повторного вызова событие CollectionPropertyChanged должно быть подписано самой ObservableCollectionTrack!

Отсюда и строка со стрелкой в ​​ObservableCollectionTrack (приведена выше). Но, учитывая это, код не работает: элемент не объявляет обработку ObservableCollectionTrack. и если я пытаюсь добавить, что T должен наследовать от ObservableCollectionTrack, я получаю это:

strange problem

Он не может преобразовать переменную в ее правильный тип! Это связано с рекурсивностью? Нужно ли преобразовывать ObservableCollectionTrack в интерфейс, более или менее как INotifyPropertyChanged? Можно ли прогрессировать и достичь этой функциональности (поскольку все почти закодировано)?

спасибо.

...