1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.Linq;
|
---|
4 | using System.Text;
|
---|
5 | using System.Windows;
|
---|
6 | using System.Windows.Controls;
|
---|
7 | using System.Windows.Media;
|
---|
8 | using System.Windows.Data;
|
---|
9 | using System.Diagnostics;
|
---|
10 | using System.ComponentModel;
|
---|
11 | using Microsoft.Research.DynamicDataDisplay.Charts.Axes;
|
---|
12 | using Microsoft.Research.DynamicDataDisplay.Common;
|
---|
13 | using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
|
---|
14 | using System.Windows.Threading;
|
---|
15 |
|
---|
16 | namespace Microsoft.Research.DynamicDataDisplay.Charts
|
---|
17 | {
|
---|
18 | /// <summary>
|
---|
19 | /// Represents a base class for all axes in ChartPlotter.
|
---|
20 | /// Contains a real UI representation of axis - AxisControl, and means to adjust number of ticks, algorythms of their generating and
|
---|
21 | /// look of ticks' labels.
|
---|
22 | /// </summary>
|
---|
23 | /// <typeparam name="T">Type of each tick's value</typeparam>
|
---|
24 | public abstract class AxisBase<T> : GeneralAxis, ITypedAxis<T>, IValueConversion<T>
|
---|
25 | {
|
---|
26 | /// <summary>
|
---|
27 | /// Initializes a new instance of the <see cref="AxisBase<T>"/> class.
|
---|
28 | /// </summary>
|
---|
29 | /// <param name="axisControl">The axis control.</param>
|
---|
30 | /// <param name="convertFromDouble">The convert from double.</param>
|
---|
31 | /// <param name="convertToDouble">The convert to double.</param>
|
---|
32 | protected AxisBase(AxisControl<T> axisControl, Func<double, T> convertFromDouble, Func<T, double> convertToDouble)
|
---|
33 | {
|
---|
34 | if (axisControl == null)
|
---|
35 | throw new ArgumentNullException("axisControl");
|
---|
36 | if (convertFromDouble == null)
|
---|
37 | throw new ArgumentNullException("convertFromDouble");
|
---|
38 | if (convertToDouble == null)
|
---|
39 | throw new ArgumentNullException("convertToDouble");
|
---|
40 |
|
---|
41 | this.convertToDouble = convertToDouble;
|
---|
42 | this.convertFromDouble = convertFromDouble;
|
---|
43 |
|
---|
44 | this.axisControl = axisControl;
|
---|
45 | axisControl.MakeDependent();
|
---|
46 | axisControl.ConvertToDouble = convertToDouble;
|
---|
47 | axisControl.ScreenTicksChanged += axisControl_ScreenTicksChanged;
|
---|
48 |
|
---|
49 | Content = axisControl;
|
---|
50 | axisControl.SetBinding(Control.BackgroundProperty, new Binding("Background") { Source = this });
|
---|
51 |
|
---|
52 | Focusable = false;
|
---|
53 |
|
---|
54 | Loaded += OnLoaded;
|
---|
55 | }
|
---|
56 |
|
---|
57 | public override void ForceUpdate()
|
---|
58 | {
|
---|
59 | axisControl.UpdateUI();
|
---|
60 | }
|
---|
61 |
|
---|
62 | private void axisControl_ScreenTicksChanged(object sender, EventArgs e)
|
---|
63 | {
|
---|
64 | RaiseTicksChanged();
|
---|
65 | }
|
---|
66 |
|
---|
67 | /// <summary>
|
---|
68 | /// Gets or sets a value indicating whether this axis is default axis.
|
---|
69 | /// ChartPlotter's AxisGrid gets axis ticks to display from two default axes - horizontal and vertical.
|
---|
70 | /// </summary>
|
---|
71 | /// <value>
|
---|
72 | /// <c>true</c> if this instance is default axis; otherwise, <c>false</c>.
|
---|
73 | /// </value>
|
---|
74 | public bool IsDefaultAxis
|
---|
75 | {
|
---|
76 | get { return Microsoft.Research.DynamicDataDisplay.Plotter.GetIsDefaultAxis(this); }
|
---|
77 | set { Microsoft.Research.DynamicDataDisplay.Plotter.SetIsDefaultAxis(this, value); }
|
---|
78 | }
|
---|
79 |
|
---|
80 | private void OnLoaded(object sender, RoutedEventArgs e)
|
---|
81 | {
|
---|
82 | RaiseTicksChanged();
|
---|
83 | }
|
---|
84 |
|
---|
85 | /// <summary>
|
---|
86 | /// Gets or sets a value indicating whether to use smooth panning, when axis ticks are not being repainted after each chart panning.
|
---|
87 | /// </summary>
|
---|
88 | /// <value><c>true</c> if use smooth panning; otherwise, <c>false</c>.</value>
|
---|
89 | public override bool UseSmoothPanning
|
---|
90 | {
|
---|
91 | get { return axisControl.UseSmoothPanning; }
|
---|
92 | set { axisControl.UseSmoothPanning = value; }
|
---|
93 | }
|
---|
94 |
|
---|
95 | /// <summary>
|
---|
96 | /// Gets the screen coordinates of axis ticks.
|
---|
97 | /// </summary>
|
---|
98 | /// <value>The screen ticks.</value>
|
---|
99 | [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
---|
100 | [EditorBrowsable(EditorBrowsableState.Never)]
|
---|
101 | public override double[] ScreenTicks
|
---|
102 | {
|
---|
103 | get { return axisControl.ScreenTicks; }
|
---|
104 | }
|
---|
105 |
|
---|
106 | /// <summary>
|
---|
107 | /// Gets the screen coordinates of minor ticks.
|
---|
108 | /// </summary>
|
---|
109 | /// <value>The minor screen ticks.</value>
|
---|
110 | [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
---|
111 | [EditorBrowsable(EditorBrowsableState.Never)]
|
---|
112 | public override MinorTickInfo<double>[] MinorScreenTicks
|
---|
113 | {
|
---|
114 | get { return axisControl.MinorScreenTicks; }
|
---|
115 | }
|
---|
116 |
|
---|
117 | private AxisControl<T> axisControl;
|
---|
118 | /// <summary>
|
---|
119 | /// Gets the axis control - actual UI representation of axis.
|
---|
120 | /// </summary>
|
---|
121 | /// <value>The axis control.</value>
|
---|
122 | [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
---|
123 | public AxisControl<T> AxisControl
|
---|
124 | {
|
---|
125 | get { return axisControl; }
|
---|
126 | }
|
---|
127 |
|
---|
128 | /// <summary>
|
---|
129 | /// Gets or sets the ticks provider, which is used to generate ticks in given range.
|
---|
130 | /// </summary>
|
---|
131 | /// <value>The ticks provider.</value>
|
---|
132 | [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
---|
133 | public ITicksProvider<T> TicksProvider
|
---|
134 | {
|
---|
135 | get { return axisControl.TicksProvider; }
|
---|
136 | set { axisControl.TicksProvider = value; }
|
---|
137 | }
|
---|
138 |
|
---|
139 | /// <summary>
|
---|
140 | /// Gets or sets the label provider, that is used to create UI look of axis ticks.
|
---|
141 | ///
|
---|
142 | /// Should not be null.
|
---|
143 | /// </summary>
|
---|
144 | /// <value>The label provider.</value>
|
---|
145 | [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
---|
146 | [NotNull]
|
---|
147 | public LabelProviderBase<T> LabelProvider
|
---|
148 | {
|
---|
149 | get { return axisControl.LabelProvider; }
|
---|
150 | set { axisControl.LabelProvider = value; }
|
---|
151 | }
|
---|
152 |
|
---|
153 | /// <summary>
|
---|
154 | /// Gets or sets the major label provider, which creates labels for major ticks.
|
---|
155 | /// If null, major labels will not be shown.
|
---|
156 | /// </summary>
|
---|
157 | /// <value>The major label provider.</value>
|
---|
158 | public LabelProviderBase<T> MajorLabelProvider
|
---|
159 | {
|
---|
160 | get { return axisControl.MajorLabelProvider; }
|
---|
161 | set { axisControl.MajorLabelProvider = value; }
|
---|
162 | }
|
---|
163 |
|
---|
164 | /// <summary>
|
---|
165 | /// Gets or sets the label string format, used to create simple formats of each tick's label, such as
|
---|
166 | /// changing tick label from "1.2" to "$1.2".
|
---|
167 | /// Should be in format "*{0}*", where '*' is any number of any chars.
|
---|
168 | ///
|
---|
169 | /// If value is null, format string will not be used.
|
---|
170 | /// </summary>
|
---|
171 | /// <value>The label string format.</value>
|
---|
172 | public string LabelStringFormat
|
---|
173 | {
|
---|
174 | get { return LabelProvider.LabelStringFormat; }
|
---|
175 | set { LabelProvider.LabelStringFormat = value; }
|
---|
176 | }
|
---|
177 |
|
---|
178 | /// <summary>
|
---|
179 | /// Gets or sets a value indicating whether to show minor ticks.
|
---|
180 | /// </summary>
|
---|
181 | /// <value><c>true</c> if show minor ticks; otherwise, <c>false</c>.</value>
|
---|
182 | public bool ShowMinorTicks
|
---|
183 | {
|
---|
184 | get { return axisControl.DrawMinorTicks; }
|
---|
185 | set { axisControl.DrawMinorTicks = value; }
|
---|
186 | }
|
---|
187 |
|
---|
188 | /// <summary>
|
---|
189 | /// Gets or sets a value indicating whether to show major labels.
|
---|
190 | /// </summary>
|
---|
191 | /// <value><c>true</c> if show major labels; otherwise, <c>false</c>.</value>
|
---|
192 | public bool ShowMajorLabels
|
---|
193 | {
|
---|
194 | get { return axisControl.DrawMajorLabels; }
|
---|
195 | set { axisControl.DrawMajorLabels = value; }
|
---|
196 | }
|
---|
197 |
|
---|
198 | protected override void OnPlotterAttached(Plotter2D plotter)
|
---|
199 | {
|
---|
200 | plotter.Viewport.PropertyChanged += OnViewportPropertyChanged;
|
---|
201 |
|
---|
202 | Panel panel = GetPanelByPlacement(Placement);
|
---|
203 | if (panel != null)
|
---|
204 | {
|
---|
205 | int index = GetInsertionIndexByPlacement(Placement, panel);
|
---|
206 | panel.Children.Insert(index, this);
|
---|
207 | }
|
---|
208 |
|
---|
209 | using (axisControl.OpenUpdateRegion(true))
|
---|
210 | {
|
---|
211 | UpdateAxisControl(plotter);
|
---|
212 | }
|
---|
213 | }
|
---|
214 |
|
---|
215 | private void UpdateAxisControl(Plotter2D plotter2d)
|
---|
216 | {
|
---|
217 | axisControl.Transform = plotter2d.Viewport.Transform;
|
---|
218 | axisControl.Range = CreateRangeFromRect(plotter2d.Visible.ViewportToData(plotter2d.Viewport.Transform));
|
---|
219 | }
|
---|
220 |
|
---|
221 | private int GetInsertionIndexByPlacement(AxisPlacement placement, Panel panel)
|
---|
222 | {
|
---|
223 | int index = panel.Children.Count;
|
---|
224 |
|
---|
225 | switch (placement)
|
---|
226 | {
|
---|
227 | case AxisPlacement.Left:
|
---|
228 | index = 0;
|
---|
229 | break;
|
---|
230 | case AxisPlacement.Top:
|
---|
231 | index = 0;
|
---|
232 | break;
|
---|
233 | default:
|
---|
234 | break;
|
---|
235 | }
|
---|
236 |
|
---|
237 | return index;
|
---|
238 | }
|
---|
239 |
|
---|
240 | ExtendedPropertyChangedEventArgs visibleChangedEventArgs;
|
---|
241 | int viewportPropertyChangedEnters = 0;
|
---|
242 | DataRect prevDataRect = DataRect.Empty;
|
---|
243 | private void OnViewportPropertyChanged(object sender, ExtendedPropertyChangedEventArgs e)
|
---|
244 | {
|
---|
245 | //if (viewportPropertyChangedEnters > 4)
|
---|
246 | //{
|
---|
247 | // if (e.PropertyName == "Visible")
|
---|
248 | // {
|
---|
249 | // visibleChangedEventArgs = e;
|
---|
250 | // }
|
---|
251 | // return;
|
---|
252 | //}
|
---|
253 |
|
---|
254 | viewportPropertyChangedEnters++;
|
---|
255 |
|
---|
256 | Viewport2D viewport = (Viewport2D)sender;
|
---|
257 |
|
---|
258 | DataRect visible = viewport.Visible;
|
---|
259 |
|
---|
260 | DataRect dataRect = visible.ViewportToData(viewport.Transform);
|
---|
261 | bool forceUpdate = dataRect != prevDataRect;
|
---|
262 | prevDataRect = dataRect;
|
---|
263 |
|
---|
264 | Range<T> range = CreateRangeFromRect(dataRect);
|
---|
265 |
|
---|
266 | using (axisControl.OpenUpdateRegion(false)) // todo was forceUpdate
|
---|
267 | {
|
---|
268 | axisControl.Range = range;
|
---|
269 | axisControl.Transform = viewport.Transform;
|
---|
270 | }
|
---|
271 |
|
---|
272 | //OnViewportPropertyChanged(Plotter.Viewport, visibleChangedEventArgs);
|
---|
273 |
|
---|
274 | //Dispatcher.BeginInvoke(() =>
|
---|
275 | //{
|
---|
276 | // viewportPropertyChangedEnters--;
|
---|
277 | // if (visibleChangedEventArgs != null)
|
---|
278 | // {
|
---|
279 | // OnViewportPropertyChanged(Plotter.Viewport, visibleChangedEventArgs);
|
---|
280 | // }
|
---|
281 | // visibleChangedEventArgs = null;
|
---|
282 | //}, DispatcherPriority.Render);
|
---|
283 | }
|
---|
284 |
|
---|
285 | private Func<double, T> convertFromDouble;
|
---|
286 | /// <summary>
|
---|
287 | /// Gets or sets the delegate that is used to create each tick from double.
|
---|
288 | /// Is used to create typed range to display for internal AxisControl.
|
---|
289 | /// If changed, ConvertToDouble should be changed appropriately, too.
|
---|
290 | /// Should not be null.
|
---|
291 | /// </summary>
|
---|
292 | /// <value>The convert from double.</value>
|
---|
293 | [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
---|
294 | [NotNull]
|
---|
295 | public Func<double, T> ConvertFromDouble
|
---|
296 | {
|
---|
297 | get { return convertFromDouble; }
|
---|
298 | set
|
---|
299 | {
|
---|
300 | if (value == null)
|
---|
301 | throw new ArgumentNullException("value");
|
---|
302 |
|
---|
303 | if (convertFromDouble != value)
|
---|
304 | {
|
---|
305 | convertFromDouble = value;
|
---|
306 | if (ParentPlotter != null)
|
---|
307 | {
|
---|
308 | UpdateAxisControl(ParentPlotter);
|
---|
309 | }
|
---|
310 | }
|
---|
311 | }
|
---|
312 | }
|
---|
313 |
|
---|
314 | private Func<T, double> convertToDouble;
|
---|
315 | /// <summary>
|
---|
316 | /// Gets or sets the delegate that is used to convert each tick to double.
|
---|
317 | /// Is used by internal AxisControl to convert tick to double to get tick's coordinates inside of viewport.
|
---|
318 | /// If changed, ConvertFromDouble should be changed appropriately, too.
|
---|
319 | /// Should not be null.
|
---|
320 | /// </summary>
|
---|
321 | /// <value>The convert to double.</value>
|
---|
322 | [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
---|
323 | [NotNull]
|
---|
324 | public Func<T, double> ConvertToDouble
|
---|
325 | {
|
---|
326 | get { return convertToDouble; }
|
---|
327 | set
|
---|
328 | {
|
---|
329 | if (value == null)
|
---|
330 | throw new ArgumentNullException("value");
|
---|
331 |
|
---|
332 | if (convertToDouble != value)
|
---|
333 | {
|
---|
334 | convertToDouble = value;
|
---|
335 | axisControl.ConvertToDouble = value;
|
---|
336 | }
|
---|
337 | }
|
---|
338 | }
|
---|
339 |
|
---|
340 | /// <summary>
|
---|
341 | /// Sets conversions of axis - functions used to convert values of axis type to and from double values of viewport.
|
---|
342 | /// Sets both ConvertToDouble and ConvertFromDouble properties.
|
---|
343 | /// </summary>
|
---|
344 | /// <param name="min">The minimal viewport value.</param>
|
---|
345 | /// <param name="minValue">The value of axis type, corresponding to minimal viewport value.</param>
|
---|
346 | /// <param name="max">The maximal viewport value.</param>
|
---|
347 | /// <param name="maxValue">The value of axis type, corresponding to maximal viewport value.</param>
|
---|
348 | public virtual void SetConversion(double min, T minValue, double max, T maxValue)
|
---|
349 | {
|
---|
350 | throw new NotImplementedException();
|
---|
351 | }
|
---|
352 |
|
---|
353 | private Range<T> CreateRangeFromRect(DataRect visible)
|
---|
354 | {
|
---|
355 | T min, max;
|
---|
356 |
|
---|
357 | Range<T> range;
|
---|
358 | switch (Placement)
|
---|
359 | {
|
---|
360 | case AxisPlacement.Left:
|
---|
361 | case AxisPlacement.Right:
|
---|
362 | min = ConvertFromDouble(visible.YMin);
|
---|
363 | max = ConvertFromDouble(visible.YMax);
|
---|
364 | break;
|
---|
365 | case AxisPlacement.Top:
|
---|
366 | case AxisPlacement.Bottom:
|
---|
367 | min = ConvertFromDouble(visible.XMin);
|
---|
368 | max = ConvertFromDouble(visible.XMax);
|
---|
369 | break;
|
---|
370 | default:
|
---|
371 | throw new NotSupportedException();
|
---|
372 | }
|
---|
373 |
|
---|
374 | TrySort(ref min, ref max);
|
---|
375 | range = new Range<T>(min, max);
|
---|
376 | return range;
|
---|
377 | }
|
---|
378 |
|
---|
379 | private static void TrySort<TS>(ref TS min, ref TS max)
|
---|
380 | {
|
---|
381 | if (min is IComparable)
|
---|
382 | {
|
---|
383 | IComparable c1 = (IComparable)min;
|
---|
384 | // if min > max
|
---|
385 | if (c1.CompareTo(max) > 0)
|
---|
386 | {
|
---|
387 | TS temp = min;
|
---|
388 | min = max;
|
---|
389 | max = temp;
|
---|
390 | }
|
---|
391 | }
|
---|
392 | }
|
---|
393 |
|
---|
394 | protected override void OnPlacementChanged(AxisPlacement oldPlacement, AxisPlacement newPlacement)
|
---|
395 | {
|
---|
396 | axisControl.Placement = Placement;
|
---|
397 | if (ParentPlotter != null)
|
---|
398 | {
|
---|
399 | Panel panel = GetPanelByPlacement(oldPlacement);
|
---|
400 | panel.Children.Remove(this);
|
---|
401 |
|
---|
402 | Panel newPanel = GetPanelByPlacement(newPlacement);
|
---|
403 | int index = GetInsertionIndexByPlacement(newPlacement, newPanel);
|
---|
404 | newPanel.Children.Insert(index, this);
|
---|
405 | }
|
---|
406 | }
|
---|
407 |
|
---|
408 | protected override void OnPlotterDetaching(Plotter2D plotter)
|
---|
409 | {
|
---|
410 | if (plotter == null)
|
---|
411 | return;
|
---|
412 |
|
---|
413 | Panel panel = GetPanelByPlacement(Placement);
|
---|
414 | if (panel != null)
|
---|
415 | {
|
---|
416 | panel.Children.Remove(this);
|
---|
417 | }
|
---|
418 |
|
---|
419 | plotter.Viewport.PropertyChanged -= OnViewportPropertyChanged;
|
---|
420 | axisControl.Transform = null;
|
---|
421 | }
|
---|
422 | }
|
---|
423 | }
|
---|