/* Copyright (C) 2008 Webyog Softworks Private Limited This file is a part of Visifire Charts. Visifire is a free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with Visifire Charts. If not, see . If GPL is not suitable for your products or company, Webyog provides Visifire under a flexible commercial license designed to meet your specific usage and distribution requirements. If you have already obtained a commercial license from Webyog, you can use this file under those license terms. */ #if WPF using System; using System.Windows; using System.Collections.Generic; using System.Linq; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Markup; using System.IO; using System.Xml; using System.Threading; using System.Collections.ObjectModel; #else using System; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; using System.Collections.Generic; using System.Collections.ObjectModel; #endif using Visifire.Commons; using System.Windows.Controls.Primitives; namespace Visifire.Charts { /// /// Partially extended chart control class /// public partial class Chart : VisifireControl { #region Public Methods /// /// Load controls from template /// private void LoadControlsFromTemplate() { _rootElement = GetTemplateChild(RootElementName) as Grid; _shadowGrid = GetTemplateChild(ShadowGridName) as Grid; _chartBorder = GetTemplateChild(ChartBorderName) as Border; _chartLightingBorder = GetTemplateChild(ChartLightingBorderName) as Rectangle; _bevelCanvas = GetTemplateChild(BevelCanvasName) as Canvas; _chartAreaGrid = GetTemplateChild(ChartAreaGridName) as Grid; _topOuterPanel = GetTemplateChild(TopOuterPanelName) as StackPanel; _topOuterTitlePanel = GetTemplateChild(TopOuterTitlePanelName) as StackPanel; _topOuterLegendPanel = GetTemplateChild(TopOuterLegendPanelName) as StackPanel; _bottomOuterPanel = GetTemplateChild(BottomOuterPanelName) as StackPanel; _bottomOuterLegendPanel = GetTemplateChild(BottomOuterLegendPanelName) as StackPanel; _bottomOuterTitlePanel = GetTemplateChild(BottomOuterTitlePanelName) as StackPanel; _leftOuterPanel = GetTemplateChild(LeftOuterPanelName) as StackPanel; _leftOuterTitlePanel = GetTemplateChild(LeftOuterTitlePanelName) as StackPanel; _leftOuterLegendPanel = GetTemplateChild(LeftOuterLegendPanelName) as StackPanel; _rightOuterPanel = GetTemplateChild(RightOuterPanelName) as StackPanel; _rightOuterLegendPanel = GetTemplateChild(RightOuterLegendPanelName) as StackPanel; _rightOuterTitlePanel = GetTemplateChild(RightOuterTitlePanelName) as StackPanel; _centerGrid = GetTemplateChild(CenterGridName) as Grid; _centerOuterGrid = GetTemplateChild(CenterOuterGridName) as Grid; _topOffsetGrid = GetTemplateChild(TopOffsetGridName) as Grid; _bottomOffsetGrid = GetTemplateChild(BottomOffsetGridName) as Grid; _leftOffsetGrid = GetTemplateChild(LeftOffsetGridName) as Grid; _rightOffsetGrid = GetTemplateChild(RightOffsetGridName) as Grid; _topAxisGrid = GetTemplateChild(TopAxisGridName) as Grid; _topAxisContainer = GetTemplateChild(TopAxisContainerName) as StackPanel; _topAxisPanel = GetTemplateChild(TopAxisPanelName) as StackPanel; _topAxisScrollBar = GetTemplateChild(TopAxisScrollBarName) as ScrollBar; _leftAxisGrid = GetTemplateChild(LeftAxisGridName) as Grid; _leftAxisContainer = GetTemplateChild(LeftAxisContainerName) as StackPanel; _leftAxisPanel = GetTemplateChild(LeftAxisPanelName) as StackPanel; _leftAxisScrollBar = GetTemplateChild(LeftAxisScrollBarName) as ScrollBar; _rightAxisGrid = GetTemplateChild(RightAxisGridName) as Grid; _rightAxisContainer = GetTemplateChild(RightAxisContainerName) as StackPanel; _rightAxisScrollBar = GetTemplateChild(RightAxisScrollBarName) as ScrollBar; _rightAxisPanel = GetTemplateChild(RightAxisPanelName) as StackPanel; _bottomAxisGrid = GetTemplateChild(BottomAxisGridName) as Grid; _bottomAxisContainer = GetTemplateChild(BottomAxisContainerName) as StackPanel; _bottomAxisScrollBar = GetTemplateChild(BottomAxisScrollBarName) as ScrollBar; _bottomAxisPanel = GetTemplateChild(BottomAxisPanelName) as StackPanel; _centerInnerGrid = GetTemplateChild(CenterInnerGridName) as Grid; _innerGrid = GetTemplateChild(InnerGridName) as Grid; _topInnerPanel = GetTemplateChild(TopInnerPanelName) as StackPanel; _topInnerTitlePanel = GetTemplateChild(TopInnerTitlePanelName) as StackPanel; _topInnerLegendPanel = GetTemplateChild(TopInnerLegendPanelName) as StackPanel; _bottomInnerPanel = GetTemplateChild(BottomInnerPanelName) as StackPanel; _bottomInnerLegendPanel = GetTemplateChild(BottomInnerLegendPanelName) as StackPanel; _bottomInnerTitlePanel = GetTemplateChild(BottomInnerTitlePanelName) as StackPanel; _leftInnerPanel = GetTemplateChild(LeftInnerPanelName) as StackPanel; _leftInnerTitlePanel = GetTemplateChild(LeftInnerTitlePanelName) as StackPanel; _leftInnerLegendPanel = GetTemplateChild(LeftInnerLegendPanelName) as StackPanel; _rightInnerPanel = GetTemplateChild(RightInnerPanelName) as StackPanel; _rightInnerLegendPanel = GetTemplateChild(RightInnerLegendPanelName) as StackPanel; _rightInnerTitlePanel = GetTemplateChild(RightInnerTitlePanelName) as StackPanel; _plotAreaGrid = GetTemplateChild(PlotAreaGridName) as Grid; _plotAreaScrollViewer = GetTemplateChild(PlotAreaScrollViewerName) as ScrollViewer; _plotCanvas = GetTemplateChild(PlotGridName) as Canvas; _plotAreaShadowCanvas = GetTemplateChild(PlotAreaShadowCanvasName) as Canvas; _drawingCanvas = GetTemplateChild(DrawingCanvasName) as Canvas; _centerDockInsidePlotAreaPanel = GetTemplateChild(CenterDockInsidePlotAreaPanelName) as StackPanel; _centerDockOutsidePlotAreaPanel = GetTemplateChild(CenterDockOutsidePlotAreaPanelName) as StackPanel; _toolTipCanvas = GetTemplateChild(ToolTipCanvasName) as Canvas; } /// /// Set Margin for various elements to maintain effects like Shadow and Bevel /// public void SetMarginOfElements() { if (ShadowEnabled) { _chartBorder.Margin = new Thickness(0, 0, SHADOW_DEPTH, SHADOW_DEPTH); _bevelCanvas.Margin = new Thickness(0, 0, SHADOW_DEPTH, SHADOW_DEPTH); } _chartAreaMargin = new Thickness(_chartAreaGrid.Margin.Left, _chartAreaGrid.Margin.Top, _chartAreaGrid.Margin.Right, _chartAreaGrid.Margin.Bottom); if (Bevel) { _chartAreaGrid.Margin = new Thickness( _chartAreaMargin.Left + BEVEL_DEPTH, _chartAreaMargin.Top + BEVEL_DEPTH, _chartAreaMargin.Right + BEVEL_DEPTH, _chartAreaMargin.Bottom + BEVEL_DEPTH); } } /// /// OnApplyTemplate() function is invoked whenever application code /// or internal processes such as a rebuilding layout pass. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); LoadControlsFromTemplate(); LoadWatermark(); if (StyleDictionary == null) LoadTheme("Theme1", false); LoadOrUpdateColorSets(); LoadToolTips(); SetMarginOfElements(); ApplyLighting(); AttachToolTipAndEvents(); BindProperties(); _internalAnimationEnabled = AnimationEnabled; if (_internalAnimationEnabled) _rootElement.IsHitTestVisible = false; _isTemplateApplied = true; #if WPF NameScope.SetNameScope(this._rootElement, new NameScope()); #endif } #endregion #region Public Properties /// /// Identifies the Visifire.Charts.Chart.SmartLabelEnabled dependency property. /// public static readonly DependencyProperty SmartLabelEnabledProperty = DependencyProperty.Register("SmartLabelEnabled", typeof(Boolean), typeof(Chart), new PropertyMetadata(OnSmartLabelEnabledPropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.DataPointWidth dependency property. /// public static readonly DependencyProperty DataPointWidthProperty = DependencyProperty.Register("DataPointWidth", typeof(Double), typeof(Chart), new PropertyMetadata(Double.NaN, OnDataPointWidthPropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.UniqueColors dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.UniqueColors dependency property. /// public static readonly DependencyProperty UniqueColorsProperty = DependencyProperty.Register ("UniqueColors", typeof(Boolean), typeof(Chart), new PropertyMetadata(true, OnUniqueColorsPropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.ScrollingEnabled dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.ScrollingEnabled dependency property. /// public static readonly DependencyProperty ScrollingEnabledProperty = DependencyProperty.Register ("ScrollingEnabled", typeof(Nullable), typeof(Chart), new PropertyMetadata(OnScrollingEnabledPropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.View3D dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.View3D dependency property. /// public static readonly DependencyProperty View3DProperty = DependencyProperty.Register ("View3D", typeof(Boolean), typeof(Chart), new PropertyMetadata(OnView3DPropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.HrefTarget dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.HrefTarget dependency property. /// public static readonly DependencyProperty HrefTargetProperty = DependencyProperty.Register ("HrefTarget", typeof(HrefTargets), typeof(Chart), new PropertyMetadata(OnHrefTargetChanged)); /// /// Identifies the Visifire.Charts.Chart.Href dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.Href dependency property. /// public static readonly DependencyProperty HrefProperty = DependencyProperty.Register ("Href", typeof(String), typeof(Chart), new PropertyMetadata(OnHrefChanged)); /// /// Identifies the Visifire.Charts.Chart.Theme dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.Theme dependency property. /// public static readonly DependencyProperty ThemeProperty = DependencyProperty.Register ("Theme", typeof(String), typeof(Chart), new PropertyMetadata(OnThemePropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.AnimationEnabled dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.AnimationEnabled dependency property. /// public static readonly DependencyProperty AnimationEnabledProperty = DependencyProperty.Register ("AnimationEnabled", typeof(Boolean), typeof(Chart), null); /// /// Identifies the Visifire.Charts.Chart.InternalBorderThickness dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.InternalBorderThickness dependency property. /// private static readonly DependencyProperty InternalBorderThicknessProperty = DependencyProperty.Register ("InternalBorderThickness", typeof(Thickness), typeof(Chart), new PropertyMetadata(OnInternalBorderThicknessChanged)); /// /// Identifies the Visifire.Charts.Chart.InternalBackground dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.InternalBackground dependency property. /// private static readonly DependencyProperty InternalBackgroundProperty = DependencyProperty.Register ("InternalBackground", typeof(Brush), typeof(Chart), new PropertyMetadata(OnInternalBackgroundPropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.Bevel dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.Bevel dependency property. /// public static readonly DependencyProperty BevelProperty = DependencyProperty.Register ("Bevel", typeof(Boolean), typeof(Chart), new PropertyMetadata(OnBevelPropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.ColorSet dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.ColorSet dependency property. /// public static readonly DependencyProperty ColorSetProperty = DependencyProperty.Register ("ColorSet", typeof(String), typeof(Chart), new PropertyMetadata(OnColorSetPropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.ColorSets dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.ColorSets dependency property. /// public static readonly DependencyProperty ColorSetsProperty = DependencyProperty.Register ("ColorSets", typeof(ColorSets), typeof(Chart), new PropertyMetadata(new ColorSets(), null)); /// /// Identifies the Visifire.Charts.Chart.LightingEnabled dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.LightingEnabled dependency property. /// public static readonly DependencyProperty LightingEnabledProperty = DependencyProperty.Register ("LightingEnabled", typeof(Boolean), typeof(Chart), new PropertyMetadata(OnLightingEnabledPropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.CornerRadius dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.CornerRadius dependency property. /// public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register ("CornerRadius", typeof(CornerRadius), typeof(Chart), null); /// /// Identifies the Visifire.Charts.Chart.ShadowEnabled dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.ShadowEnabled dependency property. /// public static readonly DependencyProperty ShadowEnabledProperty = DependencyProperty.Register ("ShadowEnabled", typeof(Boolean), typeof(Chart), new PropertyMetadata(OnShadowEnabledPropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.Watermark dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.Watermark dependency property. /// public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register ("Watermark", typeof(Boolean), typeof(Chart), new PropertyMetadata(true, OnWatermarkPropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.PlotArea dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.PlotArea dependency property. /// public static readonly DependencyProperty PlotAreaProperty = DependencyProperty.Register ("PlotArea", typeof(PlotArea), typeof(Chart), new PropertyMetadata(OnPlotAreaPropertyChanged)); /// /// Identifies the Visifire.Charts.Chart.MinScrollingGap dependency property. /// /// /// The identifier for the Visifire.Charts.Chart.MinScrollingGap dependency property. /// [Obsolete] public static readonly DependencyProperty MinimumGapProperty = DependencyProperty.Register ("MinimumGap", typeof(Nullable), typeof(Chart), new PropertyMetadata(OnMinimumGapPropertyChanged)); /// /// Decides how the color will be applied to the DataPoints. /// If UniqueColors = True and if only one DataSeries is present in Chart then each DataPoint in that DataSeries takes one color from the ColorSet given to the chart. /// If UniqueColors = True and if more than one DataSeries are present in Chart then each series takes one color from the ColorSet provided at the chart. /// If UniqueColors = False then each DataSeries takes one color /// /// public Boolean UniqueColors { get { return (Boolean)GetValue(UniqueColorsProperty); } set { SetValue(UniqueColorsProperty, value); } } /// /// Whether skipping of labels are allowed /// Note: Currently implemented for Pie and Doughnut charts only /// public Boolean SmartLabelEnabled { get { return (Boolean)GetValue(SmartLabelEnabledProperty); } set { SetValue(SmartLabelEnabledProperty, value); } } /// /// Width of a DataPoint /// public Double DataPointWidth { get { return (Double)GetValue(DataPointWidthProperty); } set { SetValue(DataPointWidthProperty, value); } } /// /// Minimum gap between two DataPoint of same series in PlotArea /// #if SL [System.ComponentModel.TypeConverter(typeof(Converters.NullableDoubleConverter))] #endif [Obsolete] public Nullable MinimumGap { get { return (Nullable)GetValue(MinimumGapProperty); } set { SetValue(MinimumGapProperty, value); } } /// /// Enable or disable 3D effect /// #if SL [System.ComponentModel.TypeConverter(typeof(NullableBoolConverter))] #endif public Nullable ScrollingEnabled { set { SetValue(ScrollingEnabledProperty, value); } get { if ((Nullable)GetValue(ScrollingEnabledProperty) == null) { return true; } else return (Nullable)GetValue(ScrollingEnabledProperty); } } /// /// Enable or disable 3D effect /// public Boolean View3D { set { SetValue(View3DProperty, value); } get { return (Boolean)GetValue(View3DProperty); } } /// /// Target window Property for hyperlink /// public HrefTargets HrefTarget { get { return (HrefTargets)GetValue(HrefTargetProperty); } set { SetValue(HrefTargetProperty, value); } } /// /// Hyperlink Property /// public String Href { get { return (String)GetValue(HrefProperty); } set { SetValue(HrefProperty, value); } } /// /// Name of the theme to be applied /// public String Theme { get { return (String)GetValue(ThemeProperty); } set { SetValue(ThemeProperty, value); } } /// /// Enables or disables animation /// public Boolean AnimationEnabled { get { if (!IsInDesignMode) { if (String.IsNullOrEmpty(AnimationEnabledProperty.ToString())) { return false; } else return (Boolean)GetValue(AnimationEnabledProperty); } else return false; } set { SetValue(AnimationEnabledProperty, value); } } #region BorderProperties /// /// BorderThickness property /// public new Thickness BorderThickness { get { return (Thickness)GetValue(BorderThicknessProperty); } set { SetValue(BorderThicknessProperty, value); SetValue(InternalBorderThicknessProperty, value); } } #endregion /// /// Background color property /// public new Brush Background { get { return (Brush)GetValue(BackgroundProperty); } set { SetValue(BackgroundProperty, value); SetValue(InternalBackgroundProperty, value); } } /// /// Enabled or disables the bevel effect /// public Boolean Bevel { get { return (Boolean)GetValue(BevelProperty); } set { SetValue(BevelProperty, value); } } /// /// Set of colors that will be used for the InternalDataPoints /// public String ColorSet { get { return (String)GetValue(ColorSetProperty); } set { SetValue(ColorSetProperty, value); } } /// /// Set of colors that will be used for the InternalDataPoints /// public ColorSets ColorSets { get { return (ColorSets)GetValue(ColorSetsProperty); } set { SetValue(ColorSetsProperty, value); } } /// /// Enables or disables automatic color shading /// public Boolean LightingEnabled { get { return (Boolean)GetValue(LightingEnabledProperty); } set { SetValue(LightingEnabledProperty, value); } } /// /// Sets the parameter used to create rounded or elliptical corners of the element /// #if WPF [System.ComponentModel.TypeConverter(typeof(System.Windows.CornerRadiusConverter))] #else [System.ComponentModel.TypeConverter(typeof(Converters.CornerRadiusConverter))] #endif public CornerRadius CornerRadius { get { return (CornerRadius)GetValue(CornerRadiusProperty); } set { SetValue(CornerRadiusProperty, value); } } /// /// Enables or disables the shadow for the element /// public Boolean ShadowEnabled { get { return (Boolean)GetValue(ShadowEnabledProperty); } set { SetValue(ShadowEnabledProperty, value); } } /// /// Enables or disables the "Powered by Visifire" watermark /// public Boolean Watermark { get { return (Boolean)GetValue(WatermarkProperty); } set { SetValue(WatermarkProperty, value); } } /// /// PlotArea of the chart /// public PlotArea PlotArea { get { return (PlotArea)GetValue(PlotAreaProperty); } set { SetValue(PlotAreaProperty, value); PlotArea.Chart = this; PlotArea.PropertyChanged -= Element_PropertyChanged; PlotArea.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Element_PropertyChanged); } } /// /// AxesX as AxisCollection of type Axis /// public AxisCollection AxesX { get; set; } /// /// AxesY as AxisCollection of type Axis /// public AxisCollection AxesY { get; set; } /// /// Titles as TitleCollection of type Title /// public TitleCollection Titles { get; set; } /// /// Legends as LegendCollection of type Legend /// public LegendCollection Legends { get; set; } /// /// ToolTips as ToolTipCollection of type ToolTip /// public ToolTipCollection ToolTips { get; set; } /// /// TrendLines as TrendLineCollection of type TrendLine /// public TrendLineCollection TrendLines { get; set; } /// /// Collection of DataSeries /// public DataSeriesCollection Series { get; set; } #endregion #region Public Events /// /// Event handler for the Rendered event /// public event EventHandler Rendered { remove { _rendered -= value; } add { _rendered += value; } } #endregion #region Protected Methods #endregion #region Private Delegates #endregion #region Private Methods /// /// Initializes the various properties of the class /// private void Init() { // Initialize colorSets //ColorSets = new ColorSets(); // Initialize tooltip ToolTips = new ToolTipCollection(); // Initialize title list Titles = new TitleCollection(); // Initialize legend list Legends = new LegendCollection(); // Initialize trendLine list TrendLines = new TrendLineCollection(); // Initialize AxesX list AxesX = new AxisCollection(); // Initialize AxesY list AxesY = new AxisCollection(); // Initialize Series list Series = new DataSeriesCollection(); // Initialize PlotArea PlotArea = new PlotArea() { Chart = this }; // Attach event handler on collection changed event with ToolTip collection ToolTips.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ToolTips_CollectionChanged); // Attach event handler on collection changed event with Title collection Titles.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Titles_CollectionChanged); // Attach event handler on collection changed event with Legend collection Legends.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Legends_CollectionChanged); // Attach event handler on collection changed event with TrendLines collection TrendLines.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(TrendLines_CollectionChanged); // Attach event handler on collection changed event with DataSeries collection Series.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Series_CollectionChanged); // Attach event handler on collection changed event with axisX collection (AxesX as AxisCollection).CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(AxesX_CollectionChanged); // Attach event handler on collection changed event with AxisY collection (AxesY as AxisCollection).CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(AxesY_CollectionChanged); } /// /// OnMouseMoveLeave chart set ToolTip position. /// ToolTip at current mouse position over the chart control /// /// Chart as object /// MouseEventArgs private void Chart_MouseLeave(object sender, MouseEventArgs e) { if (ToolTipEnabled) { if (_toolTip.Visibility == Visibility.Visible) _toolTip.Hide(); } else { _toolTip.Hide(); } _toolTip.Text = ""; } /// /// OnMouseMoveOver chart set ToolTip position. /// ToolTip at current mouse position over the chart control /// /// Chart as object /// MouseEventArgs private void Chart_MouseMove(object sender, MouseEventArgs e) { UpdateToolTipPosition(sender, e); } /// /// Event handler manages the addition and removal of tooltip from tooltip list of chart /// /// ToolTips /// NotifyCollectionChangedEventArgs private void ToolTips_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) { if (e.NewItems != null) { foreach (ToolTip toolTip in e.NewItems) { if (toolTip.Style == null && StyleDictionary != null) { Style myStyle = StyleDictionary["ToolTip"] as Style; if (myStyle != null) toolTip.Style = myStyle; } toolTip.Chart = this; } } } else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove) { foreach (ToolTip toolTip in e.OldItems) (toolTip.Chart as Chart)._toolTipCanvas.Children.Remove(toolTip); } } /// /// Event handler manages the addition and removal of Axis from AxesX list of chart /// /// AxesX /// NotifyCollectionChangedEventArgs private void AxesX_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { Boolean isAutoAxis = false; if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) { if (e.NewItems != null) { foreach (Axis axis in e.NewItems) { axis.Chart = this; if (axis._isAutoGenerated) isAutoAxis = true; if (axis.AxisLabels != null) axis.AxisLabels.Chart = this; foreach (ChartGrid cg in axis.Grids) cg.Chart = this; foreach (Ticks ticks in axis.Ticks) ticks.Chart = this; axis.IsNotificationEnable = false; if (axis.StartFromZero == null) axis.StartFromZero = false; axis.IsNotificationEnable = true; axis.PropertyChanged -= Element_PropertyChanged; axis.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Element_PropertyChanged); } } } if (!isAutoAxis) { InvokeRender(); } } /// /// Event handler manages the addition and removal of Axis from AxesY list of chart /// /// AxesY /// NotifyCollectionChangedEventArgs private void AxesY_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { Boolean isAutoAxis = false; if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) { if (e.NewItems != null) { foreach (Axis axis in e.NewItems) { axis.Chart = this; if (axis._isAutoGenerated) isAutoAxis = true; axis.AxisRepresentation = AxisRepresentations.AxisY; if (axis.AxisLabels != null) axis.AxisLabels.Chart = this; foreach (ChartGrid cg in axis.Grids) cg.Chart = this; foreach (Ticks ticks in axis.Ticks) ticks.Chart = this; axis.IsNotificationEnable = false; if (axis.StartFromZero == null) axis.StartFromZero = true; axis.IsNotificationEnable = true; axis.PropertyChanged -= Element_PropertyChanged; axis.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Element_PropertyChanged); } } } if (!isAutoAxis) { InvokeRender(); } } /// /// Event handler manages the addition and removal of title from chart /// /// Titles /// NotifyCollectionChangedEventArgs private void Titles_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) { ObservableCollection titles = sender as ObservableCollection<Title>; if (e.NewItems != null) { foreach (Title title in e.NewItems) { title.Chart = this; title.PropertyChanged -= Element_PropertyChanged; title.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Element_PropertyChanged); } } } InvokeRender(); } /// <summary> /// Event handler manages the addition and removal of legend from chart /// </summary> /// <param name="sender">Legend</param> /// <param name="e">NotifyCollectionChangedEventArgs</param> private void Legends_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { Boolean isAutoLegend = false; if (e.NewItems != null) { foreach (Legend legend in e.NewItems) { if (legend._isAutoGenerated) isAutoLegend = true; if (Legends.Count > 0) { if (String.IsNullOrEmpty((String)legend.GetValue(NameProperty))) legend.SetValue(NameProperty, "Legend" + Legends.IndexOf(legend)); } legend.Chart = this; legend.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Element_PropertyChanged); } } if (!isAutoLegend) { InvokeRender(); } } /// <summary> /// Event handler manages the addition and removal of trendLine from chart /// </summary> /// <param name="sender">TrendLine</param> /// <param name="e">NotifyCollectionChangedEventArgs</param> private void TrendLines_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) { if (e.NewItems != null) { foreach (TrendLine trendLine in e.NewItems) { trendLine.Chart = this; if (!String.IsNullOrEmpty(this.Theme)) { trendLine.ApplyStyleFromTheme(this, "TrendLine"); } trendLine.PropertyChanged -= Element_PropertyChanged; trendLine.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Element_PropertyChanged); } } } InvokeRender(); } /// <summary> /// Event handler manages the addition and removal of DataSeries from Series list of chart /// </summary> /// <param name="sender">Series</param> /// <param name="e">NotifyCollectionChangedEventArgs</param> private void Series_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) { if (e.NewItems != null) foreach (DataSeries ds in e.NewItems) { ds.Chart = this; foreach (DataPoint dp in ds.DataPoints) { dp.Chart = this; } if (!String.IsNullOrEmpty(this.Theme)) ds.ApplyStyleFromTheme(this, "DataSeries"); if (String.IsNullOrEmpty((String)ds.GetValue(NameProperty))) { //ds.SetValue(NameProperty, ds.GetType().Name + this.Series.IndexOf(ds).ToString() + "_" + Guid.NewGuid().ToString().Replace('-', '_')); ds.Name = "DataSeries" + (this.Series.Count - 1).ToString() + "_" + Guid.NewGuid().ToString().Replace('-', '_'); ds._isAutoName = true; } else ds._isAutoName = false; ds.PropertyChanged -= Element_PropertyChanged; ds.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Element_PropertyChanged); } } InvokeRender(); } /// <summary> /// Event handler manages property change of visifire elements, like Title, Legends, DataSeries etc. /// </summary> /// <param name="sender">ObservableObject</param> /// <param name="e">PropertyChangedEventArgs</param> private void Element_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { InvokeRender(); } /// <summary> /// Load watermark for www.visifire.com, present at the right and top side corner of the chart. /// </summary> private void LoadWatermark() { if (WaterMarkElement == null) { WaterMarkElement = new TextBlock(); WaterMarkElement.HorizontalAlignment = HorizontalAlignment.Right; WaterMarkElement.VerticalAlignment = VerticalAlignment.Top; WaterMarkElement.Margin = new Thickness(0, 3, 5, 0); WaterMarkElement.SetValue(Canvas.ZIndexProperty, 90000); WaterMarkElement.Text = "www.visifire.com"; WaterMarkElement.TextDecorations = TextDecorations.Underline; WaterMarkElement.Foreground = new SolidColorBrush(Colors.LightGray); WaterMarkElement.FontSize = 9; AttachHref(this, WaterMarkElement, "http://www.visifire.com", HrefTargets._blank); WaterMarkElement.Visibility = (Watermark) ? Visibility.Visible : Visibility.Collapsed; _rootElement.Children.Add(WaterMarkElement); } } /// <summary> /// Load or Update color sets from chart resource /// </summary> private void LoadOrUpdateColorSets() { if (EmbeddedColorSets == null) { string resourceName = "Visifire.Charts.ColorSets.xaml"; // Resource location for embedded color sets using (System.IO.Stream s = typeof(Chart).Assembly.GetManifestResourceStream(resourceName)) { if (s != null) { System.IO.StreamReader reader = new System.IO.StreamReader(s); String xaml = reader.ReadToEnd(); #if WPF EmbeddedColorSets = XamlReader.Load(new XmlTextReader(new StringReader(xaml))) as ColorSets; #else EmbeddedColorSets = System.Windows.Markup.XamlReader.Load(xaml) as ColorSets; #endif if (EmbeddedColorSets == null) System.Diagnostics.Debug.WriteLine("Unable to load embedded ColorSets. Reload project and try again."); //if (InternalColorSets == null) // InternalColorSets = new ColorSets(); //if (EmbeddedColorSets != null) // InternalColorSets.AddRange(EmbeddedColorSets); //if (ColorSets != null) // InternalColorSets.AddRange(ColorSets); reader.Close(); s.Close(); } } } //else //{ // InternalColorSets.Clear(); // if (EmbeddedColorSets != null) // InternalColorSets.AddRange(EmbeddedColorSets); // if (ColorSets != null) // InternalColorSets.AddRange(ColorSets); //} } /// <summary> /// Some property needs to bind with the existing property in control before first time render. /// And binding should be done only once. /// </summary> private void BindProperties() { System.Windows.Data.Binding binding = new System.Windows.Data.Binding("Background"); binding.Source = this; binding.Mode = System.Windows.Data.BindingMode.OneWay; base.SetBinding(InternalBackgroundProperty, binding); System.Windows.Data.Binding binding1 = new System.Windows.Data.Binding("BorderThickness"); binding1.Source = this; binding1.Mode = System.Windows.Data.BindingMode.OneWay; base.SetBinding(InternalBorderThicknessProperty, binding1); } /// <summary> /// Prepare chart area for drawing /// </summary> private void PrepareChartAreaForDrawing() { // Create new ChartArea if (ChartArea == null) { ChartArea = new ChartArea(this as Chart); } #if WPF NameScope.SetNameScope(this._rootElement, new NameScope()); #endif ApplyChartBevel(); ApplyChartShadow(this.ActualWidth, this.ActualHeight); _renderLapsedCounter = 0; } /// <summary> /// Apply bevel effect to Chart /// </summary> /// <returns>If success return true else false.</returns> private Boolean ApplyChartBevel() { if (Bevel && _rootElement != null) { if (_chartBorder.ActualWidth == 0 && _chartBorder.ActualHeight == 0) { return false; } _chartAreaGrid.Margin = new Thickness( _chartAreaMargin.Left + BEVEL_DEPTH, _chartAreaMargin.Top + BEVEL_DEPTH, _chartAreaMargin.Right, _chartAreaMargin.Bottom); _chartAreaGrid.UpdateLayout(); _bevelCanvas.Children.Clear(); Brush topBrush = Graphics.GetBevelTopBrush(this.Background); Brush leftBrush = Graphics.GetBevelSideBrush(0, this.Background); Brush rightBrush = Graphics.GetBevelSideBrush(170, this.Background); Brush bottomBrush = Graphics.GetBevelSideBrush(180, this.Background); BevelVisual = ExtendedGraphics.Get2DRectangleBevel(null, _chartBorder.ActualWidth - _chartBorder.BorderThickness.Left - _chartBorder.BorderThickness.Right - _chartAreaMargin.Right - _chartAreaMargin.Left, _chartBorder.ActualHeight - _chartBorder.BorderThickness.Top - _chartBorder.BorderThickness.Bottom - _chartAreaMargin.Top - _chartAreaMargin.Bottom, BEVEL_DEPTH, BEVEL_DEPTH, topBrush, leftBrush, rightBrush, bottomBrush); if (LightingEnabled) { _chartLightingBorder.Opacity = 0.4; } BevelVisual.Margin = new Thickness(0, 0, 0, 0); BevelVisual.IsHitTestVisible = false; BevelVisual.SetValue(Canvas.TopProperty, _chartAreaMargin.Top + _chartBorder.BorderThickness.Top); BevelVisual.SetValue(Canvas.LeftProperty, _chartAreaMargin.Left + _chartBorder.BorderThickness.Left); _bevelCanvas.Children.Add(BevelVisual); } return true; } /// <summary> /// Remove bevel effect from chart /// </summary> private void RemoveChartBevel() { if (_isTemplateApplied) { if (BevelVisual != null && _bevelCanvas != null) { _bevelCanvas.Children.Clear(); _chartAreaGrid.Margin = new Thickness(0); } } } /// <summary> /// Apply lighting effect for chart /// </summary> private void ApplyLighting() { if (_chartLightingBorder != null) { if (LightingEnabled) { _chartLightingBorder.Visibility = Visibility.Visible; _chartLightingBorder.Opacity = 0.4; } else { _chartLightingBorder.Visibility = Visibility.Collapsed; } } } /// <summary> /// Apply shadow effect on chart /// (Creates a shadow layer behind the chart) /// </summary> /// <param name="Height">Chart width</param> /// <param name="Width">Chart height</param> private void ApplyChartShadow(Double width, Double height) { if (!_isShadowApplied && ShadowEnabled && !Double.IsNaN(height) && height != 0 && !Double.IsNaN(width) && width != 0) { _shadowGrid.Children.Clear(); if (_rootElement != null) { // Shadow grid contains multiple rectangles that give a blurred effect at the edges ChartShadowLayer = ExtendedGraphics.Get2DRectangleShadow(null, width - Chart.SHADOW_DEPTH, height - Chart.SHADOW_DEPTH, new CornerRadius(6), new CornerRadius(6), 6); ChartShadowLayer.Width = width - Chart.SHADOW_DEPTH; ChartShadowLayer.Height = height - Chart.SHADOW_DEPTH; ChartShadowLayer.IsHitTestVisible = false; ChartShadowLayer.SetValue(Canvas.ZIndexProperty, 0); _shadowGrid.Children.Add(ChartShadowLayer); ChartShadowLayer.Margin = new Thickness(Chart.SHADOW_DEPTH, Chart.SHADOW_DEPTH, 0, 0); if (this._chartBorder != null) this._chartBorder.Margin = new Thickness(0, 0, SHADOW_DEPTH, SHADOW_DEPTH); _isShadowApplied = true; } } if (ShadowEnabled && ChartShadowLayer != null) ChartShadowLayer.Visibility = Visibility.Visible; } /// <summary> /// Removes Chart shadow /// </summary> private void RemoveShadow() { if (_isTemplateApplied && !ShadowEnabled) { _chartBorder.Margin = new Thickness(0, 0, 0, 0); _bevelCanvas.Margin = new Thickness(0, 0, 0, 0); _isShadowApplied = false; if (ChartShadowLayer != null) ChartShadowLayer.Visibility = Visibility.Collapsed; } } /// <summary> /// Load theme from resource file and select specific theme using theme name /// </summary> /// <param name="themeName">String themeName</param> private void LoadTheme(String themeName, Boolean isThemePropertyChanged) { if (String.IsNullOrEmpty(themeName)) return; StyleDictionary = null; string fooResourceName = "Visifire.Charts." + themeName + ".xaml"; using (System.IO.Stream s = typeof(Chart).Assembly.GetManifestResourceStream(fooResourceName)) { if (s == null) { #if WPF ResourceDictionary resource = (ResourceDictionary)Application.LoadComponent(new Uri(@"" + themeName + ".xaml", UriKind.RelativeOrAbsolute)); StyleDictionary = resource; #else fooResourceName = themeName + ".xaml"; System.Windows.Resources.StreamResourceInfo srif = Application.GetResourceStream(new Uri(fooResourceName, UriKind.RelativeOrAbsolute)); if (srif == null) throw new Exception(fooResourceName + " Theme file not found as application resources."); using (System.IO.Stream s1 = srif.Stream) { if (s1 != null) { System.IO.StreamReader reader = new System.IO.StreamReader(s1); String xaml = reader.ReadToEnd(); StyleDictionary = System.Windows.Markup.XamlReader.Load(xaml) as ResourceDictionary; reader.Close(); s1.Close(); } } #endif } else { System.IO.StreamReader reader = new System.IO.StreamReader(s); String xaml = reader.ReadToEnd(); #if WPF StyleDictionary = (ResourceDictionary)XamlReader.Load(new XmlTextReader(new StringReader(xaml))); #else StyleDictionary = System.Windows.Markup.XamlReader.Load(xaml) as ResourceDictionary; #endif reader.Close(); s.Close(); } } if (StyleDictionary != null) { //#if SL // if (Style == null) // { // Style myStyle = StyleDictionary["Chart"] as Style; // if (myStyle != null) // Style = myStyle; // } //#else Style myStyle = StyleDictionary["Chart"] as Style; _isThemeChanged = isThemePropertyChanged; if (myStyle != null) { if (isThemePropertyChanged) Style = myStyle; else if(Style == null) Style = myStyle; } //#endif } else { throw new Exception("Theme file " + themeName + ".xaml not found.."); } } /// <summary> /// Loads default ToolTips if required. /// Currently only one tooltip is supported /// </summary> private void LoadToolTips() { if (ToolTips.Count == 0) { ToolTip toolTip = new ToolTip() { Chart = this, Visibility = Visibility.Collapsed }; ToolTips.Add(toolTip); } _toolTip = ToolTips[0]; _toolTip.Chart = this; _toolTipCanvas.Children.Add(_toolTip); // Attach events to the root element of the chart to track mouse movement over chart. this._rootElement.MouseLeave += new MouseEventHandler(Chart_MouseLeave); } /// <summary> /// Attach events and tooltip to chart /// </summary> private void AttachToolTipAndEvents() { if (!String.IsNullOrEmpty(ToolTipText)) AttachToolTip(this, this, this); AttachEvents2Visual(this, this, this._rootElement); AttachEvents2Visual4MouseDownEvent(this, this, this._plotCanvas); } /// <summary> /// DataPointWidthProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnSmartLabelEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.InvokeRender(); } /// <summary> /// DataPointWidthProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnDataPointWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.InvokeRender(); } /// <summary> /// ScrollingEnabledProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnScrollingEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.InvokeRender(); } /// <summary> /// UniqueColorsProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnUniqueColorsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.InvokeRender(); } /// <summary> /// View3DProperty changed call back function /// </summary> /// <param name="d"></param> /// <param name="e"></param> private static void OnView3DPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.InvokeRender(); } /// <summary> /// HrefTargetProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnHrefTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.AttachHref(c, c, c.Href, (HrefTargets)e.NewValue); } /// <summary> /// HrefProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnHrefChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.AttachHref(c, c, e.NewValue.ToString(), c.HrefTarget); } /// <summary> /// ThemeProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnThemePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.LoadTheme((String)e.NewValue, (e.OldValue == null)?false:true); c.InvokeRender(); } /// <summary> /// InternalBorderThicknessProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnInternalBorderThicknessChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.RemoveChartBevel(); c.InvokeRender(); } /// <summary> /// InternalBackgroundProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnInternalBackgroundPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.RemoveChartBevel(); c.ApplyChartBevel(); c.InvokeRender(); } /// <summary> /// InternalPropertyProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnInternalPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; if (c.ApplyChartBevel()) { if ((Boolean)c.Bevel == true) { c.InvokeRender(); } else { c.RemoveChartBevel(); } } } /// <summary> /// Property changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnBevelPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.RemoveChartBevel(); c.InvokeRender(); } /// <summary> /// Property changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnColorSetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.InvokeRender(); } /// <summary> /// Property changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnLightingEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.ApplyLighting(); } /// <summary> /// Property changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnShadowEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; if ((Boolean)e.NewValue == true) { c.ApplyChartShadow(c.ActualWidth, c.ActualHeight); } else { c.RemoveShadow(); } c.ApplyChartBevel(); } /// <summary> /// WaterMarkElementProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnWatermarkPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; if (c.WaterMarkElement != null) c.WaterMarkElement.Visibility = ((Boolean)e.NewValue) ? Visibility.Visible : Visibility.Collapsed; } /// <summary> /// PlotAreaProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnPlotAreaPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart chart = d as Chart; PlotArea plotArea = (PlotArea)e.NewValue; plotArea.Chart = chart; plotArea.PropertyChanged -= chart.Element_PropertyChanged; plotArea.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(chart.Element_PropertyChanged); chart.InvokeRender(); } /// <summary> /// MinScrollingGapProperty changed call back function /// </summary> /// <param name="d">Chart</param> /// <param name="e">DependencyPropertyChangedEventArgs</param> private static void OnMinimumGapPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Chart c = d as Chart; c.InvokeRender(); } #endregion #region Private Properties /// <summary> /// Embedded color sets /// </summary> private ColorSets EmbeddedColorSets { get; set; } /// <summary> /// Chart shadow layer is created and added to ShadowGrid, present in template /// </summary> private Grid ChartShadowLayer { get; set; } /// <summary> /// TextBlock element for water Mark /// </summary> private TextBlock WaterMarkElement { get; set; } /// <summary> /// Foreground is a default property of a Control and it is not applicable in chart. /// Hides the Foreground property in control /// </summary> [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] private new Brush Foreground { get; set; } #endregion #region Internal Properties /// <summary> /// If scroll is activated by any cause then IsScrollingActivated should be set to true. /// </summary> internal Boolean IsScrollingActivated { get; set; } /// <summary> /// Collection of DataSeries /// </summary> internal List<DataSeries> InternalSeries { get; set; } /// <summary> /// Bevel canvas as Bevel Visual /// </summary> internal Canvas BevelVisual { get; set; } /// <summary> /// PlotDetails holds plotting information. /// As example, grouping of series depending upon axis types and RenderAs type of Series. /// </summary> internal PlotDetails PlotDetails { get; set; } /// <summary> /// Chart area is the entire area defined by the chart. /// Chart area manages jobs related to plotting the graph. /// </summary> internal ChartArea ChartArea { get; set; } /// <summary> /// StyleDictionary of themes /// </summary> internal ResourceDictionary StyleDictionary { get; set; } /// <summary> /// AxesX as Observable collection of type Axis /// </summary> internal IList<Axis> InternalAxesX { get; set; } /// <summary> /// AxesY as Observable collection of type Axis /// </summary> internal IList<Axis> InternalAxesY { get; set; } #endregion #region Internal Methods /// <summary> /// Visually select the DataPoints which are selected /// </summary> /// <param name="chart"></param> internal static void SelectDataPoints(Chart chart) { if (chart == null || chart.InternalSeries == null) return; foreach (DataSeries ds in chart.InternalSeries) { if (ds.SelectionEnabled) { foreach (DataPoint dp in ds.DataPoints) { if (dp.Selected) { dp.Select(true); if (ds.SelectionMode == SelectionModes.Single) dp.DeSelectOthers(); } else dp.DeSelect(dp, true, true); } } } } /// <summary> /// Get ColorSet by ColorSet name /// </summary> /// <param name="id">Name of the ColorSet</param> /// <returns>ColorSet</returns> internal ColorSet GetColorSetByName(String id) { ColorSet colorSet = null; if (ColorSets != null && ColorSets.Count > 0) colorSet = ColorSets.GetColorSetByName(id); if(colorSet == null) colorSet = EmbeddedColorSets.GetColorSetByName(id); return colorSet; } /// <summary> /// Fire Rendered event /// </summary> internal void FireRenderedEvent() { if (_rendered != null) _rendered(this, null); } /// <summary> /// Check if exception occurred due to negative size error /// </summary> /// <param name="e">ArgumentException</param> /// <returns>Boolean</returns> internal Boolean CheckSizeError(ArgumentException e) { if (e == null) return false; String stackTrace = e.StackTrace.TrimStart(); if (stackTrace.Contains("set_Height(Double value)") || stackTrace.Contains("set_Width(Double value)") || e.Message == "Height must be non-negative." || e.Message == "Width must be non-negative.") { #if DEBUG System.Diagnostics.Debug.WriteLine("------------------------------------------"); System.Diagnostics.Debug.WriteLine("The following SizeChanged Error is handled"); System.Diagnostics.Debug.WriteLine("------------------------------------------"); System.Diagnostics.Debug.WriteLine(e.Message); if (e.StackTrace != null) System.Diagnostics.Debug.WriteLine(e.StackTrace); System.Diagnostics.Debug.WriteLine("------------------------------------------"); #endif return true; } else return false; } /// <summary> /// On ToolTipText PropertyChanged event, this function is invoked /// </summary> /// <param name="NewValue">New text value</param> internal override void OnToolTipTextPropertyChanged(string newValue) { base.OnToolTipTextPropertyChanged(newValue); DetachToolTip(this._toolTipCanvas); if (!String.IsNullOrEmpty(newValue)) AttachToolTip(this, this, this); } /// <summary> /// Update position of the tooltip according to mouse position /// </summary> /// <param name="sender">FrameworkElement</param> /// <param name="e">MouseEventArgs</param> internal void UpdateToolTipPosition(object sender, MouseEventArgs e) { if (ToolTipEnabled && (Boolean)_toolTip.Enabled) { Double actualX = e.GetPosition(this).X; Double x = actualX; Double y = e.GetPosition(this).Y; #region Set position of ToolTip _toolTip.Measure(new Size(Double.MaxValue, Double.MaxValue)); _toolTip.UpdateLayout(); Size toolTipSize = Visifire.Commons.Graphics.CalculateVisualSize(_toolTip._borderElement); y = y - (toolTipSize.Height + 5); x = x - toolTipSize.Width / 2; if (x <= 0) { x = e.GetPosition(this).X + 10; y = e.GetPosition(this).Y + 20; if ((y + toolTipSize.Height) >= this.ActualHeight) y = this.ActualHeight - toolTipSize.Height; } if ((x + toolTipSize.Width) >= this.ActualWidth) { x = e.GetPosition(this).X - toolTipSize.Width; y = e.GetPosition(this).Y - toolTipSize.Height; } if (y < 0) y = e.GetPosition(this).Y + 20; if (x + toolTipSize.Width > this.ActualWidth) x = x + toolTipSize.Width - this.ActualWidth; if (toolTipSize.Width == _toolTip.MaxWidth) x = 0; if (x < 0) x = 0; // If tooltip still goes out of towards y if (!Double.IsNaN(this.ActualHeight) && y + toolTipSize.Height > this.ActualHeight) { y = 0; x = actualX + 10; if (x <= 0) x = e.GetPosition(this).X + 10; if ((x + toolTipSize.Width) >= this.ActualWidth) x = e.GetPosition(this).X - toolTipSize.Width; if (x + toolTipSize.Width > this.ActualWidth) x = x + toolTipSize.Width - this.ActualWidth; if (toolTipSize.Width == _toolTip.MaxWidth) x = 0; if (x < 0) x = 0; } _toolTip.SetValue(Canvas.LeftProperty, x); _toolTip.SetValue(Canvas.TopProperty, y); #endregion } else { _toolTip.Hide(); } } /// <summary> /// Get collection of titles which are docked inside PlotArea /// using LINQ /// </summary> /// <returns>List of Titles docked inside PlotArea </returns> internal List<Title> GetTitlesDockedInsidePlotArea() { if (Titles != null) { var titlesDockedInsidePlotArea = from title in Titles where (title.DockInsidePlotArea == true) select title; return titlesDockedInsidePlotArea.ToList<Title>(); } return null; } /// <summary> /// Get collection of titles which are docked outside PlotArea /// using LINQ /// </summary> /// <returns>List of Titles docked out side PlotArea</returns> internal List<Title> GetTitlesDockedOutSidePlotArea() { if (Titles != null) { var titlesDockedOutSidePlotArea = from title in Titles where (title.DockInsidePlotArea == false) select title; return titlesDockedOutSidePlotArea.ToList<Title>(); } else return null; } /// <summary> /// Render is a delegate to a method that takes no arguments and does not return a value, /// which is pushed onto the System.Windows.Threading.Dispatcher event queue. /// </summary> internal void InvokeRender() { if (_isTemplateApplied) { if (_renderLock) _renderLapsedCounter++; else { #if WPF if (Application.Current != null && Application.Current.Dispatcher.Thread != Thread.CurrentThread) Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, new RenderDelegate(Render)); else Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new RenderDelegate(Render)); #else if (IsInDesignMode) Render(); else this.Dispatcher.BeginInvoke(Render); #endif } } } /// <summary> /// Render redraws the chart /// </summary> internal void Render() { if (_isTemplateApplied && !_renderLock && _rootElement != null) { if (Double.IsNaN(this.ActualWidth) || Double.IsNaN(this.ActualHeight) || this.ActualWidth == 0 || this.ActualHeight == 0) return; _renderLock = true; try { PrepareChartAreaForDrawing(); ChartArea.Draw(this); } catch (Exception e) { _renderLock = false; if (CheckSizeError(e as ArgumentException)) return; else throw new Exception(e.Message, e); } } } /// <summary> /// Calculate font color of DataPoint labels depending upon chart background /// </summary> /// <param name="chart">Chart</param> /// <param name="dataPoint">DataPoint</param> /// <param name="labelFontColor">labelFontColor</param> /// <param name="labelStyle">labelStyle</param> /// <returns>Brush</returns> internal static Brush CalculateDataPointLabelFontColor(Chart chart, DataPoint dataPoint, Brush labelFontColor, LabelStyles labelStyle) { Brush returnBrush = dataPoint.LabelFontColor; if (labelFontColor == null) { Double intensity; if (labelStyle == LabelStyles.Inside && dataPoint.Parent.RenderAs != RenderAs.Line) { intensity = Graphics.GetBrushIntensity(dataPoint.Color); returnBrush = Graphics.GetDefaultFontColor(intensity); } else { if (chart.PlotArea.InternalBackground == null) { if (chart.Background == null) { returnBrush = new SolidColorBrush(Colors.Black); } else { intensity = Graphics.GetBrushIntensity(chart.Background); returnBrush = Graphics.GetDefaultFontColor(intensity); } } else { intensity = Graphics.GetBrushIntensity(chart.PlotArea.InternalBackground); returnBrush = Graphics.GetDefaultFontColor(intensity); } } } return returnBrush; } /// <summary> /// Calculate FontColor for text elements over chart depending upon chart background and dockInsidePlotArea /// </summary> /// <param name="chart">Chart</param> /// <param name="color">FontColor</param> /// <param name="dockInsidePlotArea">DockInsidePlotArea</param> /// <returns>Brush</returns> internal static Brush CalculateFontColor(Chart chart, Brush color, Boolean dockInsidePlotArea) { Brush brush = color; Double intensity; if (color == null) { if (!dockInsidePlotArea) { if (chart != null) { if (Graphics.AreBrushesEqual(chart.Background, new SolidColorBrush(Colors.Transparent)) || chart.Background == null) { brush = new SolidColorBrush(Colors.Black); } else { intensity = Graphics.GetBrushIntensity(chart.Background); brush = Graphics.GetDefaultFontColor(intensity); } } } else { if (chart.PlotArea != null) { if (Graphics.AreBrushesEqual(chart.PlotArea.InternalBackground, new SolidColorBrush(Colors.Transparent)) || chart.PlotArea.InternalBackground == null) { if (Graphics.AreBrushesEqual(chart.Background, new SolidColorBrush(Colors.Transparent)) || chart.Background == null) { brush = new SolidColorBrush(Colors.Black); } else { intensity = Graphics.GetBrushIntensity(chart.Background); brush = Graphics.GetDefaultFontColor(intensity); } } else { intensity = Graphics.GetBrushIntensity(chart.PlotArea.InternalBackground); brush = Graphics.GetDefaultFontColor(intensity); } } } } return brush; } #endregion #region Internal Events And Delegates #if WPF /// <summary> /// RenderDelegate is used for attaching Render() function as target while Invoking Render() using Dispatcher. /// </summary> internal delegate void RenderDelegate(); #endif #endregion #region Data private EventHandler _rendered; /// <summary> /// Whether shadow effect of chart is applied or not /// </summary> private Boolean _isShadowApplied; /// <summary> /// Chart area margin /// </summary> private Thickness _chartAreaMargin; /// <summary> /// Shadow Depth for chart /// </summary> internal static Double SHADOW_DEPTH = 4; /// <summary> /// Bevel Depth for chart /// </summary> internal static Double BEVEL_DEPTH = 5; /// <summary> /// Number of time render call is lapsed or failed due to render lock /// </summary> internal Int32 _renderLapsedCounter; /// <summary> /// Render lock is used to protect chart from multiple render /// </summary> internal bool _renderLock = false; /// <summary> /// Is used to handle inactive animation after first time render /// </summary> internal Boolean _internalAnimationEnabled = false; //#if WPF /// <summary> /// Whether Theme is changed by the user /// </summary> internal Boolean _isThemeChanged = false; //#endif #endregion } }