-
Notifications
You must be signed in to change notification settings - Fork 1
/
VmCollection.cs
171 lines (144 loc) · 5.88 KB
/
VmCollection.cs
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using Thingie.WPF.ViewModels;
namespace Thingie.WPF
{
/// <summary>
/// Observable collection of ViewModels that pushes changes to a related collection of models
/// </summary>
/// <typeparam name="TViewModel">Type of ViewModels in collection</typeparam>
/// <typeparam name="TModel">Type of models in underlying collection</typeparam>
public class VmCollection<TViewModel, TModel> : ObservableCollection<TViewModel>
where TViewModel : class
where TModel : class
{
private readonly ICollection<TModel> _models;
private bool _synchDisabled;
private readonly Func<TModel, TViewModel> _viewModelProvider;
/// <summary>
/// Constructor
/// </summary>
/// <param name="models">List of models to synch with</param>
/// <param name="viewModelProvider"></param>
/// <param name="context"></param>
/// <param name="autoFetch">
/// Determines whether the collection of ViewModels should be
/// fetched from the model collection on construction
/// </param>
public VmCollection(ICollection<TModel> models, Func<TModel, TViewModel> viewModelProvider, bool autoFetch = true)
{
_models = models;
_viewModelProvider = viewModelProvider;
// Register change handling for synchronization
// from ViewModels to Models
CollectionChanged += ViewModelCollectionChanged;
// If model collection is observable register change
// handling for synchronization from Models to ViewModels
if (models is ObservableCollection<TModel>)
{
var observableModels = models as ObservableCollection<TModel>;
observableModels.CollectionChanged += ModelCollectionChanged;
}
// Fecth ViewModels
if (autoFetch) FetchFromModels();
}
/// <summary>
/// CollectionChanged event of the ViewModelCollection
/// </summary>
public override sealed event NotifyCollectionChangedEventHandler CollectionChanged
{
add { base.CollectionChanged += value; }
remove { base.CollectionChanged -= value; }
}
/// <summary>
/// Load VM collection from model collection
/// </summary>
public void FetchFromModels()
{
// Deactivate change pushing
_synchDisabled = true;
// Clear collection
Clear();
// Create and add new VM for each model
foreach (var model in _models)
AddForModel(model);
// Reactivate change pushing
_synchDisabled = false;
}
private void ViewModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Return if synchronization is internally disabled
if (_synchDisabled) return;
// Disable synchronization
_synchDisabled = true;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var m in e.NewItems.OfType<VMBase<TModel>>().Select(v => v.Model).OfType<TModel>())
_models.Add(m);
break;
case NotifyCollectionChangedAction.Remove:
foreach (var m in e.OldItems.OfType<VMBase<TModel>>().Select(v => v.Model).OfType<TModel>())
_models.Remove(m);
break;
case NotifyCollectionChangedAction.Reset:
_models.Clear();
foreach (var m in e.NewItems.OfType<VMBase<TModel>>().Select(v => v.Model).OfType<TModel>())
_models.Add(m);
break;
}
//Enable synchronization
_synchDisabled = false;
}
private void ModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_synchDisabled) return;
_synchDisabled = true;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var m in e.NewItems.OfType<TModel>())
this.Add(CreateViewModel(m));
break;
case NotifyCollectionChangedAction.Remove:
foreach (var m in e.OldItems.OfType<TModel>())
this.Remove(GetViewModelOfModel(m));
break;
case NotifyCollectionChangedAction.Reset:
Clear();
FetchFromModels();
break;
}
_synchDisabled = false;
}
private TViewModel CreateViewModel(TModel model)
{
return _viewModelProvider(model);
}
private TViewModel GetViewModelOfModel(TModel model)
{
return Items.OfType<VMBase<TModel>>().FirstOrDefault(v => v.Model == model) as TViewModel;
}
/// <summary>
/// Adds a new ViewModel for the specified Model instance
/// </summary>
/// <param name="model">Model to create ViewModel for</param>
public void AddForModel(TModel model)
{
Add(CreateViewModel(model));
}
/// <summary>
/// Adds a new ViewModel with a new model instance of the specified type,
/// which is the ModelType or derived from the Model type
/// </summary>
/// <typeparam name="TSpecificModel">Type of Model to add ViewModel for</typeparam>
public void AddNew<TSpecificModel>() where TSpecificModel : TModel, new()
{
var m = new TSpecificModel();
Add(CreateViewModel(m));
}
}
}