//The MIT License(MIT) //Copyright(c) 2016 Alberto Rodriguez & LiveCharts Contributors //Permission is hereby granted, free of charge, to any person obtaining a copy //of this software and associated documentation files (the "Software"), to deal //in the Software without restriction, including without limitation the rights //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell //copies of the Software, and to permit persons to whom the Software is //furnished to do so, subject to the following conditions: //The above copyright notice and this permission notice shall be included in all //copies or substantial portions of the Software. //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE //SOFTWARE. using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; using LiveCharts.Defaults; using LiveCharts.Definitions.Points; using LiveCharts.Definitions.Series; using LiveCharts.Helpers; using LiveCharts.Wpf.Charts.Base; using LiveCharts.Wpf.Components; using LiveCharts.Wpf.Converters; namespace LiveCharts.Wpf { /// /// Base WPF and WinForms series, this class is abstract /// public abstract class Series : FrameworkElement, ISeriesView { #region Constructors /// /// Initializes a new Instance of Series /// protected Series() { DefaultFillOpacity = 0.35; SetCurrentValue(TitleProperty, "Series"); IsVisibleChanged += OnIsVisibleChanged; IsFirstDraw = true; } /// /// Initializes a new Instance of series, with a given configuration /// /// protected Series(object configuration) { Configuration = configuration; SetValue(TitleProperty, "Series"); IsVisibleChanged += OnIsVisibleChanged; IsFirstDraw = true; } #endregion #region Properties private IChartValues LastKnownValues { get; set; } internal double DefaultFillOpacity { get; set; } /// /// Gets a value indicating whether this instance is first draw. /// /// /// true if this instance is first draw; otherwise, false. /// public bool IsFirstDraw { get; internal set; } /// /// THe Model is set by every series type, it is the motor of the series, it is the communication with the core of the library /// public SeriesAlgorithm Model { get; set; } /// /// Gets the Actual values in the series, active or visible series only /// public IChartValues ActualValues { get { if (DesignerProperties.GetIsInDesignMode(this) && (Values == null || Values.Count == 0)) SetValue(ValuesProperty, GetValuesForDesigner()); return Values ?? new ChartValues(); } } /// /// Gets whether the series is visible /// public bool IsSeriesVisible { get { return Visibility == Visibility.Visible; } } /// /// Gets the current chart points in the series /// public IEnumerable ChartPoints { get { return ActualValues.GetPoints(this); } } /// /// The values property /// public static readonly DependencyProperty ValuesProperty = DependencyProperty.Register( "Values", typeof (IChartValues), typeof (Series), new PropertyMetadata(default(IChartValues), OnValuesInstanceChanged)); /// /// Gets or sets chart values. /// [TypeConverter(typeof(NumericChartValuesConverter))] public IChartValues Values { get { return ThreadAccess.Resolve(this, ValuesProperty); } set { SetValue(ValuesProperty, value); } } /// /// The title property /// public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( "Title", typeof (string), typeof (Series), new PropertyMetadata(default(string), CallChartUpdater())); /// /// Gets or sets series title /// public string Title { get { return (string) GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// The stroke property /// public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register( "Stroke", typeof (Brush), typeof (Series), new PropertyMetadata(default(Brush), CallChartUpdater())); /// /// Gets or sets series stroke, if this property is null then a SolidColorBrush will be assigned according to series position in collection and Chart.Colors property /// public Brush Stroke { get { return (Brush) GetValue(StrokeProperty); } set { SetValue(StrokeProperty, value); } } /// /// The stroke thickness property /// public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register( "StrokeThickness", typeof (double), typeof (Series), new PropertyMetadata(default(double), CallChartUpdater())); /// /// Gets or sets the series stroke thickness. /// public double StrokeThickness { get { return (double) GetValue(StrokeThicknessProperty); } set { SetValue(StrokeThicknessProperty, value); } } /// /// The fill property /// public static readonly DependencyProperty FillProperty = DependencyProperty.Register( "Fill", typeof (Brush), typeof (Series), new PropertyMetadata(default(Brush), CallChartUpdater())); /// /// Gets or sets series fill color, if this property is null then a SolidColorBrush will be assigned according to series position in collection and Chart.Colors property, also Fill property has a default opacity according to chart type. /// public Brush Fill { get { return (Brush) GetValue(FillProperty); } set { SetValue(FillProperty, value); } } /// /// The data labels property /// public static readonly DependencyProperty DataLabelsProperty = DependencyProperty.Register( "DataLabels", typeof (bool), typeof (Series), new PropertyMetadata(default(bool), CallChartUpdater())); /// /// Gets or sets if series should include a label over each data point. /// public bool DataLabels { get { return (bool) GetValue(DataLabelsProperty); } set { SetValue(DataLabelsProperty, value); } } /// /// The labels template property /// public static readonly DependencyProperty DataLabelsTemplateProperty = DependencyProperty.Register( "DataLabelsTemplate", typeof(DataTemplate), typeof(Series), new PropertyMetadata(DefaultXamlReader.DataLabelTemplate(), CallChartUpdater())); /// /// Gets or sets the labels template. /// /// /// The labels template. /// public DataTemplate DataLabelsTemplate { get { return (DataTemplate) GetValue(DataLabelsTemplateProperty); } set { SetValue(DataLabelsTemplateProperty, value); } } /// /// The font family property /// public static readonly DependencyProperty FontFamilyProperty = DependencyProperty.Register( "FontFamily", typeof (FontFamily), typeof (Series), new PropertyMetadata(new FontFamily("Segoe UI"))); /// /// Gets or sets labels font family /// public FontFamily FontFamily { get { return (FontFamily) GetValue(FontFamilyProperty); } set { SetValue(FontFamilyProperty, value); } } /// /// The font size property /// public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register( "FontSize", typeof (double), typeof (Series), new PropertyMetadata(10d, CallChartUpdater())); /// /// Gets or sets labels font size /// public double FontSize { get { return (double)GetValue(FontSizeProperty); } set { SetValue(FontSizeProperty, value); } } /// /// The font weight property /// public static readonly DependencyProperty FontWeightProperty = DependencyProperty.Register( "FontWeight", typeof (FontWeight), typeof (Series), new PropertyMetadata(FontWeights.Bold, CallChartUpdater())); /// /// Gets or sets labels font weight /// public FontWeight FontWeight { get { return (FontWeight)GetValue(FontWeightProperty); } set { SetValue(FontWeightProperty, value); } } /// /// The font style property /// public static readonly DependencyProperty FontStyleProperty = DependencyProperty.Register( "FontStyle", typeof (FontStyle), typeof (Series), new PropertyMetadata(FontStyles.Normal, CallChartUpdater())); /// /// Gets or sets labels font style /// public FontStyle FontStyle { get { return (FontStyle)GetValue(FontStyleProperty); } set { SetValue(FontStyleProperty, value); } } /// /// The font stretch property /// public static readonly DependencyProperty FontStretchProperty = DependencyProperty.Register( "FontStretch", typeof (FontStretch), typeof (Series), new PropertyMetadata(FontStretches.Normal, CallChartUpdater())); /// /// Gets or sets labels font stretch /// public FontStretch FontStretch { get { return (FontStretch)GetValue(FontStretchProperty); } set { SetValue(FontStretchProperty, value); } } /// /// The foreground property /// public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register( "Foreground", typeof (Brush), typeof (Series), new PropertyMetadata(new SolidColorBrush(Color.FromRgb(55, 71, 79)))); /// /// Gets or sets labels text color. /// public Brush Foreground { get { return (Brush)GetValue(ForegroundProperty); } set { SetValue(ForegroundProperty, value); } } /// /// The stroke dash array property /// public static readonly DependencyProperty StrokeDashArrayProperty = DependencyProperty.Register( "StrokeDashArray", typeof(DoubleCollection), typeof(Series), new PropertyMetadata(default(DoubleCollection))); /// /// Gets or sets the stroke dash array of a series, sue this property to draw dashed strokes /// public DoubleCollection StrokeDashArray { get { return (DoubleCollection)GetValue(StrokeDashArrayProperty); } set { SetValue(StrokeDashArrayProperty, value); } } /// /// The point geometry property /// public static readonly DependencyProperty PointGeometryProperty = DependencyProperty.Register("PointGeometry", typeof (Geometry), typeof (Series), new PropertyMetadata(DefaultGeometries.Circle, CallChartUpdater())); /// /// Gets or sets the point geometry, this shape will be drawn in the Tooltip, Legend, and if line series in every point also. /// public Geometry PointGeometry { get { return ((Geometry)GetValue(PointGeometryProperty)); } set { SetValue(PointGeometryProperty, value); } } /// /// The scales x at property /// public static readonly DependencyProperty ScalesXAtProperty = DependencyProperty.Register( "ScalesXAt", typeof (int), typeof (Series), new PropertyMetadata(default(int), CallChartUpdater())); /// /// Gets or sets the axis where series is scaled at, the axis must exist in the collection /// public int ScalesXAt { get { return (int) GetValue(ScalesXAtProperty); } set { SetValue(ScalesXAtProperty, value); } } /// /// The scales y at property /// public static readonly DependencyProperty ScalesYAtProperty = DependencyProperty.Register( "ScalesYAt", typeof (int), typeof (Series), new PropertyMetadata(default(int), CallChartUpdater())); /// /// Gets or sets the axis where series is scaled at, the axis must exist in the collection /// public int ScalesYAt { get { return (int) GetValue(ScalesYAtProperty); } set { SetValue(ScalesYAtProperty, value); } } /// /// The label point property /// public static readonly DependencyProperty LabelPointProperty = DependencyProperty.Register( "LabelPoint", typeof (Func), typeof (Series), new PropertyMetadata(default(Func))); /// /// Gets or sets the label formatter for the data label and tooltip, this property is set by default according to the series /// public Func LabelPoint { get { return (Func) GetValue(LabelPointProperty); } set { SetValue(LabelPointProperty, value); } } /// /// The configuration property /// public static readonly DependencyProperty ConfigurationProperty = DependencyProperty.Register( "Configuration", typeof (object), typeof (Series), new PropertyMetadata(default(object), CallChartUpdater())); /// /// Gets or sets series mapper, if this property is set then the library will ignore the SeriesCollection mapper and global mappers. /// public object Configuration { get { return GetValue(ConfigurationProperty); } set { SetValue(ConfigurationProperty, value); } } #endregion #region Internal Helpers internal ContentControl UpdateLabelContent(DataLabelViewModel content, ContentControl currentControl) { ContentControl control; if (currentControl == null) { control = new ContentControl(); control.SetBinding(VisibilityProperty, new Binding {Path = new PropertyPath(VisibilityProperty), Source = this}); Panel.SetZIndex(control, int.MaxValue - 1); Model.Chart.View.AddToDrawMargin(control); } else { control = currentControl; } control.Content = content; control.ContentTemplate = DataLabelsTemplate; control.FontFamily = FontFamily; control.FontSize = FontSize; control.FontStretch = FontStretch; control.FontStyle = FontStyle; control.FontWeight = FontWeight; control.Foreground = Foreground; return control; } #endregion #region Publics /// /// Gets the view of a given point /// /// /// /// public virtual IChartPointView GetPointView(ChartPoint point, string label) { throw new NotImplementedException(); } /// /// This method runs when the update starts /// public virtual void OnSeriesUpdateStart() { } /// /// Erases series /// public virtual void Erase(bool removeFromView = true) { Values.GetPoints(this).ForEach(p => { if (p.View != null) p.View.RemoveFromView(Model.Chart); }); if (removeFromView) Model.Chart.View.RemoveFromView(this); } /// /// This method runs when the update finishes /// public virtual void OnSeriesUpdatedFinish() { IsFirstDraw = false; } /// /// Initializes the series colors if they are not set /// public virtual void InitializeColors() { var wpfChart = (Chart) Model.Chart.View; if (Stroke != null && Fill != null) return; var nextColor = wpfChart.GetNextDefaultColor(); if (Stroke == null) { var strokeBrush = new SolidColorBrush(nextColor); strokeBrush.Freeze(); SetValue(StrokeProperty, strokeBrush); } if (Fill == null) { var fillBursh = new SolidColorBrush(nextColor) {Opacity = DefaultFillOpacity}; fillBursh.Freeze(); SetValue(FillProperty, fillBursh); } } /// /// Defines special elements to draw according to the series type /// public virtual void DrawSpecializedElements() { } /// /// Places specializes items /// public virtual void PlaceSpecializedElements() { } /// /// Gets the label point formatter. /// /// public Func GetLabelPointFormatter() { if (DesignerProperties.GetIsInDesignMode(this)) return x => "Label"; return LabelPoint; } #endregion private static void OnValuesInstanceChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { var series = (Series) dependencyObject; if (series.Values != series.LastKnownValues && series.LastKnownValues != null) { series.LastKnownValues.GetPoints(series).ForEach( x => { if (x.View != null) x.View.RemoveFromView(series.Model.Chart); }); } CallChartUpdater()(dependencyObject, dependencyPropertyChangedEventArgs); series.LastKnownValues = series.Values; } /// /// Calls the chart updater. /// /// if set to true [animate]. /// protected static PropertyChangedCallback CallChartUpdater(bool animate = false) { return (o, args) => { var wpfSeries = o as Series; if (wpfSeries == null) return; if (wpfSeries.Model == null) return; if (wpfSeries.Model.Chart != null) wpfSeries.Model.Chart.Updater.Run(animate); }; } private Visibility? PreviousVisibility { get; set; } private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { if (Model.Chart == null || PreviousVisibility == Visibility) return; PreviousVisibility = Visibility; if (PreviousVisibility != null) Model.Chart.Updater.Run(false, false); if (Visibility == Visibility.Collapsed || Visibility == Visibility.Hidden) { Erase(false); } } private static IChartValues GetValuesForDesigner() { var r = new Random(); var gvt = Type.GetType("LiveCharts.Geared.GearedValues`1, LiveCharts.Geared"); if (gvt != null) gvt = gvt.MakeGenericType(typeof(ObservableValue)); var obj = gvt != null ? (IChartValues) Activator.CreateInstance(gvt) : new ChartValues(); obj.Add(new ObservableValue(r.Next(0, 100))); obj.Add(new ObservableValue(r.Next(0, 100))); obj.Add(new ObservableValue(r.Next(0, 100))); obj.Add(new ObservableValue(r.Next(0, 100))); obj.Add(new ObservableValue(r.Next(0, 100))); return obj; } #region Obsoletes #endregion } }