Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ScaleBar.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Globalization;
6 using System.Windows;
7 using System.Windows.Input;
8 using System.Windows.Media;
9 
10 namespace SiliconStudio.Presentation.Controls
11 {
12  public delegate void CustomRenderRoutedEventHandler(object sender, CustomRenderRoutedEventArgs e);
13 
15  {
16  public DrawingContext DrawingContext { get; private set; }
17 
18  public CustomRenderRoutedEventArgs(RoutedEvent routedEvent, DrawingContext drawingContext)
19  {
20  RoutedEvent = routedEvent;
21  DrawingContext = drawingContext;
22  }
23  }
24 
25  public delegate void RoutedDependencyPropertyChangedEventHandler(object sender, RoutedDependencyPropertyChangedEventArgs e);
26 
28  {
29  public object OldValue { get; private set; }
30  public object NewValue { get; private set; }
31  public DependencyProperty DependencyProperty { get; private set; }
32 
33  public RoutedDependencyPropertyChangedEventArgs(RoutedEvent routedEvent, DependencyProperty dependencyProperty, object oldValue, object newValue)
34  {
35  RoutedEvent = routedEvent;
36  DependencyProperty = dependencyProperty;
37  OldValue = oldValue;
38  NewValue = newValue;
39  }
40  }
41 
42  public class ScaleBar : FrameworkElement
43  {
44  public static readonly DependencyProperty CustomDrawingContextProperty = DependencyProperty.Register(
45  "CustomDrawingContext",
46  typeof(DrawingContext),
47  typeof(ScaleBar),
48  new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
49 
50  public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register(
51  "Background",
52  typeof(Brush),
53  typeof(ScaleBar),
54  new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.AffectsRender));
55 
56  public static readonly DependencyProperty LargeTickPenProperty = DependencyProperty.Register(
57  "LargeTickPen",
58  typeof(Pen),
59  typeof(ScaleBar),
60  new FrameworkPropertyMetadata(new Pen(Brushes.Black, 1.0), FrameworkPropertyMetadataOptions.AffectsRender));
61 
62  public static readonly DependencyProperty SmallTickPenProperty = DependencyProperty.Register(
63  "SmallTickPen",
64  typeof(Pen),
65  typeof(ScaleBar),
66  new FrameworkPropertyMetadata(new Pen(Brushes.Gray, 1.0), FrameworkPropertyMetadataOptions.AffectsRender));
67 
68  public static readonly DependencyProperty LargeTickTopProperty = DependencyProperty.Register(
69  "LargeTickTop",
70  typeof(double),
71  typeof(ScaleBar),
72  new FrameworkPropertyMetadata(0.625, FrameworkPropertyMetadataOptions.AffectsRender));
73 
74  public static readonly DependencyProperty LargeTickBottomProperty = DependencyProperty.Register(
75  "LargeTickBottom",
76  typeof(double),
77  typeof(ScaleBar),
78  new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender));
79 
80  public static readonly DependencyProperty SmallTickTopProperty = DependencyProperty.Register(
81  "SmallTickTop",
82  typeof(double),
83  typeof(ScaleBar),
84  new FrameworkPropertyMetadata(0.75, FrameworkPropertyMetadataOptions.AffectsRender));
85 
86  public static readonly DependencyProperty SmallTickBottomProperty = DependencyProperty.Register(
87  "SmallTickBottom",
88  typeof(double),
89  typeof(ScaleBar),
90  new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender));
91 
92  public static readonly DependencyProperty DecimalCountRoundingProperty = DependencyProperty.Register(
93  "DecimalCountRounding",
94  typeof(int),
95  typeof(ScaleBar),
96  new FrameworkPropertyMetadata(6, null, CoerceDecimalCountRoundingPropertyValue));
97 
98  public static readonly DependencyProperty TextPositionOriginProperty = DependencyProperty.Register(
99  "TextPositionOrigin",
100  typeof(Point),
101  typeof(ScaleBar),
102  new FrameworkPropertyMetadata(new Point(0.5, 0.0), FrameworkPropertyMetadataOptions.AffectsRender));
103 
104  public static readonly DependencyProperty TextPositionProperty = DependencyProperty.Register(
105  "TextPosition",
106  typeof(double),
107  typeof(ScaleBar),
108  new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
109 
110  public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register(
111  "Foreground",
112  typeof(Brush),
113  typeof(ScaleBar),
114  new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender));
115 
116  public static readonly DependencyProperty FontProperty = DependencyProperty.Register(
117  "Font",
118  typeof(Typeface),
119  typeof(ScaleBar),
120  new FrameworkPropertyMetadata(new Typeface("Meiryo"), FrameworkPropertyMetadataOptions.AffectsRender));
121 
122  public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register(
123  "FontSize",
124  typeof(double),
125  typeof(ScaleBar),
126  new FrameworkPropertyMetadata(9.0, FrameworkPropertyMetadataOptions.AffectsRender));
127 
128  public static readonly DependencyProperty StartUnitProperty = DependencyProperty.Register(
129  "StartUnit",
130  typeof(double),
131  typeof(ScaleBar),
132  new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
133 
134  public static readonly DependencyProperty MinimumUnitsPerTickProperty = DependencyProperty.Register(
135  "MinimumUnitsPerTick",
136  typeof(double),
137  typeof(ScaleBar),
138  new FrameworkPropertyMetadata(1e-12, FrameworkPropertyMetadataOptions.AffectsRender, OnUnitsPerTickPropertyChanged));
139 
140  public static readonly DependencyProperty MaximumUnitsPerTickProperty = DependencyProperty.Register(
141  "MaximumUnitsPerTick",
142  typeof(double),
143  typeof(ScaleBar),
144  new FrameworkPropertyMetadata(1e12, FrameworkPropertyMetadataOptions.AffectsRender, OnUnitsPerTickPropertyChanged));
145 
146  public static readonly DependencyProperty UnitsPerTickProperty = DependencyProperty.Register(
147  "UnitsPerTick",
148  typeof(double),
149  typeof(ScaleBar),
150  new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender, OnUnitsPerTickPropertyChanged, CoerceUnitsPerTickPropertyValue));
151 
152  public static readonly DependencyProperty PixelsPerTickProperty = DependencyProperty.Register(
153  "PixelsPerTick",
154  typeof(double),
155  typeof(ScaleBar),
156  new FrameworkPropertyMetadata(100.0, FrameworkPropertyMetadataOptions.AffectsRender, OnPixelsPerTickPropertyChanged, CoercePixelsPerTickPropertyValue));
157 
158  private static readonly DependencyPropertyKey AdjustedUnitsPerTickPropertyKey = DependencyProperty.RegisterReadOnly(
159  "AdjustedUnitsPerTick",
160  typeof(double),
161  typeof(ScaleBar),
162  new PropertyMetadata());
163  public static readonly DependencyProperty AdjustedUnitsPerTickProperty = AdjustedUnitsPerTickPropertyKey.DependencyProperty;
164 
165  private static readonly DependencyPropertyKey AdjustedPixelsPerTickPropertyKey = DependencyProperty.RegisterReadOnly(
166  "AdjustedPixelsPerTick",
167  typeof(double),
168  typeof(ScaleBar),
169  new PropertyMetadata());
170  public static readonly DependencyProperty AdjustedPixelsPerTickProperty = AdjustedPixelsPerTickPropertyKey.DependencyProperty;
171 
172  private static readonly DependencyPropertyKey PixelsPerUnitPropertyKey = DependencyProperty.RegisterReadOnly(
173  "PixelsPerUnit",
174  typeof(double),
175  typeof(ScaleBar),
176  new PropertyMetadata());
177  public static readonly DependencyProperty PixelsPerUnitProperty = PixelsPerUnitPropertyKey.DependencyProperty;
178 
179  private static readonly DependencyPropertyKey AdjustedPixelsPerUnitPropertyKey = DependencyProperty.RegisterReadOnly(
180  "AdjustedPixelsPerUnit",
181  typeof(double),
182  typeof(ScaleBar),
183  new PropertyMetadata());
184  public static readonly DependencyProperty AdjustedPixelsPerUnitProperty = AdjustedPixelsPerUnitPropertyKey.DependencyProperty;
185 
186  public static readonly DependencyProperty IsAliasedProperty = DependencyProperty.Register(
187  "IsAliased",
188  typeof(bool),
189  typeof(ScaleBar),
190  new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender, OnIsAliasedPropertyChanged));
191 
192  public static readonly DependencyProperty IsTextVisibleProperty = DependencyProperty.Register(
193  "IsTextVisible",
194  typeof(bool),
195  typeof(ScaleBar),
196  new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender));
197 
198  public static readonly DependencyProperty IsSmallTickVisibleProperty = DependencyProperty.Register(
199  "IsSmallTickVisible",
200  typeof(bool),
201  typeof(ScaleBar),
202  new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender));
203 
204  public static readonly DependencyProperty IsZoomingOnMouseWheelProperty = DependencyProperty.Register(
205  "IsZoomingOnMouseWheel",
206  typeof(bool),
207  typeof(ScaleBar),
208  new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
209 
210  public static readonly DependencyProperty MouseWheelZoomCoeficientProperty = DependencyProperty.Register(
211  "MouseWheelZoomCoeficient",
212  typeof(double),
213  typeof(ScaleBar),
214  new FrameworkPropertyMetadata(1.1, FrameworkPropertyMetadataOptions.AffectsRender));
215 
216  public static readonly DependencyProperty IsDraggingOnLeftMouseButtonProperty = DependencyProperty.Register(
217  "IsDraggingOnLeftMouseButton",
218  typeof(bool),
219  typeof(ScaleBar),
220  new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
221 
222  public static readonly DependencyProperty IsDraggingOnRightMouseButtonProperty = DependencyProperty.Register(
223  "IsDraggingOnRightMouseButton",
224  typeof(bool),
225  typeof(ScaleBar),
226  new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
227 
228  public static readonly DependencyProperty UnitSystemProperty = DependencyProperty.Register(
229  "UnitSystem",
230  typeof(UnitSystem),
231  typeof(ScaleBar),
232  new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, OnUnitSystemPropertyChanged));
233 
234  public static readonly DependencyProperty SymbolProperty = DependencyProperty.Register(
235  "UnitSymbol",
236  typeof(string),
237  typeof(ScaleBar),
238  new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
239 
240  public static readonly DependencyProperty TickTextUnitDividerProperty = DependencyProperty.Register(
241  "TickTextUnitDivider",
242  typeof(double),
243  typeof(ScaleBar),
244  new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender));
245 
246  public static readonly RoutedEvent BeforeRenderEvent = EventManager.RegisterRoutedEvent(
247  "BeforeRender",
248  RoutingStrategy.Bubble,
249  typeof(RoutedEventHandler),
250  typeof(ScaleBar));
251 
252  public static readonly RoutedEvent AfterRenderEvent = EventManager.RegisterRoutedEvent(
253  "AfterRender",
254  RoutingStrategy.Bubble,
255  typeof(RoutedEventHandler),
256  typeof(ScaleBar));
257 
258  public static readonly RoutedEvent BeforeTicksRenderEvent = EventManager.RegisterRoutedEvent(
259  "BeforeTicksRender",
260  RoutingStrategy.Bubble,
262  typeof(ScaleBar));
263 
264  public static readonly RoutedEvent AfterTicksRenderEvent = EventManager.RegisterRoutedEvent(
265  "AfterTicksRender",
266  RoutingStrategy.Bubble,
268  typeof(ScaleBar));
269 
270  public static readonly RoutedEvent ScaleChangingEvent = EventManager.RegisterRoutedEvent(
271  "ScaleChanging",
272  RoutingStrategy.Bubble,
274  typeof(ScaleBar));
275 
276  public static readonly RoutedEvent ScaleChangedEvent = EventManager.RegisterRoutedEvent(
277  "ScaleChanged",
278  RoutingStrategy.Bubble,
280  typeof(ScaleBar));
281 
282  public ScaleBar()
283  {
284  Loaded += OnLoaded;
285  ClipToBounds = true;
286  }
287 
288  private void OnLoaded(object sender, RoutedEventArgs e)
289  {
290  SetValue(AdjustedUnitsPerTickPropertyKey, UnitsPerTick);
291  SetValue(AdjustedPixelsPerTickPropertyKey, PixelsPerTick);
292  RenderOptions.SetEdgeMode(this, IsAliased ? EdgeMode.Aliased : EdgeMode.Unspecified);
293  InvalidateVisual();
294  }
295 
296  public event RoutedEventHandler BeforeRender
297  {
298  add { AddHandler(BeforeRenderEvent, value); }
299  remove { RemoveHandler(BeforeRenderEvent, value); }
300  }
301 
302  public event RoutedEventHandler AfterRender
303  {
304  add { AddHandler(AfterRenderEvent, value); }
305  remove { RemoveHandler(AfterRenderEvent, value); }
306  }
307 
308  public event CustomRenderRoutedEventHandler BeforeTicksRender
309  {
310  add { AddHandler(BeforeTicksRenderEvent, value); }
311  remove { RemoveHandler(BeforeTicksRenderEvent, value); }
312  }
313 
314  public event CustomRenderRoutedEventHandler AfterTicksRender
315  {
316  add { AddHandler(AfterTicksRenderEvent, value); }
317  remove { RemoveHandler(AfterTicksRenderEvent, value); }
318  }
319 
320  public event RoutedDependencyPropertyChangedEventHandler ScaleChanging
321  {
322  add { AddHandler(ScaleChangingEvent, value); }
323  remove { RemoveHandler(ScaleChangingEvent, value); }
324  }
325 
326  public event RoutedDependencyPropertyChangedEventHandler ScaleChanged
327  {
328  add { AddHandler(ScaleChangedEvent, value); }
329  remove { RemoveHandler(ScaleChangedEvent, value); }
330  }
331 
332  private void RaiseBeforeRenderEvent()
333  {
334  RaiseEvent(new RoutedEventArgs(BeforeRenderEvent));
335  }
336 
337  private void RaiseAfterRenderEvent()
338  {
339  RaiseEvent(new RoutedEventArgs(AfterRenderEvent));
340  }
341 
342  private void RaiseBeforeTicksRenderEvent(DrawingContext drawingContext)
343  {
344  RaiseEvent(new CustomRenderRoutedEventArgs(BeforeTicksRenderEvent, drawingContext));
345  }
346 
347  private void RaiseAfterTicksRenderEvent(DrawingContext drawingContext)
348  {
349  RaiseEvent(new CustomRenderRoutedEventArgs(AfterTicksRenderEvent, drawingContext));
350  }
351 
352  private void RaiseScaleChangingEvent(DependencyProperty dependencyProperty, object oldValue, object newValue)
353  {
354  RaiseEvent(new RoutedDependencyPropertyChangedEventArgs(ScaleChangingEvent, dependencyProperty, oldValue, newValue));
355  }
356 
357  private void RaiseScaleChangedEvent(DependencyProperty dependencyProperty, object oldValue, object newValue)
358  {
359  RaiseEvent(new RoutedDependencyPropertyChangedEventArgs(ScaleChangedEvent, dependencyProperty, oldValue, newValue));
360  }
361 
362  private void SetScaleChangingProperty(DependencyProperty dependencyProperty, object value)
363  {
364  var oldValue = GetValue(dependencyProperty);
365  RaiseScaleChangingEvent(dependencyProperty, oldValue, value);
366  SetValue(dependencyProperty, value);
367  RaiseScaleChangedEvent(dependencyProperty, oldValue, value);
368  }
369 
370  private void SetScaleChangingProperty(DependencyPropertyKey dependencyPropertyKey, DependencyProperty dependencyProperty, object value)
371  {
372  var oldValue = GetValue(dependencyProperty);
373  RaiseScaleChangingEvent(dependencyProperty, oldValue, value);
374  SetValue(dependencyPropertyKey, value);
375  RaiseScaleChangedEvent(dependencyProperty, oldValue, value);
376  }
377 
378  public DrawingContext CustomDrawingContext
379  {
380  get { return (DrawingContext)GetValue(CustomDrawingContextProperty); }
381  set { SetValue(CustomDrawingContextProperty, value); }
382  }
383 
384  public Brush Background
385  {
386  get { return (Brush)GetValue(BackgroundProperty); }
387  set { SetValue(BackgroundProperty, value); }
388  }
389 
390  public Pen LargeTickPen
391  {
392  get { return (Pen)GetValue(LargeTickPenProperty); }
393  set { SetValue(LargeTickPenProperty, value); }
394  }
395 
396  public Pen SmallTickPen
397  {
398  get { return (Pen)GetValue(SmallTickPenProperty); }
399  set { SetValue(SmallTickPenProperty, value); }
400  }
401 
402  /// <summary>
403  /// Gets or sets the relative top (Y) coordinate of the drawn large ticks. This is a dependency property.
404  /// </summary>
405  /// <remarks>The coordinate is relative, that means 0.0 is top and 1.0 is bottom.
406  /// The coordinate can be set to less than 0.0 or more than 1.0 where additional offset is needed.</remarks>
407  public double LargeTickTop
408  {
409  get { return (double)GetValue(LargeTickTopProperty); }
410  set { SetValue(LargeTickTopProperty, value); }
411  }
412 
413  /// <summary>
414  /// Gets or sets the relative bottom (Y) coordinate of the drawn large ticks. This is a dependency property.
415  /// </summary>
416  /// <remarks>The coordinate is relative, that means 0.0 is top and 1.0 is bottom.
417  /// The coordinate can be set to less than 0.0 or more than 1.0 where additional offset is needed.</remarks>
418  public double LargeTickBottom
419  {
420  get { return (double)GetValue(LargeTickBottomProperty); }
421  set { SetValue(LargeTickBottomProperty, value); }
422  }
423 
424  /// <summary>
425  /// Gets or sets the relative top (Y) coordinate of the drawn small ticks. This is a dependency property.
426  /// </summary>
427  /// <remarks>The coordinate is relative, that means 0.0 is top and 1.0 is bottom.
428  /// The coordinate can be set to less than 0.0 or more than 1.0 where additional offset is needed.</remarks>
429  public double SmallTickTop
430  {
431  get { return (double)GetValue(SmallTickTopProperty); }
432  set { SetValue(SmallTickTopProperty, value); }
433  }
434 
435  /// <summary>
436  /// Gets or sets the relative bottom (Y) coordinate of the drawn small ticks. This is a dependency property.
437  /// </summary>
438  /// <remarks>The coordinate is relative, that means 0.0 is top and 1.0 is bottom.
439  /// The coordinate can be set to less than 0.0 or more than 1.0 where additional offset is needed.</remarks>
440  public double SmallTickBottom
441  {
442  get { return (double)GetValue(SmallTickBottomProperty); }
443  set { SetValue(SmallTickBottomProperty, value); }
444  }
445 
446  public int DecimalCountRounding
447  {
448  get { return (int)GetValue(DecimalCountRoundingProperty); }
449  set { SetValue(DecimalCountRoundingProperty, value); }
450  }
451 
452  /// <summary>
453  /// Gets or sets the center point of drawn text, relative to the bounds of the drawn text itself. This is a dependency property.
454  /// </summary>
455  /// <remarks>Each coordinate axis can be set to less than 0.0 or more than 1.0 where additional offset is needed.</remarks>
456  public Point TextPositionOrigin
457  {
458  get { return (Point)GetValue(TextPositionOriginProperty); }
459  set { SetValue(TextPositionOriginProperty, value); }
460  }
461 
462  /// <summary>
463  /// Gets or sets the relative top (Y) coordinate of the center of the drawn text. This is a dependency property.
464  /// </summary>
465  /// <remarks>The coordinate is relative, that means 0.0 is top and 1.0 is bottom.
466  /// The coordinate can be set to less than 0.0 or more than 1.0 where additional offset is needed.</remarks>
467  public double TextPosition
468  {
469  get { return (double)GetValue(TextPositionProperty); }
470  set { SetValue(TextPositionProperty, value); }
471  }
472 
473  public Brush Foreground
474  {
475  get { return (Brush)GetValue(ForegroundProperty); }
476  set { SetValue(ForegroundProperty, value); }
477  }
478 
479  public Typeface Font
480  {
481  get { return (Typeface)GetValue(FontProperty); }
482  set { SetValue(FontProperty, value); }
483  }
484 
485  public double FontSize
486  {
487  get { return (double)GetValue(FontSizeProperty); }
488  set { SetValue(FontSizeProperty, value); }
489  }
490 
491  public double StartUnit
492  {
493  get { return (double)GetValue(StartUnitProperty); }
494  set { SetScaleChangingProperty(StartUnitProperty, value); }
495  }
496 
497  public double MinimumUnitsPerTick
498  {
499  get { return (double)GetValue(MinimumUnitsPerTickProperty); }
500  set { SetScaleChangingProperty(MinimumUnitsPerTickProperty, value); }
501  }
502 
503  public double MaximumUnitsPerTick
504  {
505  get { return (double)GetValue(MaximumUnitsPerTickProperty); }
506  set { SetScaleChangingProperty(MaximumUnitsPerTickProperty, value); }
507  }
508 
509  public double UnitsPerTick
510  {
511  get { return (double)GetValue(UnitsPerTickProperty); }
512  set { SetScaleChangingProperty(UnitsPerTickProperty, value); }
513  }
514 
515  public double AdjustedUnitsPerTick
516  {
517  get { return (double)GetValue(AdjustedUnitsPerTickProperty); }
518  private set { SetValue(AdjustedUnitsPerTickPropertyKey, value); }
519  }
520 
521  public double PixelsPerTick
522  {
523  get { return (double)GetValue(PixelsPerTickProperty); }
524  set { SetScaleChangingProperty(PixelsPerTickProperty, value); }
525  }
526 
527  public double AdjustedPixelsPerTick
528  {
529  get { return (double)GetValue(AdjustedPixelsPerTickProperty); }
530  private set { SetValue(AdjustedPixelsPerTickPropertyKey, value); }
531  }
532 
533  public double PixelsPerUnit
534  {
535  get { return (double)GetValue(PixelsPerUnitProperty); }
536  private set { SetScaleChangingProperty(PixelsPerUnitPropertyKey, PixelsPerUnitProperty, value); }
537  }
538 
539  public double AdjustedPixelsPerUnit
540  {
541  get { return (double)GetValue(AdjustedPixelsPerUnitProperty); }
542  private set { SetValue(AdjustedPixelsPerUnitPropertyKey, value); }
543  }
544 
545  public bool IsAliased
546  {
547  get { return (bool)GetValue(IsAliasedProperty); }
548  set { SetValue(IsAliasedProperty, value); }
549  }
550 
551  public bool IsTextVisible
552  {
553  get { return (bool)GetValue(IsTextVisibleProperty); }
554  set { SetValue(IsTextVisibleProperty, value); }
555  }
556 
557  public bool IsSmallTickVisible
558  {
559  get { return (bool)GetValue(IsSmallTickVisibleProperty); }
560  set { SetValue(IsSmallTickVisibleProperty, value); }
561  }
562 
563  public bool IsZoomingOnMouseWheel
564  {
565  get { return (bool)GetValue(IsZoomingOnMouseWheelProperty); }
566  set { SetValue(IsZoomingOnMouseWheelProperty, value); }
567  }
568 
569  public double MouseWheelZoomCoeficient
570  {
571  get { return (double)GetValue(MouseWheelZoomCoeficientProperty); }
572  set { SetValue(MouseWheelZoomCoeficientProperty, value); }
573  }
574 
575  public bool IsDraggingOnLeftMouseButton
576  {
577  get { return (bool)GetValue(IsDraggingOnLeftMouseButtonProperty); }
578  set { SetValue(IsDraggingOnLeftMouseButtonProperty, value); }
579  }
580 
581  public bool IsDraggingOnRightMouseButton
582  {
583  get { return (bool)GetValue(IsDraggingOnRightMouseButtonProperty); }
584  set { SetValue(IsDraggingOnRightMouseButtonProperty, value); }
585  }
586 
587  public UnitSystem UnitSystem
588  {
589  get { return (UnitSystem)GetValue(UnitSystemProperty); }
590  set { SetValue(UnitSystemProperty, value); }
591  }
592 
593  public string UnitSymbol
594  {
595  get { return (string)GetValue(SymbolProperty); }
596  set { SetValue(SymbolProperty, value); }
597  }
598 
599  public double TickTextUnitDivider
600  {
601  get { return (double)GetValue(TickTextUnitDividerProperty); }
602  set { SetValue(TickTextUnitDividerProperty, value); }
603  }
604 
605  private void UpdatePixelInfo()
606  {
607  AdjustedPixelsPerTick = PixelsPerTick * AdjustedUnitsPerTick / UnitsPerTick;
608  AdjustedPixelsPerUnit = AdjustedPixelsPerTick / AdjustedUnitsPerTick;
609  PixelsPerUnit = PixelsPerTick / UnitsPerTick;
610  }
611 
612  private static void OnUnitSystemPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
613  {
614  var scalebar = (ScaleBar)sender;
615  scalebar.AdjustUnitIntervalWithUnitSystem(scalebar.UnitsPerTick);
616  scalebar.UpdatePixelInfo();
617  }
618 
619  private static void OnUnitsPerTickPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
620  {
621  var scalebar = (ScaleBar)sender;
622  scalebar.AdjustUnitIntervalWithUnitSystem((double)e.NewValue);
623  scalebar.UpdatePixelInfo();
624  }
625 
626  private static void OnPixelsPerTickPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
627  {
628  var scalebar = (ScaleBar)sender;
629  scalebar.UpdatePixelInfo();
630  }
631 
632  private static void OnIsAliasedPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
633  {
634  RenderOptions.SetEdgeMode(sender, (bool)e.NewValue ? EdgeMode.Aliased : EdgeMode.Unspecified);
635  }
636 
637  private static object CoerceUnitsPerTickPropertyValue(DependencyObject sender, object value)
638  {
639  var scalebar = (ScaleBar)sender;
640  return scalebar.MinimumUnitsPerTick < scalebar.MaximumUnitsPerTick ? Math.Min(scalebar.MaximumUnitsPerTick, Math.Max(scalebar.MinimumUnitsPerTick, (double)value)) : value;
641  }
642 
643  private static object CoercePixelsPerTickPropertyValue(DependencyObject sender, object value)
644  {
645  return Math.Max(10.0, (double)value);
646  }
647 
648  private static object CoerceDecimalCountRoundingPropertyValue(DependencyObject sender, object value)
649  {
650  return Math.Max(0, 12);
651  }
652 
653  public double GetPixelAt(double unit)
654  {
655  return ((unit - StartUnit) * AdjustedPixelsPerTick) / AdjustedUnitsPerTick;
656  }
657 
658  public double GetUnitAt(double pixel)
659  {
660  return StartUnit + (pixel * AdjustedUnitsPerTick) / AdjustedPixelsPerTick;
661  }
662 
663  public void SetUnitAt(double unit, double pixel)
664  {
665  StartUnit = unit - (pixel * AdjustedUnitsPerTick) / AdjustedPixelsPerTick;
666  InvalidateVisual();
667  }
668 
669  public void SetUnitsPerTickAt(double unitsPerTick, double pixel)
670  {
671  double unit = GetUnitAt(pixel);
672  UnitsPerTick = unitsPerTick;
673  SetUnitAt(unit, pixel);
674  }
675 
676  public void SetPixelsPerTickAt(double pixelsPerTick, double pixel)
677  {
678  double unit = GetUnitAt(pixel);
679  PixelsPerTick = pixelsPerTick;
680  SetUnitAt(unit, pixel);
681  }
682 
683  private Pen largeTickPen;
684  private Pen smallTickPen;
685 
686  private Point textPositionOrigin;
687  private double textPosition;
688  private bool isTextVisible;
689  private Brush foreground;
690  private Typeface font;
691  private double fontSize;
692 
693  private double actualWidth;
694  private double actualHeight;
695 
696  private double largeTickTopPosition;
697  private double largeTickBottomPosition;
698  private double smallTickTopPosition;
699  private double smallTickBottomPosition;
700 
701  private int adjustedSmallIntervalPerTick = 10;
702 
703  protected override void OnRender(DrawingContext localDrawingContext)
704  {
705  DrawingContext drawingContext = CustomDrawingContext ?? localDrawingContext;
706 
707  if (AdjustedPixelsPerTick.Equals(0.0))
708  SetValue(AdjustedPixelsPerTickPropertyKey, PixelsPerTick);
709 
710  actualWidth = ActualWidth;
711  actualHeight = ActualHeight;
712 
713  largeTickTopPosition = actualHeight * LargeTickTop;
714  largeTickBottomPosition = actualHeight * LargeTickBottom;
715  smallTickTopPosition = actualHeight * SmallTickTop;
716  smallTickBottomPosition = actualHeight * SmallTickBottom;
717 
718  largeTickPen = LargeTickPen;
719  smallTickPen = SmallTickPen;
720 
721  bool isSmallTickVisible = IsSmallTickVisible;
722 
723  textPositionOrigin = TextPositionOrigin;
724  textPosition = TextPosition;
725 
726  isTextVisible = IsTextVisible;
727  if (isTextVisible)
728  {
729  foreground = Foreground;
730  font = Font;
731  fontSize = FontSize;
732  }
733 
734  double adjustedUnitsPerTick = AdjustedUnitsPerTick;
735  double adjustedPixelsPerTick = AdjustedPixelsPerTick;
736  int decimalCountRounding = DecimalCountRounding;
737 
738  double currentUnit = (int)(StartUnit / adjustedUnitsPerTick) * adjustedUnitsPerTick;
739  double currentPixel = ((currentUnit - StartUnit) / adjustedUnitsPerTick) * adjustedPixelsPerTick;
740 
741  double smallIntevalLength = (1.0 / adjustedSmallIntervalPerTick);
742 
743  if (StartUnit >= 0.0)
744  {
745  currentPixel += adjustedPixelsPerTick;
746  currentUnit += adjustedUnitsPerTick;
747  currentUnit = Math.Round(currentUnit, decimalCountRounding);
748  }
749 
750  RaiseBeforeRenderEvent();
751 
752  drawingContext.DrawRectangle(Background, null, new Rect(0.0, 0.0, actualWidth, actualHeight));
753 
754  RaiseBeforeTicksRenderEvent(drawingContext);
755 
756  if (isSmallTickVisible)
757  {
758  for (int i = 0; i < adjustedSmallIntervalPerTick - 1; i++)
759  {
760  double smallLeft = currentPixel - ((i + 1) * adjustedPixelsPerTick) * smallIntevalLength;
761  if (smallLeft < 0.0)
762  break;
763  DrawSmallTick(drawingContext, smallLeft);
764  }
765  }
766 
767  if (currentPixel < 0.0)
768  {
769  currentPixel += adjustedPixelsPerTick * Math.Ceiling(Math.Abs(currentPixel) / adjustedPixelsPerTick);
770  }
771 
772  while (currentPixel < actualWidth)
773  {
774  DrawLargeTick(drawingContext, currentUnit, currentPixel + 1.0);
775 
776  if (isSmallTickVisible)
777  {
778  for (int i = 0; i < adjustedSmallIntervalPerTick - 1; i++)
779  {
780  double smallLeft = currentPixel + ((i + 1) * adjustedPixelsPerTick) * smallIntevalLength;
781  if (smallLeft > actualWidth)
782  break;
783  DrawSmallTick(drawingContext, smallLeft + 1.0);
784  }
785  }
786 
787  currentPixel += adjustedPixelsPerTick;
788  currentUnit += adjustedUnitsPerTick;
789  }
790 
791  RaiseAfterTicksRenderEvent(drawingContext);
792 
793  RaiseAfterRenderEvent();
794  }
795 
796  private static double AdjustUnitInterval(double value)
797  {
798  // computing cannot be done on negative values
799  bool negative = (value <= 0.0f);
800  if (negative)
801  value = -value;
802 
803  double log = Math.Log10(value);
804  double log0 = Math.Pow(10.0, Math.Floor(log));
805 
806  double result;
807 
808  log = value / log0;
809  if (log < (1.0f + 2.0f) * 0.5f) result = log0;
810  else if (log < (2.0f + 5.0f) * 0.5f) result = log0 * 2.0f;
811  else if (log < (5.0f + 10.0f) * 0.5f) result = log0 * 5.0f;
812  else result = log0 * 10.0f;
813 
814  if (negative)
815  result = -result;
816 
817  return result;
818  }
819 
820  private static bool IsCloser(double value, double other, double reference)
821  {
822  return Math.Abs(value - reference) < Math.Abs(other - reference);
823  }
824 
825  private static bool IsCloseEnoughToMultiply(List<double> sortedGroupings, double value, double target)
826  {
827  bool result = true;
828  int index = sortedGroupings.FindIndex(x => x.Equals(value));
829 
830  if (index > 0 && sortedGroupings[index - 1] > target)
831  result = false;
832 
833  if (index < sortedGroupings.Count - 1 && sortedGroupings[index + 1] < target)
834  result = false;
835 
836  return result;
837  }
838 
839  private void AdjustUnitIntervalWithUnitSystem(double value)
840  {
841  TickTextUnitDivider = 1.0;
842 
843  if (UnitSystem == null)
844  {
845  AdjustedUnitsPerTick = AdjustUnitInterval(value);
846  return;
847  }
848 
849  UnitSymbol = UnitSystem.Symbol;
850 
851  double scaledValue = value * 1.5;
852  double referenceUnitsPerTick = AdjustedUnitsPerTick;
853  bool hasResult = false;
854  var allGrouping = new List<double>();
855  UnitSystem.GetAllGroupingValues(ref allGrouping);
856  allGrouping.Sort();
857 
858  // Check if there is a grouping matching our value
859  foreach (UnitGrouping grouping in UnitSystem.GroupingValues)
860  {
861  if (!hasResult || IsCloser(grouping.LargeIntervalSize, referenceUnitsPerTick, scaledValue))
862  {
863  AdjustedUnitsPerTick = grouping.LargeIntervalSize;
864  referenceUnitsPerTick = AdjustedUnitsPerTick;
865  adjustedSmallIntervalPerTick = grouping.SmallIntervalCount;
866  hasResult = true;
867  }
868 
869  // If the grouping is multipliable using the default grouping method ({1/2/5}*10^n), check for a better value
870  if (grouping.IsMultipliable)
871  {
872  double val = AdjustUnitInterval(scaledValue / grouping.LargeIntervalSize) * grouping.LargeIntervalSize;
873 
874  if (IsCloseEnoughToMultiply(allGrouping, grouping.LargeIntervalSize, scaledValue) && IsCloser(val, referenceUnitsPerTick, scaledValue))
875  {
876  AdjustedUnitsPerTick = val;
877  referenceUnitsPerTick = grouping.LargeIntervalSize;
878  adjustedSmallIntervalPerTick = grouping.SmallIntervalCount;
879  }
880  }
881  }
882 
883  // When there is no grouping, use the default grouping method
884  if (UnitSystem.GroupingValues.Count == 0)
885  {
886  AdjustedUnitsPerTick = AdjustUnitInterval(scaledValue);
887  referenceUnitsPerTick = 1;
888  adjustedSmallIntervalPerTick = 10;
889  }
890 
891  // Check if a conversion may fit better our scale
892  foreach (UnitConversion conversion in UnitSystem.Conversions)
893  {
894  // Check if there is a grouping matching our value
895  foreach (UnitGrouping grouping in conversion.GroupingValues)
896  {
897  double groupingValue = grouping.LargeIntervalSize * conversion.Value;
898 
899  if (IsCloser(groupingValue, referenceUnitsPerTick, scaledValue))
900  {
901  AdjustedUnitsPerTick = groupingValue;
902  referenceUnitsPerTick = groupingValue;
903  adjustedSmallIntervalPerTick = grouping.SmallIntervalCount;
904  TickTextUnitDivider = conversion.Value;
905  UnitSymbol = conversion.Symbol;
906  }
907 
908  // If the grouping is multipliable using the default grouping method ({1/2/5}*10^n), check for a better value
909  if (grouping.IsMultipliable)
910  {
911  double val = AdjustUnitInterval(scaledValue / groupingValue) * groupingValue;
912 
913  if (IsCloseEnoughToMultiply(allGrouping, groupingValue, scaledValue) && IsCloser(val, referenceUnitsPerTick, scaledValue))
914  {
915  AdjustedUnitsPerTick = val;
916  referenceUnitsPerTick = groupingValue;
917  adjustedSmallIntervalPerTick = grouping.SmallIntervalCount;
918  TickTextUnitDivider = conversion.Value;
919  UnitSymbol = conversion.Symbol;
920  }
921  }
922  }
923 
924  // When there is no grouping, use the default grouping method
925  if (conversion.GroupingValues.Count == 0)
926  {
927  double val = conversion.Value;
928  bool canMultiply = true;
929  if (conversion.IsMultipliable)
930  {
931  canMultiply = IsCloseEnoughToMultiply(allGrouping, conversion.Value, scaledValue);
932  val *= AdjustUnitInterval(scaledValue / conversion.Value);
933  }
934  if (canMultiply && IsCloser(val, referenceUnitsPerTick, scaledValue))
935  {
936  AdjustedUnitsPerTick = val;
937  referenceUnitsPerTick = conversion.Value;
938  adjustedSmallIntervalPerTick = 10;
939  TickTextUnitDivider = conversion.Value;
940  UnitSymbol = conversion.Symbol;
941  }
942  }
943  }
944  }
945 
946  protected virtual void DrawLargeTick(DrawingContext drawingContext, double unit, double position)
947  {
948  if (isTextVisible)
949  {
950  string symbol = UnitSymbol ?? "";
951  double dividedUnit = !TickTextUnitDivider.Equals(0.0) ? unit / TickTextUnitDivider : unit;
952  dividedUnit = Math.Round(dividedUnit, 6);
953 
954  var ft = new FormattedText(dividedUnit + symbol, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, font, fontSize, foreground);
955  drawingContext.DrawText(ft, new Point(position - ft.Width * textPositionOrigin.X, (textPosition * actualHeight) - (ft.Height * textPositionOrigin.Y)));
956  }
957 
958  drawingContext.DrawLine(largeTickPen, new Point(position, largeTickTopPosition), new Point(position, largeTickBottomPosition));
959  }
960 
961  protected virtual void DrawSmallTick(DrawingContext drawingContext, double position)
962  {
963  drawingContext.DrawLine(smallTickPen, new Point(position, smallTickTopPosition), new Point(position, smallTickBottomPosition));
964  }
965 
966  protected override void OnMouseWheel(MouseWheelEventArgs e)
967  {
968  base.OnMouseWheel(e);
969 
970  if (IsZoomingOnMouseWheel)
971  {
972  double coeficient = e.Delta >= 0.0 ? MouseWheelZoomCoeficient : 1.0 / MouseWheelZoomCoeficient;
973  Point pos = e.GetPosition(this);
974 
975  ZoomAtPosition(pos.X, coeficient, Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift));
976  }
977 
978  e.Handled = true;
979  }
980 
981  public void ZoomAtPosition(double position, double coeficient, bool affectPixelsPerTick)
982  {
983  if (affectPixelsPerTick)
984  SetPixelsPerTickAt(PixelsPerTick * coeficient, position);
985  else
986  SetUnitsPerTickAt(UnitsPerTick / coeficient, position);
987  }
988 
989  private bool isDraggingScale;
990 
991  public bool StartDraggingScale()
992  {
993  if (isDraggingScale)
994  return true;
995 
996  isDraggingScale = CaptureMouse();
997 
998  mouseDelta = Mouse.GetPosition(this);
999  return isDraggingScale;
1000  }
1001 
1002  public bool EndDraggingScale()
1003  {
1004  if (!isDraggingScale)
1005  return true;
1006 
1007  isDraggingScale = !Mouse.Capture(null);
1008  return !isDraggingScale;
1009  }
1010 
1011  private Point mouseDelta;
1012 
1013  protected override void OnMouseMove(MouseEventArgs e)
1014  {
1015  if (isDraggingScale)
1016  {
1017  Vector delta = e.GetPosition(this) - mouseDelta;
1018  mouseDelta = e.GetPosition(this);
1019  StartUnit -= delta.X / PixelsPerUnit;
1020  }
1021  }
1022 
1023  protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
1024  {
1025  if (IsDraggingOnLeftMouseButton)
1026  {
1027  StartDraggingScale();
1028  }
1029  }
1030 
1031  protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
1032  {
1033  if (IsDraggingOnLeftMouseButton)
1034  {
1035  EndDraggingScale();
1036  }
1037  }
1038 
1039  protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
1040  {
1041  if (IsDraggingOnRightMouseButton)
1042  {
1043  StartDraggingScale();
1044  }
1045  }
1046 
1047  protected override void OnMouseRightButtonUp(MouseButtonEventArgs e)
1048  {
1049  if (IsDraggingOnRightMouseButton)
1050  {
1051  EndDraggingScale();
1052  }
1053  }
1054  }
1055 }
void SetPixelsPerTickAt(double pixelsPerTick, double pixel)
Definition: ScaleBar.cs:676
delegate void CustomRenderRoutedEventHandler(object sender, CustomRenderRoutedEventArgs e)
override void OnMouseRightButtonDown(MouseButtonEventArgs e)
Definition: ScaleBar.cs:1039
virtual void DrawSmallTick(DrawingContext drawingContext, double position)
Definition: ScaleBar.cs:961
void ZoomAtPosition(double position, double coeficient, bool affectPixelsPerTick)
Definition: ScaleBar.cs:981
override void OnMouseWheel(MouseWheelEventArgs e)
Definition: ScaleBar.cs:966
SharpDX.DirectWrite.Font Font
override void OnRender(DrawingContext localDrawingContext)
Definition: ScaleBar.cs:703
CustomRenderRoutedEventArgs(RoutedEvent routedEvent, DrawingContext drawingContext)
Definition: ScaleBar.cs:18
override void OnMouseRightButtonUp(MouseButtonEventArgs e)
Definition: ScaleBar.cs:1047
override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
Definition: ScaleBar.cs:1023
delegate void RoutedDependencyPropertyChangedEventHandler(object sender, RoutedDependencyPropertyChangedEventArgs e)
Represent an unit system that can be used with a ScaleBar
Definition: UnitSystem.cs:12
virtual void DrawLargeTick(DrawingContext drawingContext, double unit, double position)
Definition: ScaleBar.cs:946
override void OnMouseMove(MouseEventArgs e)
Definition: ScaleBar.cs:1013
RoutedDependencyPropertyChangedEventArgs(RoutedEvent routedEvent, DependencyProperty dependencyProperty, object oldValue, object newValue)
Definition: ScaleBar.cs:33
void SetUnitAt(double unit, double pixel)
Definition: ScaleBar.cs:663
override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
Definition: ScaleBar.cs:1031
System.Windows.Point Point
Definition: ColorPicker.cs:15
void SetUnitsPerTickAt(double unitsPerTick, double pixel)
Definition: ScaleBar.cs:669