//copyright(c) 2016 Alberto Rodriguez //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 Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Shapes; using LiveCharts.Definitions.Charts; using LiveCharts.Dtos; using LiveCharts.Helpers; using LiveCharts.Uwp.Components; namespace LiveCharts.Uwp { /// /// An Axis section highlights values or ranges in a chart. /// public class AxisSection : FrameworkElement, IAxisSectionView { private readonly Rectangle _rectangle; private TextBlock _label; internal static AxisSection Dragging; /// /// Initializes a new instance of AxisSection class /// public AxisSection() { _rectangle = new Rectangle(); //_rectangle.MouseDown += (sender, args) => //{ // if (!Draggable) return; // Dragging = this; // args.Handled = true; // Chart.Ldsp = null; //}; //SetCurrentValue(StrokeProperty, new SolidColorBrush(Color.FromRgb(131, 172, 191))); //SetCurrentValue(FillProperty, new SolidColorBrush(Color.FromRgb(131, 172, 191)) {Opacity = .35}); //SetCurrentValue(StrokeThicknessProperty, 0d); } #region Properties /// /// Gets or sets the model. /// /// /// The model. /// public AxisSectionCore Model { get; set; } /// /// The label property /// public static readonly DependencyProperty LabelProperty = DependencyProperty.Register( "Label", typeof(string), typeof(AxisSection), new PropertyMetadata(default(string))); /// /// Gets or sets the name, the title of the section, a visual element will be added to the chart if this property is not null. /// [Obsolete("Use a VisualElement instead")] public string Label { get { return (string)GetValue(LabelProperty); } set { SetValue(LabelProperty, value); } } /// /// From value property /// public static readonly DependencyProperty FromValueProperty = DependencyProperty.Register( "FromValue", typeof(double), typeof(AxisSection), new PropertyMetadata(double.NaN, UpdateSection)); /// /// Gets or sets the value where the section starts /// [Obsolete("This property will be removed in future versions, instead use Value and SectionWidth properties")] public double FromValue { get { return (double)GetValue(FromValueProperty); } set { SetValue(FromValueProperty, value); } } /// /// To value property /// public static readonly DependencyProperty ToValueProperty = DependencyProperty.Register( "ToValue", typeof(double), typeof(AxisSection), new PropertyMetadata(double.NaN, UpdateSection)); /// /// Gets or sets the value where the section ends /// [Obsolete("This property will be removed in future versions, instead use Value and SectionWidth properties")] public double ToValue { get { return (double)GetValue(ToValueProperty); } set { SetValue(ToValueProperty, value); } } /// /// The value property /// public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(double), typeof(AxisSection), new PropertyMetadata(default(double), UpdateSection)); /// /// Gets or sets the value where the section is drawn /// public double Value { get { return (double) GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } /// /// The section width property /// public static readonly DependencyProperty SectionWidthProperty = DependencyProperty.Register( "SectionWidth", typeof(double), typeof(AxisSection), new PropertyMetadata(default(double), UpdateSection)); /// /// Gets or sets the section width /// public double SectionWidth { get { return (double) GetValue(SectionWidthProperty); } set { SetValue(SectionWidthProperty, value); } } /// /// The stroke property /// public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register( "Stroke", typeof(Brush), typeof(AxisSection), new PropertyMetadata(default(Brush))); /// /// Gets o sets the section stroke, the stroke brush will be used to draw the border of the section /// public Brush Stroke { get { return (Brush)GetValue(StrokeProperty); } set { SetValue(StrokeProperty, value); } } /// /// The fill property /// public static readonly DependencyProperty FillProperty = DependencyProperty.Register( "Fill", typeof(Brush), typeof(AxisSection), new PropertyMetadata(default(Brush))); /// /// Gets or sets the section fill brush. /// public Brush Fill { get { return (Brush)GetValue(FillProperty); } set { SetValue(FillProperty, value); } } /// /// The stroke thickness property /// public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register( "StrokeThickness", typeof(double), typeof(AxisSection), new PropertyMetadata(default(double))); /// /// Gets or sets the stroke thickness. /// public double StrokeThickness { get { return (double)GetValue(StrokeThicknessProperty); } set { SetValue(StrokeThicknessProperty, value); } } /// /// The stroke dash array property /// public static readonly DependencyProperty StrokeDashArrayProperty = DependencyProperty.Register( "StrokeDashArray", typeof(DoubleCollection), typeof(AxisSection), new PropertyMetadata(default(DoubleCollection))); /// /// Gets or sets the stroke dash array collection, use this property to create dashed stroke sections /// public DoubleCollection StrokeDashArray { get { return (DoubleCollection)GetValue(StrokeDashArrayProperty); } set { SetValue(StrokeDashArrayProperty, value); } } /// /// The draggable property /// public static readonly DependencyProperty DraggableProperty = DependencyProperty.Register( "Draggable", typeof(bool), typeof(AxisSection), new PropertyMetadata(default(bool))); /// /// Gets or sets if a user can drag the section /// public bool Draggable { get { return (bool) GetValue(DraggableProperty); } set { SetValue(DraggableProperty, value); } } /// /// The disable animations property /// public static readonly DependencyProperty DisableAnimationsProperty = DependencyProperty.Register( "DisableAnimations", typeof(bool), typeof(AxisSection), new PropertyMetadata(default(bool))); /// /// Gets or sets a value indicating whether the section is animated /// /// /// true if [disable animations]; otherwise, false. /// public bool DisableAnimations { get { return (bool) GetValue(DisableAnimationsProperty); } set { SetValue(DisableAnimationsProperty, value); } } /// /// The data label property /// public static readonly DependencyProperty DataLabelProperty = DependencyProperty.Register( "DataLabel", typeof(bool), typeof(AxisSection), new PropertyMetadata(default(bool))); /// /// Gets or sets a value indicating whether the section should display a label that displays its current value. /// /// /// true if [data label]; otherwise, false. /// public bool DataLabel { get { return (bool) GetValue(DataLabelProperty); } set { SetValue(DataLabelProperty, value); } } /// /// The data label brush property /// public static readonly DependencyProperty DataLabelForegroundProperty = DependencyProperty.Register( "DataLabelForeground", typeof(Brush), typeof(AxisSection), new PropertyMetadata(default(Brush))); /// /// Gets or sets the data label brush. /// /// /// The label brush. /// public Brush DataLabelForeground { get { return (Brush) GetValue(DataLabelForegroundProperty); } set { SetValue(DataLabelForegroundProperty, value); } } #endregion /// /// Draws the or move. /// /// The source. /// The axis. public void DrawOrMove(AxisOrientation source, int axis) { _rectangle.Fill = Fill; _rectangle.Stroke = Stroke; _rectangle.StrokeDashArray = StrokeDashArray; _rectangle.StrokeThickness = StrokeThickness; Canvas.SetZIndex(_rectangle, Canvas.GetZIndex(this)); BindingOperations.SetBinding(_rectangle, VisibilityProperty, new Binding {Path = new PropertyPath(nameof(Visibility)), Source = this}); var ax = source == AxisOrientation.X ? Model.Chart.AxisX[axis] : Model.Chart.AxisY[axis]; var uw = ax.EvaluatesUnitWidth ? ChartFunctions.GetUnitWidth(source, Model.Chart, axis) / 2 : 0; if (Parent == null) { _label = ((Axis) ax.View).BindATextBlock(); _label.Padding = new Thickness(5, 2, 5, 2); Model.Chart.View.AddToView(this); Model.Chart.View.AddToDrawMargin(_rectangle); Model.Chart.View.AddToView(_label); _rectangle.Height = 0; _rectangle.Width = 0; Canvas.SetLeft(_rectangle, 0d); Canvas.SetTop(_rectangle, Model.Chart.DrawMargin.Height); #region Obsolete Canvas.SetTop(_label, Model.Chart.DrawMargin.Height); Canvas.SetLeft(_label, 0d); #endregion } #pragma warning disable 618 var from = ChartFunctions.ToDrawMargin(double.IsNaN(FromValue) ? Value : FromValue, source, Model.Chart, axis) + uw; #pragma warning restore 618 #pragma warning disable 618 var to = ChartFunctions.ToDrawMargin(double.IsNaN(ToValue) ? Value + SectionWidth : ToValue, source, Model.Chart, axis) + uw; #pragma warning restore 618 if (from > to) { var temp = to; to = from; from = temp; } var anSpeed = Model.Chart.View.AnimationsSpeed; if (DataLabel) { if (DataLabelForeground != null) _label.Foreground = DataLabelForeground; _label.UpdateLayout(); //_label.Background = Stroke ?? Fill; PlaceLabel(ax.GetFormatter()(Value), ax, source); } if (source == AxisOrientation.X) { var w = to - from; w = StrokeThickness > w ? StrokeThickness : w; Canvas.SetTop(_rectangle, 0); _rectangle.Height = Model.Chart.DrawMargin.Height; if (Model.Chart.View.DisableAnimations || DisableAnimations) { _rectangle.Width = w > 0 ? w : 0; Canvas.SetLeft(_rectangle, from - StrokeThickness/2); } else { _rectangle.BeginDoubleAnimation(nameof(Width), w > 0 ? w : 0, anSpeed); _rectangle.BeginDoubleAnimation("Canvas.Left", from - StrokeThickness / 2, anSpeed); } return; } var h = to - from; h = StrokeThickness > h ? StrokeThickness : h; Canvas.SetLeft(_rectangle, 0d); _rectangle.Width = Model.Chart.DrawMargin.Width; if (Model.Chart.View.DisableAnimations || DisableAnimations) { Canvas.SetTop(_rectangle, from - StrokeThickness/2); _rectangle.Height = h > 0 ? h : 0; } else { _rectangle.BeginDoubleAnimation("Canvas.Left", from, anSpeed); _rectangle.BeginDoubleAnimation(nameof(Height), h, anSpeed); } } /// /// Removes this instance. /// public void Remove() { Model.Chart.View.RemoveFromView(this); Model.Chart.View.RemoveFromDrawMargin(_rectangle); Model.Chart.View.RemoveFromDrawMargin(_label); } /// /// Ases the core element. /// /// The axis. /// The source. /// public AxisSectionCore AsCoreElement(AxisCore axis, AxisOrientation source) { var model = new AxisSectionCore(this, axis.Chart); model.View.Model = model; return model; } private static void UpdateSection(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { var section = (AxisSection) dependencyObject; if (section.Model != null && section.Model.Chart != null) { if (!section.Model.Chart.AreComponentsLoaded) return; section.DrawOrMove(section.Model.Source, section.Model.AxisIndex); } } private void PlaceLabel(string text, AxisCore axis, AxisOrientation source) { _label.Text = text; _label.UpdateLayout(); var transform = new LabelEvaluation(axis.View.LabelsRotation, _label.Width + 10, _label.Height, axis, source); _label.RenderTransform = Math.Abs(transform.LabelAngle) > 1 ? new RotateTransform {Angle = transform.LabelAngle} : null; var toLine = ChartFunctions.ToPlotArea(Value, source, Model.Chart, axis); var direction = source == AxisOrientation.X ? 1 : -1; toLine += axis.EvaluatesUnitWidth ? direction * ChartFunctions.GetUnitWidth(source, Model.Chart, axis) / 2 : 0; var toLabel = toLine + transform.GetOffsetBySource(source); var chart = Model.Chart; if (axis.IsMerged) { const double padding = 4; if (source == AxisOrientation.Y) { if (toLabel + transform.ActualHeight > chart.DrawMargin.Top + chart.DrawMargin.Height) toLabel -= transform.ActualHeight + padding; } else { if (toLabel + transform.ActualWidth > chart.DrawMargin.Left + chart.DrawMargin.Width) toLabel -= transform.ActualWidth + padding; } } var labelTab = axis.Tab; labelTab += transform.GetOffsetBySource(source.Invert()); if (source == AxisOrientation.Y) { if (Model.View.DisableAnimations || DisableAnimations) { Canvas.SetLeft(_label, labelTab); Canvas.SetTop(_label, toLabel); return; } _label.BeginDoubleAnimation("Canvas.Top", toLabel, chart.View.AnimationsSpeed); _label.BeginDoubleAnimation("Canvas.Left", labelTab, chart.View.AnimationsSpeed); } else { if (Model.View.DisableAnimations || DisableAnimations) { Canvas.SetLeft(_label, toLabel); Canvas.SetTop(_label, labelTab); return; } _label.BeginDoubleAnimation("Canvas.Left", toLabel, chart.View.AnimationsSpeed); _label.BeginDoubleAnimation("Canvas.Top", labelTab, chart.View.AnimationsSpeed); } } } }