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 RealTimeGraphExReachCircle : RealTimeGraphExBase
{
#region Protected Fields
protected int updateCounter;
protected Ellipse ellipse;
protected double lastValue;
#endregion
#region Cross Thread Fields
protected Brush _stroke;
protected Brush _fill;
protected GraphController _graphController;
#endregion
#region Properties
///
/// Gets or sets the graph strokes color.
///
public Brush Stroke
{
get { return (Brush)GetValue(StrokeProperty); }
set { SetValue(StrokeProperty, value); }
}
public static readonly DependencyProperty StrokeProperty =
DependencyProperty.Register("Stroke", typeof(Brush), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(Brushes.Black, new PropertyChangedCallback(CrossModelChanged)));
///
/// Gets or sets the graph fill color.
///
public Brush Fill
{
get { return (Brush)GetValue(FillProperty); }
set { SetValue(FillProperty, value); }
}
public static readonly DependencyProperty FillProperty =
DependencyProperty.Register("Fill", typeof(Brush), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(Brushes.Gray, new PropertyChangedCallback(CrossModelChanged)));
///
/// Gets or sets the IDataSeries used to push data points to the graph.
///
public GraphController Controller
{
get { return (GraphController)GetValue(ControllerProperty); }
set { SetValue(ControllerProperty, value); }
}
public static readonly DependencyProperty ControllerProperty =
DependencyProperty.Register("Controller", typeof(GraphController), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(null, new PropertyChangedCallback(GraphControllerChanged)));
private static void GraphControllerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as RealTimeGraphExReachCircle;
if (control.Controller != null)
{
control.Controller.RegisterMethods(control.ClearGraph, control.StartPushThread, control.SetPaused, control.ChangeRenderMode, null);
control._graphController = control.Controller;
}
}
///
/// Gets or sets the collection of VU range data determines how to fill the VU graph.
///
public ObservableCollection Ranges
{
get { return (ObservableCollection)GetValue(RangesProperty); }
set { SetValue(RangesProperty, value); }
}
public static readonly DependencyProperty RangesProperty =
DependencyProperty.Register("Ranges", typeof(ObservableCollection), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(null));
///
/// Gets or sets whether to animate the circle when when it's value doens not change.
///
public bool Animate
{
get { return (bool)GetValue(AnimateProperty); }
set { SetValue(AnimateProperty, value); }
}
public static readonly DependencyProperty AnimateProperty =
DependencyProperty.Register("Animate", typeof(bool), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(false));
///
/// Gets or sets the height value to animate when the Animate property is set to true.
///
public double AnimationAmount
{
get { return (double)GetValue(AnimationAmountProperty); }
set { SetValue(AnimationAmountProperty, value); }
}
public static readonly DependencyProperty AnimationAmountProperty =
DependencyProperty.Register("AnimationAmount", typeof(double), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(10.0));
///
/// Gets or sets the duration of the animation when the Animate property is set to true.
///
public Duration AnimationDuration
{
get { return (Duration)GetValue(AnimationDurationProperty); }
set { SetValue(AnimationDurationProperty, value); }
}
public static readonly DependencyProperty AnimationDurationProperty =
DependencyProperty.Register("AnimationDuration", typeof(Duration), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(new Duration(TimeSpan.FromMilliseconds(500))));
#endregion
#region Constructors
public RealTimeGraphExReachCircle()
: base()
{
Ranges = new ObservableCollection();
}
#endregion
#region Override Methods
protected override void Initialize()
{
base.Initialize();
if (ellipse == null)
{
ellipse = new Ellipse() { Tag = "Default" };
ellipse.Height = 0;
}
gridLinesAndImageWrapperGrid.Children.Remove(img);
if (!gridLinesAndImageWrapperGrid.Children.Contains(ellipse))
{
gridLinesAndImageWrapperGrid.Children.Add(ellipse);
}
//TODO: Base on developer selection vertical/horizontal VU.
ellipse.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
ellipse.VerticalAlignment = System.Windows.VerticalAlignment.Center;
}
protected override void OnSetCrossThreadFields()
{
base.OnSetCrossThreadFields();
this.Dispatcher.Invoke(() =>
{
_graphController = Controller;
_stroke = Stroke;
_fill = Fill;
if (ellipse.Tag != null && ellipse.Tag.ToString() == "Default")
{
ellipse.Fill = Fill;
ellipse.Stroke = Stroke;
}
if (_graphController != null && _graphController.dataSeries != null && _graphController.dataSeries.useFillandStroke)
{
ellipse.Fill = _graphController.dataSeries.fill;
ellipse.Stroke = _graphController.dataSeries.stroke;
}
}, DispatcherPriority.Send);
}
protected override void OnClearGraph()
{
base.OnClearGraph();
this.Dispatcher.Invoke(() =>
{
ellipse.BeginAnimation(WidthProperty, null);
ellipse.BeginAnimation(HeightProperty, null);
ellipse.Height = 0;
ellipse.Width = 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 valueHeightPrecentage = ConvertYToImageY(value);
double valueWidthPrecentage = ConvertYToImageX(value);
updateCounter++;
if (updateCounter >= 1 && !_isPaused)
{
updateCounter = 0;
OnDrawVisuals(valueHeightPrecentage, valueWidthPrecentage);
}
_graphController.ClearPoints();
}
}
#endregion
#region Virtual Methods
protected virtual void OnDrawVisuals(double valueHeightPrecentage, double valueWidthPrecentage)
{
this.Dispatcher.Invoke(() =>
{
ellipse.BeginAnimation(WidthProperty, null);
ellipse.BeginAnimation(HeightProperty, null);
ellipse.Height = valueHeightPrecentage;
ellipse.Width = valueWidthPrecentage;
ellipse.Fill = GetEllipseBrush();
if (Animate)
{
DoubleAnimation ani = new DoubleAnimation();
ani.CurrentTimeInvalidated += (x, y) =>
{
ellipse.Fill = GetEllipseBrush();
};
ani.To = ellipse.Width - AnimationAmount;
ani.From = ellipse.Width;
ani.RepeatBehavior = RepeatBehavior.Forever;
ani.AutoReverse = true;
ani.Duration = AnimationDuration;
ellipse.BeginAnimation(WidthProperty, ani);
ellipse.BeginAnimation(HeightProperty, ani);
}
});
}
private Brush GetEllipseBrush()
{
if (Ranges != null && Ranges.Count > 0)
{
RadialGradientBrush rangesBrush = new RadialGradientBrush();
rangesBrush.MappingMode = BrushMappingMode.Absolute;
rangesBrush.Center = new Point(ellipse.Width / 2, ellipse.Height / 2);
rangesBrush.GradientOrigin = new Point(ellipse.Width / 2, ellipse.Height / 2);
rangesBrush.RadiusX = _width;
rangesBrush.RadiusY = _height;
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;
rangesBrush.GradientStops.Add(stop);
}
return rangesBrush;
}
return Fill;
}
#endregion
}
}