#if WPF using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Media.Animation; #else using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Collections.Generic; #endif using Visifire.Commons; namespace Visifire.Charts { /// /// Visifire.Charts.ChartGrid class. It contains grids of axis. /// public class ChartGrid : ObservableObject { #region Public Methods /// /// Initializes a new instance of the Visifire.Charts.ChartGrid class /// public ChartGrid() { // Apply default style from generic #if WPF if (!_defaultStyleKeyApplied) { DefaultStyleKeyProperty.OverrideMetadata(typeof(ChartGrid), new FrameworkPropertyMetadata(typeof(ChartGrid))); _defaultStyleKeyApplied = true; } #else DefaultStyleKey = typeof(ChartGrid); #endif } #endregion #region Public Properties /// /// Identifies the Visifire.Charts.ChartGrid.Interval dependency property. /// /// /// The identifier for the Visifire.Charts.ChartGrid.Interval dependency property. /// public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register ("Interval", typeof(Nullable), typeof(ChartGrid), new PropertyMetadata(OnIntervalPropertyChanged)); /// /// Identifies the Visifire.Charts.ChartGrid.Enabled dependency property. /// /// /// The identifier for the Visifire.Charts.ChartGrid.Enabled dependency property. /// public static readonly DependencyProperty EnabledProperty = DependencyProperty.Register ("Enabled", typeof(Nullable), typeof(ChartGrid), new PropertyMetadata(OnEnabledPropertyChanged)); /// /// Identifies the Visifire.Charts.ChartGrid.LineColor dependency property. /// /// /// The identifier for the Visifire.Charts.ChartGrid.LineColor dependency property. /// public static readonly DependencyProperty LineColorProperty = DependencyProperty.Register ("LineColor", typeof(Brush), typeof(ChartGrid), new PropertyMetadata(OnLineColorPropertyChanged)); /// /// Identifies the Visifire.Charts.ChartGrid.LineStyle dependency property. /// /// /// The identifier for the Visifire.Charts.ChartGrid.LineStyle dependency property. /// public static readonly DependencyProperty LineStyleProperty = DependencyProperty.Register ("LineStyle", typeof(LineStyles), typeof(ChartGrid), new PropertyMetadata(OnLineStylePropertyChanged)); /// /// Identifies the Visifire.Charts.ChartGrid.LineThickness dependency property. /// /// /// The identifier for the Visifire.Charts.ChartGrid.LineThickness dependency property. /// public static readonly DependencyProperty LineThicknessProperty = DependencyProperty.Register ("LineThickness", typeof(Double), typeof(ChartGrid), new PropertyMetadata(0.25, OnLineThicknessPropertyChanged)); /// /// Identifies the Visifire.Charts.ChartGrid.InterlacedColor dependency property. /// /// /// The identifier for the Visifire.Charts.ChartGrid.InterlacedColor dependency property. /// public static readonly DependencyProperty InterlacedColorProperty = DependencyProperty.Register( "InterlacedColor", typeof(Brush), typeof(ChartGrid), new PropertyMetadata(OnInterlacedColorPropertyChanged)); /// /// Get or set the grid interval /// #if SL [System.ComponentModel.TypeConverter(typeof(Converters.NullableDoubleConverter))] #endif public Nullable Interval { get { if ((Nullable)GetValue(IntervalProperty) == null && ParentAxis != null) return ParentAxis.InternalInterval; else return (Nullable)GetValue(IntervalProperty); } set { SetValue(IntervalProperty, value); } } /// /// ToolTipText property /// ( NotImplemented ) /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public override String ToolTipText { get { throw new NotImplementedException("ToolTipText property for ChartGrid is not implemented"); } set { throw new NotImplementedException("ToolTipText property for ChartGrid is not implemented"); } } /// /// Get or set the grid line style /// public LineStyles LineStyle { get { return (LineStyles)GetValue(LineStyleProperty); } set { SetValue(LineStyleProperty, value); } } /// /// Get or set the grid interlaced color /// public Brush InterlacedColor { get { return (Brush)GetValue(InterlacedColorProperty); } set { SetValue(InterlacedColorProperty, value); } } /// /// Get or set the grid line thickness /// public Double LineThickness { get { return (Double)GetValue(LineThicknessProperty); } set { SetValue(LineThicknessProperty, value); } } /// /// Enables or disables grid lines /// [System.ComponentModel.TypeConverter(typeof(NullableBoolConverter))] public Nullable Enabled { get { if ((Nullable)GetValue(EnabledProperty) == null) return true; else return (Nullable)GetValue(EnabledProperty); } set { SetValue(EnabledProperty, value); } } /// /// Get or set the grid line color /// public Brush LineColor { get { return (GetValue(LineColorProperty) != null) ? (Brush)GetValue(LineColorProperty) : new SolidColorBrush(Colors.Gray); } set { SetValue(LineColorProperty, value); } } #endregion #region Public Events #endregion #region Protected Methods #endregion #region Internal Properties /// /// Visual element for grid /// internal Canvas Visual { get; private set; } /// /// Actual minimum value of the axis /// internal Double Minimum { get; set; } /// /// Actual maximum value of the axis /// internal Double Maximum { get; set; } /// /// Minimum data value for the axis /// internal Double DataMinimum { get; set; } /// /// Maximum data value for the axis /// internal Double DataMaximum { get; set; } /// /// Get or set the width of the grid canvas, will be used only with the Horizontal axis /// internal new Double Width { get { return _width; } set { _width = value; } } /// /// Get or set the height of the grid canvas, will be used ony with the vertical axis /// internal new Double Height { get { return _height; } set { _height = value; } } /// /// Placement decides how the grids have to be positioned /// internal PlacementTypes Placement { get; set; } /// /// Parent axis reference /// internal Axis ParentAxis { get; set; } /// /// Chart grid storyboard /// internal Storyboard Storyboard { get; set; } #endregion #region Private Delegates #endregion #region Private Methods /// /// Creates grids and also position them appropriately /// /// Whether animation is enabled /// Animation duration private void CreateAndPositionChartGrid(bool animationEnabled, Double animationDuration) { Double interval = (Double)Interval; // Interval for the chart grid Decimal index =0;// = (Decimal)Minimum; // starting point for the loop that generates grids Decimal minVal = (Decimal)Minimum; // smallest value from where the grid must be drawn Decimal maxVal = (Decimal)Maximum; // largest value from where the grid must be drawn // gap between two intervals Decimal gap = (Decimal)interval;// +(((Nullable)GetValue(IntervalProperty) == null) ? ParentAxis.SkipOffset : 0); //Int32 count = 0; // counts the number of lines required for alternate colored bands Int32 countRectangles = 0; // counts the number of color bands for animating them alternately in opposite direction Double position = 0; // value of the line position for the running loop cycle Double prevPosition = 0; // value of the line position for the previous position // if axisX and the first data point is in a gap between the prescribed interval, the index must dateTime from the // datapoint rather than the axis minimum // if ((DataMinimum - interval) < Minimum && ParentAxis.AxisRepresentation == AxisRepresentations.AxisX) // index = (Decimal)DataMinimum; if (ParentAxis.AxisRepresentation == AxisRepresentations.AxisX) { if (Double.IsNaN((Double)ParentAxis.AxisMinimumNumeric)) { if (ParentAxis.XValueType != ChartValueTypes.Numeric) { minVal = (Decimal)ParentAxis.FirstLabelPosition; } else { if ((DataMinimum - Minimum) / interval >= 1) minVal = (Decimal)(DataMinimum - Math.Floor((DataMinimum - Minimum) / interval) * interval); else minVal = (Decimal)DataMinimum; } } } //index = minval; // maxVal = maxVal + gap / 1000; #if WPF if (Storyboard != null && Storyboard.GetValue(Storyboard.TargetProperty) != null) Storyboard.Stop(); #else if (Storyboard != null) Storyboard.Stop(); #endif InterlacedRectangles = new List(); InterlacedLines = new List(); if (minVal != maxVal) { InterlacedRectangles = new List(); Decimal xValue; for (xValue = minVal; xValue <= maxVal; ) { Line line = new Line(); InterlacedLines.Add(line); line.Stroke = LineColor; line.StrokeThickness = LineThickness; line.StrokeDashArray = ExtendedGraphics.GetDashArray(LineStyle); line.Width = Width; line.Height = Height; switch (Placement) { case PlacementTypes.Top: case PlacementTypes.Bottom: position = Graphics.ValueToPixelPosition(0, Width, Minimum, Maximum, (Double)xValue); line.X1 = position; line.X2 = position; line.Y1 = 0; line.Y2 = Height; if (index % 2 == 1) { Rectangle rectangle = new Rectangle(); if (animationEnabled) { ScaleTransform scaleTransform; if (countRectangles % 2 == 0) { scaleTransform = new ScaleTransform() { ScaleX = 1, ScaleY = 0 }; rectangle.RenderTransformOrigin = new Point(0.5, 1); Storyboard.Children.Add(CreateDoubleAnimation(scaleTransform, "(ScaleTransform.ScaleY)", 0, 1, 0.5, animationDuration)); } else { scaleTransform = new ScaleTransform() { ScaleX = 1, ScaleY = 0 }; rectangle.RenderTransformOrigin = new Point(0.5, 0); Storyboard.Children.Add(CreateDoubleAnimation(scaleTransform, "(ScaleTransform.ScaleY)", 0, 1, 0.5, animationDuration)); } rectangle.RenderTransform = scaleTransform; } countRectangles++; rectangle.Width = Math.Abs(position - prevPosition); rectangle.Height = Height; rectangle.Fill = InterlacedColor; rectangle.SetValue(Canvas.LeftProperty, prevPosition); rectangle.SetValue(Canvas.TopProperty, (Double)0); rectangle.SetValue(Canvas.ZIndexProperty, (Int32)(-countRectangles)); Visual.Children.Add(rectangle); InterlacedRectangles.Add(rectangle); } break; case PlacementTypes.Left: case PlacementTypes.Right: position = Graphics.ValueToPixelPosition(Height, 0, Minimum, Maximum, (Double)xValue); if (position == 0) position += this.LineThickness; line.X1 = 0; line.X2 = Width; line.Y1 = position; line.Y2 = position; if (index % 2 == 1) { Rectangle rectangle = new Rectangle(); if (animationEnabled) { ScaleTransform scaleTransform; if (countRectangles % 2 == 0) { scaleTransform = new ScaleTransform() { ScaleX = 0, ScaleY = 1 }; rectangle.RenderTransformOrigin = new Point(0, 0.5); Storyboard.Children.Add(CreateDoubleAnimation(scaleTransform, "(ScaleTransform.ScaleX)", 0, 1, 0.5, animationDuration)); } else { scaleTransform = new ScaleTransform() { ScaleX = 0, ScaleY = 1 }; rectangle.RenderTransformOrigin = new Point(1, 0.5); Storyboard.Children.Add(CreateDoubleAnimation(scaleTransform, "(ScaleTransform.ScaleX)", 0, 1, 0.5, animationDuration)); } rectangle.RenderTransform = scaleTransform; } countRectangles++; rectangle.Width = Width; rectangle.Height = Math.Abs(position - prevPosition); rectangle.Fill = InterlacedColor; rectangle.SetValue(Canvas.LeftProperty, (Double)0); rectangle.SetValue(Canvas.TopProperty, position); rectangle.SetValue(Canvas.ZIndexProperty, (Int32)(-countRectangles)); Visual.Children.Add(rectangle); InterlacedRectangles.Add(rectangle); } break; } Visual.Children.Add(line); prevPosition = position; index += (ParentAxis.SkipOffset + 1); if (ParentAxis.IsDateTimeAxis) { DateTime dt = DateTimeHelper.UpdateDate(ParentAxis.FirstLabelDate, (Double)(index * gap), ParentAxis.InternalIntervalType); Decimal oneUnit = (Decimal)DateTimeHelper.DateDiff(dt, ParentAxis.FirstLabelDate, ParentAxis.MinDateRange, ParentAxis.MaxDateRange, ParentAxis.InternalIntervalType, ParentAxis.XValueType); xValue = minVal + oneUnit; } else { xValue = minVal + index * gap; } } if (index % 2 == 1) { Rectangle rectangle = new Rectangle(); ScaleTransform scaleTransform = null; switch (Placement) { case PlacementTypes.Top: case PlacementTypes.Bottom: rectangle.Width = Math.Abs(Width - position); rectangle.Height = Height; if (animationEnabled) { if (countRectangles % 2 == 0) { scaleTransform = new ScaleTransform() { ScaleX = 1, ScaleY = 0 }; rectangle.RenderTransformOrigin = new Point(0.5, 1); Storyboard.Children.Add(CreateDoubleAnimation(scaleTransform, "(ScaleTransform.ScaleY)", 0, 1, 0.5, animationDuration)); } else { scaleTransform = new ScaleTransform() { ScaleX = 1, ScaleY = 0 }; rectangle.RenderTransformOrigin = new Point(0.5, 0); Storyboard.Children.Add(CreateDoubleAnimation(scaleTransform, "(ScaleTransform.ScaleY)", 0, 1, 0.5, animationDuration)); } } rectangle.SetValue(Canvas.LeftProperty, position); rectangle.SetValue(Canvas.TopProperty, (Double)0); break; case PlacementTypes.Left: case PlacementTypes.Right: rectangle.Width = Width; rectangle.Height = Math.Abs(position); if (animationEnabled) { if (countRectangles % 2 == 0) { scaleTransform = new ScaleTransform() { ScaleX = 0, ScaleY = 1 }; rectangle.RenderTransformOrigin = new Point(0, 0.5); Storyboard.Children.Add(CreateDoubleAnimation(scaleTransform, "(ScaleTransform.ScaleX)", 0, 1, 0.5, animationDuration)); } else { scaleTransform = new ScaleTransform() { ScaleX = 0, ScaleY = 1 }; rectangle.RenderTransformOrigin = new Point(1, 0.5); Storyboard.Children.Add(CreateDoubleAnimation(scaleTransform, "(ScaleTransform.ScaleX)", 0, 1, 0.5, animationDuration)); } } rectangle.SetValue(Canvas.LeftProperty, (Double)0); rectangle.SetValue(Canvas.TopProperty, (Double)0); break; } rectangle.RenderTransform = scaleTransform; rectangle.Fill = InterlacedColor; rectangle.SetValue(Canvas.ZIndexProperty, (Int32)(-countRectangles)); Visual.Children.Add(rectangle); InterlacedRectangles.Add(rectangle); } } Visual.Width = Width; Visual.Height = Height; } /// /// Generates a Double animation sequence with fixed parameters /// /// Animation target object /// Animation target property /// Start value /// End value /// Animation begin time /// Animation duration /// DoubleAnimationUsingKeyFrames private DoubleAnimationUsingKeyFrames CreateDoubleAnimation(DependencyObject target, String property, Double from, Double to, Double begin, Double duration) { DoubleAnimationUsingKeyFrames da = new DoubleAnimationUsingKeyFrames(); da.BeginTime = TimeSpan.FromSeconds(begin); #if WPF target.SetValue(NameProperty, target.GetType().Name + Guid.NewGuid().ToString().Replace('-', '_')); Storyboard.SetTargetName(da, target.GetValue(NameProperty).ToString()); Chart._rootElement.RegisterName((string)target.GetValue(NameProperty), target); #else Storyboard.SetTarget(da, target); #endif Storyboard.SetTargetProperty(da, new PropertyPath(property)); SplineDoubleKeyFrame keyFrame = new SplineDoubleKeyFrame(); keyFrame.KeySpline = new KeySpline() { ControlPoint1 = new Point(0, 0), ControlPoint2 = new Point(1, 1) }; keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)); keyFrame.Value = from; da.KeyFrames.Add(keyFrame); keyFrame = new SplineDoubleKeyFrame(); keyFrame.KeySpline = new KeySpline() { ControlPoint1 = new Point(0.5, 0), ControlPoint2 = new Point(0.5, 1) }; keyFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(duration)); keyFrame.Value = to; da.KeyFrames.Add(keyFrame); return da; } /// /// IntervalProperty changed call back function /// /// DependencyObject /// DependencyPropertyChangedEventArgs private static void OnIntervalPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ChartGrid chartGrid = d as ChartGrid; chartGrid.FirePropertyChanged("Interval"); } /// /// EnabledProperty changed call back function /// /// DependencyObject /// DependencyPropertyChangedEventArgs private static void OnEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ChartGrid chartGrid = d as ChartGrid; chartGrid.FirePropertyChanged("Enabled"); } /// /// LineColorProperty changed call back function /// /// DependencyObject /// DependencyPropertyChangedEventArgs private static void OnLineColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ChartGrid chartGrid = d as ChartGrid; chartGrid.UpdateVisual("LineColor", e.NewValue); } /// /// LineStyleProperty changed call back function /// /// DependencyObject /// DependencyPropertyChangedEventArgs private static void OnLineStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ChartGrid chartGrid = d as ChartGrid; chartGrid.UpdateVisual("LineStyle", e.NewValue); } /// /// LineThicknessProperty changed call back function /// /// DependencyObject /// DependencyPropertyChangedEventArgs private static void OnLineThicknessPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ChartGrid chartGrid = d as ChartGrid; chartGrid.UpdateVisual("LineThickness", e.NewValue); } /// /// InterlacedColorProperty changed call back function /// /// DependencyObject /// DependencyPropertyChangedEventArgs private static void OnInterlacedColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ChartGrid chartGrid = d as ChartGrid; chartGrid.UpdateVisual("InterlacedColor", e.NewValue); } #endregion #region Private Properties /// /// Identifies the Visifire.Charts.ChartGrid.ToolTipText dependency property. /// /// /// The identifier for the Visifire.Charts.ChartGrid.ToolTipText dependency property. /// private new static readonly DependencyProperty ToolTipTextProperty = DependencyProperty.Register ("ToolTipText", typeof(String), typeof(ChartGrid), null); /// /// List of interlaced rectangles /// private List InterlacedRectangles { get; set; } /// /// List of interlaced lines /// private List InterlacedLines { get; set; } #endregion #region Internal Methods /// /// Creates the visual element for chart grid /// /// ChartVisualCanvas width /// ChartVisualCanvas height /// Whether animation is enabled /// Animation duration internal void CreateVisualObject(Double width, Double height, bool animationEnabled, Double animationDuration) { if (!(Boolean)Enabled) { Visual = null; return; } Visual = new Canvas(); Width = width; Height = height; if (animationEnabled) { Storyboard = new Storyboard(); ScaleTransform st = new ScaleTransform() { ScaleX = 1, ScaleY = 1 }; Visual.RenderTransformOrigin = new Point(0.5, 0.5); Visual.RenderTransform = st; if (Placement == PlacementTypes.Top || Placement == PlacementTypes.Bottom) Storyboard.Children.Add(CreateDoubleAnimation(st, "(ScaleTransform.ScaleY)", 0, 1, 0, animationDuration)); else Storyboard.Children.Add(CreateDoubleAnimation(st, "(ScaleTransform.ScaleX)", 0, 1, 0, animationDuration)); } CreateAndPositionChartGrid(animationEnabled, animationDuration); Visual.Opacity = this.Opacity; } /// /// Update visual used for partial update /// /// Name of the property /// Value of the property internal override void UpdateVisual(string propertyName, object value) { if (Visual != null) { foreach (Rectangle rec in InterlacedRectangles) rec.Fill = InterlacedColor; foreach (Line line in InterlacedLines) { line.Stroke = LineColor; line.StrokeThickness = LineThickness; line.StrokeDashArray = ExtendedGraphics.GetDashArray(LineStyle); } } else FirePropertyChanged(propertyName); } #endregion #region Internal Events #endregion #region Data /// /// Set the width of the grid canvas, will be used only with the Horizontal axis /// private Double _width; /// /// Set the height of the grid canvas, will be used ony with the vertical axis /// private Double _height; #if WPF /// /// Whether the default style is applied /// private static Boolean _defaultStyleKeyApplied; #endif #endregion } }