1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.Linq;
|
---|
4 | using System.Text;
|
---|
5 | using System.Windows.Markup;
|
---|
6 | using System.Windows;
|
---|
7 | using System.Windows.Data;
|
---|
8 | using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
|
---|
9 | using System.Windows.Threading;
|
---|
10 | using Microsoft.Research.DynamicDataDisplay.Converters;
|
---|
11 | using System.Globalization;
|
---|
12 | using System.Windows.Controls;
|
---|
13 | using System.Diagnostics.Contracts;
|
---|
14 | using System.Windows.Shapes;
|
---|
15 | using System.Windows.Media;
|
---|
16 | using System.Diagnostics;
|
---|
17 | using System.ComponentModel;
|
---|
18 |
|
---|
19 | namespace Microsoft.Research.DynamicDataDisplay.Charts.Shapes
|
---|
20 | {
|
---|
21 | public class PivotSegmentEditor : FrameworkElement, IPlotterElement, INotifyPropertyChanged
|
---|
22 | {
|
---|
23 | private Plotter2D plotter = null;
|
---|
24 | private ViewportHostPanel panel = new ViewportHostPanel();
|
---|
25 | private Segment segment;
|
---|
26 | private DraggablePoint startThumb;
|
---|
27 | private DraggablePoint endThumb;
|
---|
28 | private Func<double, string> xMapping = d => d.ToString("F");
|
---|
29 | private readonly ViewportRay leftRay;
|
---|
30 | private readonly ViewportRay rightRay;
|
---|
31 |
|
---|
32 | /// <summary>
|
---|
33 | /// Initializes a new instance of the <see cref="PivotSegmentEditor"/> class.
|
---|
34 | /// </summary>
|
---|
35 | public PivotSegmentEditor()
|
---|
36 | {
|
---|
37 | ResourceDictionary resources = new ResourceDictionary
|
---|
38 | {
|
---|
39 | Source = new Uri("/DynamicDataDisplay;component/Charts/Shapes/PivotSegmentEditor.xaml", UriKind.Relative)
|
---|
40 | };
|
---|
41 |
|
---|
42 | panel.BeginBatchAdd();
|
---|
43 |
|
---|
44 | ControlTemplate segmentTemplate = (ControlTemplate)resources["segment"];
|
---|
45 | segment = (Segment)segmentTemplate.LoadContent();
|
---|
46 | segment.DataContext = this;
|
---|
47 |
|
---|
48 | ControlTemplate startThumbTemplate = (ControlTemplate)resources["leftThumb"];
|
---|
49 | startThumb = (DraggablePoint)startThumbTemplate.LoadContent();
|
---|
50 | startThumb.DataContext = this;
|
---|
51 |
|
---|
52 | ControlTemplate endThumbTemplate = (ControlTemplate)resources["rightThumb"];
|
---|
53 | endThumb = (DraggablePoint)endThumbTemplate.LoadContent();
|
---|
54 | endThumb.DataContext = this;
|
---|
55 |
|
---|
56 | ControlTemplate leftRayTemplate = (ControlTemplate)resources["leftRay"];
|
---|
57 | leftRay = (ViewportRay)leftRayTemplate.LoadContent();
|
---|
58 | leftRay.DataContext = this;
|
---|
59 |
|
---|
60 | ControlTemplate rightRayTemplate = (ControlTemplate)resources["rightRay"];
|
---|
61 | rightRay = (ViewportRay)rightRayTemplate.LoadContent();
|
---|
62 | rightRay.DataContext = this;
|
---|
63 |
|
---|
64 | ControlTemplate mTextTemplate = (ControlTemplate)resources["mText"];
|
---|
65 | TextBlock mText = (TextBlock)mTextTemplate.LoadContent();
|
---|
66 | panel.Children.Add(mText);
|
---|
67 | mText.DataContext = this;
|
---|
68 |
|
---|
69 | ControlTemplate leftPointGridTemplate = (ControlTemplate)resources["leftPointGrid"];
|
---|
70 | Panel leftPointGrid = (Panel)leftPointGridTemplate.LoadContent();
|
---|
71 | panel.Children.Add(leftPointGrid);
|
---|
72 | leftPointGrid.DataContext = this;
|
---|
73 |
|
---|
74 | ControlTemplate rightPointGridTemplate = (ControlTemplate)resources["rightPointGrid"];
|
---|
75 | Panel rightPointGrid = (Panel)rightPointGridTemplate.LoadContent();
|
---|
76 | panel.Children.Add(rightPointGrid);
|
---|
77 | rightPointGrid.DataContext = this;
|
---|
78 |
|
---|
79 | ControlTemplate leftTextTemplate = (ControlTemplate)resources["leftText"];
|
---|
80 | FrameworkElement leftBorder = (FrameworkElement)leftTextTemplate.LoadContent();
|
---|
81 | panel.Children.Add(leftBorder);
|
---|
82 | leftBorder.DataContext = this;
|
---|
83 |
|
---|
84 | ControlTemplate rightTextTemplate = (ControlTemplate)resources["rightText"];
|
---|
85 | FrameworkElement rightBorder = (FrameworkElement)rightTextTemplate.LoadContent();
|
---|
86 | panel.Children.Add(rightBorder);
|
---|
87 | rightBorder.DataContext = this;
|
---|
88 | }
|
---|
89 |
|
---|
90 | #region Properties
|
---|
91 |
|
---|
92 | #region XMapping property
|
---|
93 |
|
---|
94 | [NotNull]
|
---|
95 | public Func<double, string> XMapping
|
---|
96 | {
|
---|
97 | get { return xMapping; }
|
---|
98 | set
|
---|
99 | {
|
---|
100 | Contract.Assert(value != null);
|
---|
101 |
|
---|
102 | xMapping = value;
|
---|
103 | PropertyChanged.Raise(this, "");
|
---|
104 | }
|
---|
105 | }
|
---|
106 |
|
---|
107 | #endregion
|
---|
108 |
|
---|
109 | #region Point1 property
|
---|
110 |
|
---|
111 | public Point Point1
|
---|
112 | {
|
---|
113 | get { return (Point)GetValue(Point1Property); }
|
---|
114 | set { SetValue(Point1Property, value); }
|
---|
115 | }
|
---|
116 |
|
---|
117 | public static readonly DependencyProperty Point1Property = DependencyProperty.Register(
|
---|
118 | "Point1",
|
---|
119 | typeof(Point),
|
---|
120 | typeof(PivotSegmentEditor),
|
---|
121 | new FrameworkPropertyMetadata(new Point(0, 0), OnPointChanged));
|
---|
122 |
|
---|
123 | private static void OnPointChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
---|
124 | {
|
---|
125 | PivotSegmentEditor owner = (PivotSegmentEditor)d;
|
---|
126 | owner.CoerceValue(LeftYProperty);
|
---|
127 | owner.CoerceValue(RightYProperty);
|
---|
128 | owner.OnPointChanged();
|
---|
129 | }
|
---|
130 |
|
---|
131 | private void OnPointChanged()
|
---|
132 | {
|
---|
133 | PropertyChanged.Raise(this, "");
|
---|
134 | }
|
---|
135 |
|
---|
136 | #endregion
|
---|
137 |
|
---|
138 | #region Point2 property
|
---|
139 |
|
---|
140 | public Point Point2
|
---|
141 | {
|
---|
142 | get { return (Point)GetValue(Point2Property); }
|
---|
143 | set { SetValue(Point2Property, value); }
|
---|
144 | }
|
---|
145 |
|
---|
146 | public static readonly DependencyProperty Point2Property = DependencyProperty.Register(
|
---|
147 | "Point2",
|
---|
148 | typeof(Point),
|
---|
149 | typeof(PivotSegmentEditor),
|
---|
150 | new FrameworkPropertyMetadata(new Point(1, 1), OnPointChanged));
|
---|
151 |
|
---|
152 | #endregion
|
---|
153 |
|
---|
154 | #region LineStroke property
|
---|
155 |
|
---|
156 | public Brush LineStroke
|
---|
157 | {
|
---|
158 | get { return (Brush)GetValue(LineStrokeProperty); }
|
---|
159 | set { SetValue(LineStrokeProperty, value); }
|
---|
160 | }
|
---|
161 |
|
---|
162 | public static readonly DependencyProperty LineStrokeProperty = DependencyProperty.Register(
|
---|
163 | "LineStroke",
|
---|
164 | typeof(Brush),
|
---|
165 | typeof(PivotSegmentEditor),
|
---|
166 | new FrameworkPropertyMetadata(Brushes.Black));
|
---|
167 |
|
---|
168 | #endregion
|
---|
169 |
|
---|
170 | #region LineThickness property
|
---|
171 |
|
---|
172 | public double LineThickness
|
---|
173 | {
|
---|
174 | get { return (double)GetValue(LineThicknessProperty); }
|
---|
175 | set { SetValue(LineThicknessProperty, value); }
|
---|
176 | }
|
---|
177 |
|
---|
178 | public static readonly DependencyProperty LineThicknessProperty = DependencyProperty.Register(
|
---|
179 | "LineThickbess",
|
---|
180 | typeof(double),
|
---|
181 | typeof(PivotSegmentEditor),
|
---|
182 | new FrameworkPropertyMetadata(2.0));
|
---|
183 |
|
---|
184 | #endregion
|
---|
185 |
|
---|
186 | #region LeftY property
|
---|
187 |
|
---|
188 | public double LeftY
|
---|
189 | {
|
---|
190 | get { return (double)GetValue(LeftYProperty); }
|
---|
191 | }
|
---|
192 |
|
---|
193 | private static readonly DependencyPropertyKey LeftYPropertyKey = DependencyProperty.RegisterReadOnly(
|
---|
194 | "LeftY",
|
---|
195 | typeof(double),
|
---|
196 | typeof(PivotSegmentEditor),
|
---|
197 | new FrameworkPropertyMetadata(null, OnLeftYCoerce));
|
---|
198 |
|
---|
199 | public static readonly DependencyProperty LeftYProperty = LeftYPropertyKey.DependencyProperty;
|
---|
200 |
|
---|
201 | private Func<double, double> GetLineFunc()
|
---|
202 | {
|
---|
203 | Viewport2D viewport = plotter.Viewport;
|
---|
204 | double deltaX = Point1.X - Point2.X;
|
---|
205 | double deltaY = Point1.Y - Point2.Y;
|
---|
206 | double m = deltaY / deltaX;
|
---|
207 | double b = Point1.Y - Point1.X * deltaY / deltaX;
|
---|
208 |
|
---|
209 | Func<double, double> func = x => m * x + b;
|
---|
210 |
|
---|
211 | return func;
|
---|
212 | }
|
---|
213 |
|
---|
214 | private static object OnLeftYCoerce(DependencyObject source, object value)
|
---|
215 | {
|
---|
216 | PivotSegmentEditor editor = (PivotSegmentEditor)source;
|
---|
217 | if (editor.plotter == null)
|
---|
218 | return value;
|
---|
219 |
|
---|
220 | Func<double, double> func = editor.GetLineFunc();
|
---|
221 | double xmin = editor.plotter.Viewport.Visible.XMin;
|
---|
222 | double result = func(xmin);
|
---|
223 |
|
---|
224 | return result;
|
---|
225 | }
|
---|
226 |
|
---|
227 | #endregion
|
---|
228 |
|
---|
229 | #region RightY property
|
---|
230 |
|
---|
231 | public double RightY
|
---|
232 | {
|
---|
233 | get { return (double)GetValue(RightYProperty); }
|
---|
234 | }
|
---|
235 |
|
---|
236 | private static readonly DependencyPropertyKey RightYPropertyKey = DependencyProperty.RegisterReadOnly(
|
---|
237 | "RightY",
|
---|
238 | typeof(double),
|
---|
239 | typeof(PivotSegmentEditor),
|
---|
240 | new FrameworkPropertyMetadata(null, OnRightYCoerce));
|
---|
241 |
|
---|
242 | public static readonly DependencyProperty RightYProperty = RightYPropertyKey.DependencyProperty;
|
---|
243 |
|
---|
244 | private static object OnRightYCoerce(DependencyObject source, object value)
|
---|
245 | {
|
---|
246 | PivotSegmentEditor editor = (PivotSegmentEditor)source;
|
---|
247 | if (editor.plotter == null)
|
---|
248 | return value;
|
---|
249 |
|
---|
250 | Func<double, double> func = editor.GetLineFunc();
|
---|
251 | double xmax = editor.plotter.Viewport.Visible.XMax;
|
---|
252 | double result = func(xmax);
|
---|
253 |
|
---|
254 | return result;
|
---|
255 | }
|
---|
256 | #endregion
|
---|
257 |
|
---|
258 | public double M
|
---|
259 | {
|
---|
260 | get { return (Point1.Y - Point2.Y) / (Point1.X - Point2.X); }
|
---|
261 | set { Debug.WriteLine("M set = " + value.ToString()); }
|
---|
262 | }
|
---|
263 |
|
---|
264 | public string LeftName
|
---|
265 | {
|
---|
266 | get
|
---|
267 | {
|
---|
268 | return String.Format("({0}, {1:F})", xMapping(Point1.X), Point1.Y);
|
---|
269 | }
|
---|
270 | }
|
---|
271 |
|
---|
272 | public string RightName
|
---|
273 | {
|
---|
274 | get
|
---|
275 | {
|
---|
276 | return String.Format("({0}, {1:F})", xMapping(Point2.X), Point2.Y);
|
---|
277 | }
|
---|
278 | }
|
---|
279 |
|
---|
280 | public Point Center
|
---|
281 | {
|
---|
282 | get
|
---|
283 | {
|
---|
284 | return Point1 + (Point2 - Point1) / 2;
|
---|
285 | }
|
---|
286 | }
|
---|
287 |
|
---|
288 | #endregion
|
---|
289 |
|
---|
290 | #region IPlotterElement Members
|
---|
291 |
|
---|
292 | public void OnPlotterAttached(Plotter plotter)
|
---|
293 | {
|
---|
294 | this.plotter = (Plotter2D)plotter;
|
---|
295 |
|
---|
296 | this.plotter.Viewport.PropertyChanged += OnViewport_PropertyChanged;
|
---|
297 |
|
---|
298 | plotter.Dispatcher.BeginInvoke(() =>
|
---|
299 | {
|
---|
300 | plotter.Children.AddMany(
|
---|
301 | segment,
|
---|
302 | startThumb,
|
---|
303 | endThumb,
|
---|
304 | panel,
|
---|
305 | leftRay,
|
---|
306 | rightRay);
|
---|
307 |
|
---|
308 | CoerceValue(LeftYProperty);
|
---|
309 | CoerceValue(RightYProperty);
|
---|
310 |
|
---|
311 | PropertyChanged.Raise(this, "");
|
---|
312 | }, DispatcherPriority.Normal);
|
---|
313 | }
|
---|
314 |
|
---|
315 | private void OnViewport_PropertyChanged(object sender, ExtendedPropertyChangedEventArgs e)
|
---|
316 | {
|
---|
317 | CoerceValue(LeftYProperty);
|
---|
318 | CoerceValue(RightYProperty);
|
---|
319 | }
|
---|
320 |
|
---|
321 | public void OnPlotterDetaching(Plotter plotter)
|
---|
322 | {
|
---|
323 | this.plotter.Viewport.PropertyChanged -= OnViewport_PropertyChanged;
|
---|
324 |
|
---|
325 | plotter.Dispatcher.BeginInvoke(() =>
|
---|
326 | {
|
---|
327 | plotter.Children.RemoveAll(
|
---|
328 | segment,
|
---|
329 | startThumb,
|
---|
330 | endThumb,
|
---|
331 | panel,
|
---|
332 | leftRay,
|
---|
333 | rightRay);
|
---|
334 | }, DispatcherPriority.Normal);
|
---|
335 |
|
---|
336 | this.plotter = null;
|
---|
337 | }
|
---|
338 |
|
---|
339 | public Plotter2D Plotter
|
---|
340 | {
|
---|
341 | get { return plotter; }
|
---|
342 | }
|
---|
343 |
|
---|
344 | Plotter IPlotterElement.Plotter
|
---|
345 | {
|
---|
346 | get { return plotter; }
|
---|
347 | }
|
---|
348 |
|
---|
349 | #endregion
|
---|
350 |
|
---|
351 | #region INotifyPropertyChanged Members
|
---|
352 |
|
---|
353 | public event PropertyChangedEventHandler PropertyChanged;
|
---|
354 |
|
---|
355 | #endregion
|
---|
356 | }
|
---|
357 |
|
---|
358 | public sealed class MToVerticalOffsetConverter : GenericValueConverter<double>
|
---|
359 | {
|
---|
360 | public override object ConvertCore(double value, Type targetType, object parameter, CultureInfo culture)
|
---|
361 | {
|
---|
362 | if (value > 0)
|
---|
363 | return 2.0;
|
---|
364 | else
|
---|
365 | return -2.0;
|
---|
366 | }
|
---|
367 | }
|
---|
368 |
|
---|
369 | public sealed class MToVerticalAlignmentInvertedConverter : GenericValueConverter<double>
|
---|
370 | {
|
---|
371 | public override object ConvertCore(double value, Type targetType, object parameter, CultureInfo culture)
|
---|
372 | {
|
---|
373 | if (value > 0)
|
---|
374 | return VerticalAlignment.Bottom;
|
---|
375 | else
|
---|
376 | return VerticalAlignment.Top;
|
---|
377 | }
|
---|
378 | }
|
---|
379 |
|
---|
380 | public sealed class MToVerticalAlignmentConverter : GenericValueConverter<double>
|
---|
381 | {
|
---|
382 | public override object ConvertCore(double value, Type targetType, object parameter, CultureInfo culture)
|
---|
383 | {
|
---|
384 | if (value > 0)
|
---|
385 | return VerticalAlignment.Top;
|
---|
386 | else
|
---|
387 | return VerticalAlignment.Bottom;
|
---|
388 | }
|
---|
389 | }
|
---|
390 | }
|
---|
391 |
|
---|