Форум программистов, компьютерный форум, киберфорум
Наши страницы
skilllab
Войти
Регистрация
Восстановить пароль
Оценить эту запись

Изменяемая коллекция для WPF с уведомлением об изменении отдельных айтемов.

Запись от skilllab размещена 13.02.2018 в 11:28

Коллекция, которая предлагается как стандартная для использования в WPF проектах, имеет один существенный недостаток: в случае использования ссылочных типов и изменения отдельно взятой сущности, подписчики не уведомляются об изменении. Т.е. если имеем

C#
1
2
3
4
5
6
7
8
9
10
11
public class User : INotifyPropertyChanged
{
    public string Name{get; set;}
    public int Age {get; set;}
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName]string prop = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
    }
}
коллекцию
C#
1
ObservableCollection<User> Users {get;set;};
то при изменении свойства любого айтема в коллекции никто не будет оповещён, ибо коллекция фактически не изменилась. Чтобы добавить уведомление для подписчиков об изменении свойств отдельных айтемов, предлагаю такую реализацию, нарытую из гугла
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
[DebuggerStepThrough]
[Serializable]
public class FullyObservableCollection<T> : ObservableCollection<T>
    where T : INotifyPropertyChanged
{
    public FullyObservableCollection() { }
 
    public FullyObservableCollection(List<T> list) : base(list)
    {
        ObserveAll();
    }
 
    public FullyObservableCollection(IEnumerable<T> enumerable) : base(enumerable)
    {
        ObserveAll();
    }
 
    /// <summary> Событие изменения свойства </summary>
    public event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
 
    protected override void ClearItems()
    {
        foreach (var item in Items)
        {
            item.PropertyChanged -= ChildPropertyChanged;
        }
 
        base.ClearItems();
    }
 
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Replace)
        {
            foreach (T item in e.OldItems)
            {
                item.PropertyChanged -= ChildPropertyChanged;
            }
        }
 
        if (e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Replace)
        {
            foreach (T item in e.NewItems)
            {
                item.PropertyChanged += ChildPropertyChanged;
            }
        }
 
        base.OnCollectionChanged(e);
    }
 
    protected void OnItemPropertyChanged(ItemPropertyChangedEventArgs e)
    {
        ItemPropertyChanged?.Invoke(this, e);
    }
 
    protected void OnItemPropertyChanged(int index, PropertyChangedEventArgs e)
    {
        OnItemPropertyChanged(new ItemPropertyChangedEventArgs(index, e));
    }
 
    private void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var typedSender = (T)sender;
        var i = Items.IndexOf(typedSender);
 
        if (i < 0)
        {
            throw new ArgumentException("Received property notification from item not in collection");
        }
 
        OnItemPropertyChanged(i, e);
    }
 
    private void ObserveAll()
    {
        foreach (var item in Items)
        {
            item.PropertyChanged += ChildPropertyChanged;
        }
    }
}
 
/// <summary>
///     Аргументы с параметрами для события <see cref="FullyObservableCollection{T}.ItemPropertyChanged" />.
/// </summary>
public class ItemPropertyChangedEventArgs : PropertyChangedEventArgs
{
    /// <summary>
    ///     Конструктор для создания класса <see cref="ItemPropertyChangedEventArgs" />.
    /// </summary>
    /// <param name="index">Индекс в коллекции для изменения.</param>
    /// <param name="name">Имя свойства, которое изменилось.</param>
    public ItemPropertyChangedEventArgs(int index, string name) : base(name)
    {
        CollectionIndex = index;
    }
 
    /// <summary>
    ///     Уонструктор для создания класса <see cref="ItemPropertyChangedEventArgs" />.
    /// </summary>
    /// <param name="index">Индекс.</param>
    /// <param name="args">Экземпляр <see cref="PropertyChangedEventArgs" /> содержащий измененные данные.</param>
    public ItemPropertyChangedEventArgs(int index, PropertyChangedEventArgs args) : this(index, args.PropertyName) { }
 
    /// <summary>
    ///     Индекс в коллекции, где произошло изменнеие.
    /// </summary>
    /// <value>
    ///     Индекс в родительской коллекции.
    /// </value>
    public int CollectionIndex { get; }
}
Теперь, создав коллекцию
C#
1
FullyObservableCollection<User> Users {get;set;};
и, к примеру, изменив у любого User свойство Name - все подписчики будут уведомлены и View обновится.
Размещено в Без категории
Просмотров 367 Комментарии 2
Всего комментариев 2
Комментарии
  1. Старый комментарий
    То есть я верно понимаю, теперь в конструкторе ViewModel подписавшись на событие скажем
    C#
    1
    2
    3
    4
    5
    
    UserManagerData.ItemPropertyChanged += ItemProperty;
            private void ItemProperty(object sender, ItemPropertyChangedEventArgs e)
            {
                
            }
    я могу узнать индекс и свойство которое было изменено, верно?
    Запись от LEX38RUS размещена 02.04.2018 в 16:18 LEX38RUS вне форума
  2. Старый комментарий
    Аватар для skilllab
    Конечно. Если в UserManagerData есть свойство ID, то можете узнать. А имя изменённого свойства у вас будет видно в параметре e
    C#
    1
    2
    3
    4
    5
    6
    7
    
    private void ItemProperty(object sender, ItemPropertyChangedEventArgs e)
    {
        if (e.PropertyName == "Age")
        {
            //Do something
        }
    }
    Запись от skilllab размещена 04.04.2018 в 10:25 skilllab вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru