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
<using RealTimeGraphEx.Controllers;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace RealTimeGraphEx.ReachGraphs
{
public class RealTimeGraphExReachVUMeter : RealTimeGraphExBase
{
#region Protected Fields
protected int updateCounter;
protected Rectangle rectangle;
protected double lastValue;
#endregion
#region Cross Thread Fields
protected Brush _stroke;
protected Brush _fill;
protected GraphController _graphController;
#endregion
#region Properties
/// <summary>
/// Gets or sets the graph strokes color.
/// </summary>
public Brush Stroke
{
get { return (Brush)GetValue(StrokeProperty); }
set { SetValue(StrokeProperty, value); }
}
public static readonly DependencyProperty StrokeProperty =
DependencyProperty.Register("Stroke", typeof(Brush), typeof(RealTimeGraphExReachVUMeter), new PropertyMetadata(Brushes.Black, new PropertyChangedCallback(CrossModelChanged)));
/// <summary>
/// Gets or sets the graph fill color.
/// </summary>
public Brush Fill
{
get { return (Brush)GetValue(FillProperty); }
set { SetValue(FillProperty, value); }
}
public static readonly DependencyProperty FillProperty =
DependencyProperty.Register("Fill", typeof(Brush), typeof(RealTimeGraphExReachVUMeter), new PropertyMetadata(Brushes.Gray, new PropertyChangedCallback(CrossModelChanged)));
/// <summary>
/// Gets or sets the IDataSeries used to push data points to the graph.
/// </summary>
public GraphController Controller
{
get { return (GraphController)GetValue(ControllerProperty); }
set { SetValue(ControllerProperty, value); }
}
public static readonly DependencyProperty ControllerProperty =
DependencyProperty.Register("Controller", typeof(GraphController), typeof(RealTimeGraphExReachVUMeter), new PropertyMetadata(null, new PropertyChangedCallback(GraphControllerChanged)));
private static void GraphControllerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as RealTimeGraphExReachVUMeter;
if (control.Controller != null)
{
control.Controller.RegisterMethods(control.ClearGraph, control.StartPushThread, control.SetPaused,control.ChangeRenderMode,null);
control._graphController = control.Controller;
}
}
/// <summary>
/// Gets or sets the collection of VU range data determines how to fill the VU graph.
/// </summary>
public ObservableCollection<Models.RangeData> Ranges
{
get { return (ObservableCollection<Models.RangeData>)GetValue(RangesProperty); }
set { SetValue(RangesProperty, value); }
}
public static readonly DependencyProperty RangesProperty =
DependencyProperty.Register("Ranges", typeof(ObservableCollection<Models.RangeData>), typeof(RealTimeGraphExReachVUMeter), new PropertyMetadata(null));
/// <summary>
/// Gets or sets whether to animate the VU when when it's value doens not change.
/// </summary>
public bool Animate
{
get { return (bool)GetValue(AnimateProperty); }
set { SetValue(AnimateProperty, value); }
}
public static readonly DependencyProperty AnimateProperty =
DependencyProperty.Register("Animate", typeof(bool), typeof(RealTimeGraphExReachVUMeter), new PropertyMetadata(false));
/// <summary>
/// Gets or sets the height value to animate when the Animate property is set to true.
/// </summary>
public double AnimationAmount
{
get { return (double)GetValue(AnimationAmountProperty); }
set { SetValue(AnimationAmountProperty, value); }
}
public static readonly DependencyProperty AnimationAmountProperty =
DependencyProperty.Register("AnimationAmount", typeof(double), typeof(RealTimeGraphExReachVUMeter), new PropertyMetadata(10.0));
/// <summary>
/// Gets or sets the duration of the animation when the Animate property is set to true.
/// </summary>
public Duration AnimationDuration
{
get { return (Duration)GetValue(AnimationDurationProperty); }
set { SetValue(AnimationDurationProperty, value); }
}
public static readonly DependencyProperty AnimationDurationProperty =
DependencyProperty.Register("AnimationDuration", typeof(Duration), typeof(RealTimeGraphExReachVUMeter), new PropertyMetadata(new Duration(TimeSpan.FromMilliseconds(500))));
#endregion
#region Constructors
public RealTimeGraphExReachVUMeter()
: base()
{
Ranges = new ObservableCollection<Models.RangeData>();
}
#endregion
#region Override Methods
protected override void Initialize()
{
base.Initialize();
if (rectangle == null)
{
rectangle = new Rectangle() { Tag = "Default" };
rectangle.Height = 0;
}
gridLinesAndImageWrapperGrid.Children.Remove(img);
if (!gridLinesAndImageWrapperGrid.Children.Contains(rectangle))
{
gridLinesAndImageWrapperGrid.Children.Add(rectangle);
}
//TODO: Base on developer selection vertical/horizontal VU.
rectangle.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
rectangle.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
}
protected override void OnSetCrossThreadFields()
{
base.OnSetCrossThreadFields();
this.Dispatcher.Invoke(() =>
{
_graphController = Controller;
_stroke = Stroke;
_fill = Fill;
if (rectangle.Tag != null && rectangle.Tag.ToString() == "Default")
{
rectangle.Fill = Fill;
rectangle.Stroke = Stroke;
}
if (_graphController != null && _graphController.dataSeries != null && _graphController.dataSeries.useFillandStroke)
{
rectangle.Fill = _graphController.dataSeries.fill;
rectangle.Stroke = _graphController.dataSeries.stroke;
}
}, DispatcherPriority.Send);
}
protected override void OnClearGraph()
{
base.OnClearGraph();
this.Dispatcher.Invoke(() =>
{
rectangle.BeginAnimation(Rectangle.HeightProperty, null);
rectangle.Height = 0;
});
}
protected internal override void OnRenderGraph()
{
if (_graphController != null && _graphController.dataSeries.Points != null && _graphController.dataSeries.Points.Count > 0 && _width > 1 && _height > 1)
{
double value = _graphController.dataSeries.Points[_graphController.dataSeries.Points.Count - 1]; //Get last value.
if (value == lastValue)
{
_graphController.ClearPoints();
return;
}
lastValue = value;
double valuePrecentage = ConvertYToImageY(value);
updateCounter++;
if (updateCounter >= 1 && !_isPaused)
{
updateCounter = 0;
OnDrawVisuals(valuePrecentage);
}
_graphController.ClearPoints();
}
}
#endregion
#region Virtual Methods
protected virtual void OnDrawVisuals(double valuePrecentage)
{
this.Dispatcher.Invoke(() =>
{
rectangle.BeginAnimation(Rectangle.HeightProperty, null);
rectangle.Height = valuePrecentage;
rectangle.Fill = GetRectangleBrush();
if (Animate)
{
DoubleAnimation ani = new DoubleAnimation();
ani.CurrentTimeInvalidated += (x, y) =>
{
rectangle.Fill = GetRectangleBrush();
};
ani.To = rectangle.Height - AnimationAmount;
ani.From = rectangle.Height;
ani.RepeatBehavior = RepeatBehavior.Forever;
ani.AutoReverse = true;
ani.Duration = AnimationDuration;
rectangle.BeginAnimation(HeightProperty, ani);
}
});
}
private Brush GetRectangleBrush()
{
if (Ranges != null && Ranges.Count > 0)
{
LinearGradientBrush brush = new LinearGradientBrush();
brush.MappingMode = BrushMappingMode.Absolute;
brush.StartPoint = new Point(0, _height - rectangle.Height);
brush.EndPoint = new Point(0, (rectangle.Height * -1));
brush.Transform = new RotateTransform() { Angle = 180 };
for (int i = Ranges.Count - 1; i >= 0; i--)
{
var range = Ranges[i];
double rangeValuePrecentage = ConvertYToImageY(range.Value);
GradientStop stop = new GradientStop();
stop.Color = range.Color;
stop.Offset = ((rangeValuePrecentage * 100) / _height) / 100;
brush.GradientStops.Add(stop);
}
return brush;
}
else
{
return Fill;
}
}
#endregion
}
}
|