1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.Collections.ObjectModel;
|
---|
4 | using System.Collections.Specialized;
|
---|
5 | using System.Windows;
|
---|
6 | using System.Windows.Threading;
|
---|
7 |
|
---|
8 | namespace Microsoft.Research.DynamicDataDisplay.DataSources
|
---|
9 | {
|
---|
10 | // todo I don't think that we should create data source which supports
|
---|
11 | // suspending its DataChanged event - it is better to create
|
---|
12 | // collection with the same functionality - then it would be able to be used
|
---|
13 | // as a source in many data sources.
|
---|
14 | public class ObservableDataSource<T> : IPointDataSource
|
---|
15 | {
|
---|
16 |
|
---|
17 | /// <summary>True if collection was changed between SuspendUpdate and ResumeUpdate
|
---|
18 | /// or false otherwise</summary>
|
---|
19 | private bool collectionChanged = false;
|
---|
20 |
|
---|
21 | /// <summary>True if event should be raised on each collection change
|
---|
22 | /// or false otherwise</summary>
|
---|
23 | private bool updatesEnabled = true;
|
---|
24 |
|
---|
25 | public ObservableDataSource()
|
---|
26 | {
|
---|
27 | collection.CollectionChanged += OnCollectionChanged;
|
---|
28 |
|
---|
29 | // todo this is hack
|
---|
30 | if (typeof(T) == typeof(Point))
|
---|
31 | {
|
---|
32 | xyMapping = t => (Point)(object)t;
|
---|
33 | }
|
---|
34 | }
|
---|
35 |
|
---|
36 | public ObservableDataSource(IEnumerable<T> data)
|
---|
37 | : this()
|
---|
38 | {
|
---|
39 | if (data == null)
|
---|
40 | throw new ArgumentNullException("data");
|
---|
41 |
|
---|
42 | foreach (T item in data)
|
---|
43 | {
|
---|
44 | collection.Add(item);
|
---|
45 | }
|
---|
46 | }
|
---|
47 |
|
---|
48 | public void SuspendUpdate()
|
---|
49 | {
|
---|
50 | updatesEnabled = false;
|
---|
51 | }
|
---|
52 |
|
---|
53 | public void ResumeUpdate()
|
---|
54 | {
|
---|
55 | updatesEnabled = true;
|
---|
56 | if (collectionChanged)
|
---|
57 | {
|
---|
58 | collectionChanged = false;
|
---|
59 | RaiseDataChanged();
|
---|
60 | }
|
---|
61 | }
|
---|
62 |
|
---|
63 | private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
---|
64 | {
|
---|
65 | if (updatesEnabled)
|
---|
66 | {
|
---|
67 | RaiseDataChanged();
|
---|
68 | }
|
---|
69 | else
|
---|
70 | {
|
---|
71 | collectionChanged = true;
|
---|
72 | }
|
---|
73 | }
|
---|
74 |
|
---|
75 | private readonly ObservableCollection<T> collection = new ObservableCollection<T>();
|
---|
76 |
|
---|
77 | public ObservableCollection<T> Collection
|
---|
78 | {
|
---|
79 | get { return collection; }
|
---|
80 | }
|
---|
81 |
|
---|
82 | public void AppendMany(IEnumerable<T> data)
|
---|
83 | {
|
---|
84 | if (data == null)
|
---|
85 | throw new ArgumentNullException("data");
|
---|
86 |
|
---|
87 | updatesEnabled = false;
|
---|
88 | foreach (var p in data)
|
---|
89 | {
|
---|
90 | collection.Add(p);
|
---|
91 | }
|
---|
92 | updatesEnabled = true;
|
---|
93 | RaiseDataChanged();
|
---|
94 | }
|
---|
95 |
|
---|
96 | public void AppendAsync(Dispatcher dispatcher, T item)
|
---|
97 | {
|
---|
98 | dispatcher.Invoke(DispatcherPriority.Normal,
|
---|
99 | new Action(() =>
|
---|
100 | {
|
---|
101 | collection.Add(item);
|
---|
102 | RaiseDataChanged();
|
---|
103 | }));
|
---|
104 | }
|
---|
105 |
|
---|
106 | private readonly List<Mapping<T>> mappings = new List<Mapping<T>>();
|
---|
107 | private Func<T, double> xMapping;
|
---|
108 | private Func<T, double> yMapping;
|
---|
109 | private Func<T, Point> xyMapping;
|
---|
110 |
|
---|
111 | public void SetXMapping(Func<T, double> mapping)
|
---|
112 | {
|
---|
113 | if (mapping == null)
|
---|
114 | throw new ArgumentNullException("mapping");
|
---|
115 |
|
---|
116 | this.xMapping = mapping;
|
---|
117 | }
|
---|
118 |
|
---|
119 | public void SetYMapping(Func<T, double> mapping)
|
---|
120 | {
|
---|
121 | if (mapping == null)
|
---|
122 | throw new ArgumentNullException("mapping");
|
---|
123 |
|
---|
124 | this.yMapping = mapping;
|
---|
125 | }
|
---|
126 |
|
---|
127 | public void SetXYMapping(Func<T, Point> mapping)
|
---|
128 | {
|
---|
129 | if (mapping == null)
|
---|
130 | throw new ArgumentNullException("mapping");
|
---|
131 |
|
---|
132 | this.xyMapping = mapping;
|
---|
133 | }
|
---|
134 |
|
---|
135 | #region IChartDataSource Members
|
---|
136 |
|
---|
137 | private class ObservableIterator : IPointEnumerator
|
---|
138 | {
|
---|
139 | private readonly ObservableDataSource<T> dataSource;
|
---|
140 | private readonly IEnumerator<T> enumerator;
|
---|
141 |
|
---|
142 | public ObservableIterator(ObservableDataSource<T> dataSource)
|
---|
143 | {
|
---|
144 | this.dataSource = dataSource;
|
---|
145 | enumerator = dataSource.collection.GetEnumerator();
|
---|
146 | }
|
---|
147 |
|
---|
148 | #region IChartPointEnumerator Members
|
---|
149 |
|
---|
150 | public bool MoveNext()
|
---|
151 | {
|
---|
152 | return enumerator.MoveNext();
|
---|
153 | }
|
---|
154 |
|
---|
155 | public void GetCurrent(ref Point p)
|
---|
156 | {
|
---|
157 | dataSource.FillPoint(enumerator.Current, ref p);
|
---|
158 | }
|
---|
159 |
|
---|
160 | public void ApplyMappings(DependencyObject target)
|
---|
161 | {
|
---|
162 | dataSource.ApplyMappings(target, enumerator.Current);
|
---|
163 | }
|
---|
164 |
|
---|
165 | public void Dispose()
|
---|
166 | {
|
---|
167 | enumerator.Dispose();
|
---|
168 | GC.SuppressFinalize(this);
|
---|
169 | }
|
---|
170 |
|
---|
171 | #endregion
|
---|
172 | }
|
---|
173 |
|
---|
174 | private void FillPoint(T elem, ref Point point)
|
---|
175 | {
|
---|
176 | if (xyMapping != null)
|
---|
177 | {
|
---|
178 | point = xyMapping(elem);
|
---|
179 | }
|
---|
180 | else
|
---|
181 | {
|
---|
182 | if (xMapping != null)
|
---|
183 | {
|
---|
184 | point.X = xMapping(elem);
|
---|
185 | }
|
---|
186 | if (yMapping != null)
|
---|
187 | {
|
---|
188 | point.Y = yMapping(elem);
|
---|
189 | }
|
---|
190 | }
|
---|
191 | }
|
---|
192 |
|
---|
193 | private void ApplyMappings(DependencyObject target, T elem)
|
---|
194 | {
|
---|
195 | if (target != null)
|
---|
196 | {
|
---|
197 | foreach (var mapping in mappings)
|
---|
198 | {
|
---|
199 | target.SetValue(mapping.Property, mapping.F(elem));
|
---|
200 | }
|
---|
201 | }
|
---|
202 | }
|
---|
203 |
|
---|
204 | public IPointEnumerator GetEnumerator(DependencyObject context)
|
---|
205 | {
|
---|
206 | return new ObservableIterator(this);
|
---|
207 | }
|
---|
208 |
|
---|
209 | public event EventHandler DataChanged;
|
---|
210 | private void RaiseDataChanged()
|
---|
211 | {
|
---|
212 | if (DataChanged != null)
|
---|
213 | {
|
---|
214 | DataChanged(this, EventArgs.Empty);
|
---|
215 | }
|
---|
216 | }
|
---|
217 |
|
---|
218 | #endregion
|
---|
219 | }
|
---|
220 | }
|
---|