项目结构调整

This commit is contained in:
艾竹
2023-04-16 20:11:40 +08:00
parent cbfbf96033
commit 81f91f3f35
2124 changed files with 218 additions and 5516 deletions

View File

@@ -0,0 +1,164 @@
//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 Windows.Foundation;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Shapes;
namespace LiveCharts.Uwp.Components
{
internal static class AnimationsHelper
{
public static DoubleAnimation CreateDouble(double? to, Duration duration, string targetProperty)
{
var animation = new DoubleAnimation
{
To = to,
Duration = duration,
EnableDependentAnimation = true
};
Storyboard.SetTargetProperty(animation, targetProperty);
return animation;
}
public static PointAnimation CreatePoint(Point to, Duration duration, string targetProperty)
{
var animation = new PointAnimation
{
To = to,
Duration = duration,
EnableDependentAnimation = true
};
Storyboard.SetTargetProperty(animation, targetProperty);
return animation;
}
public static PointAnimation CreatePoint(Point from, Point to, Duration duration, string targetProperty)
{
var animation = new PointAnimation
{
From = from,
To = to,
Duration = duration
};
Storyboard.SetTargetProperty(animation, targetProperty);
return animation;
}
public static ColorAnimation CreateColor(Color to, Duration duration, string targetProperty)
{
var animation = new ColorAnimation
{
To = to,
Duration = duration,
EnableDependentAnimation = true
};
Storyboard.SetTargetProperty(animation, targetProperty);
return animation;
}
public static void BeginDoubleAnimation(this DependencyObject target, string path, double? to,
Duration duration)
{
var animation = new DoubleAnimation
{
To = to,
Duration = duration,
EnableDependentAnimation = true
};
target.BeginAnimation(animation, path);
}
public static void BeginDoubleAnimation(this DependencyObject target, string path, double? from, double? to,
Duration duration)
{
var animation = new DoubleAnimation
{
From = from,
To = to,
Duration = duration,
EnableDependentAnimation = true
};
target.BeginAnimation(animation, path);
}
public static void BeginPointAnimation(this DependencyObject target, string path, Point to, Duration duration)
{
var animation = CreatePoint(to, duration, path);
target.BeginAnimation(animation, path);
}
public static void BeginColorAnimation(this DependencyObject target, string path, Color to, Duration duration)
{
var animation = CreateColor(to, duration, path);
target.BeginAnimation(animation, path);
}
public static void BeginAnimation(this DependencyObject target, Timeline animation, string path)
{
var sb = new Storyboard();
Storyboard.SetTarget(animation, target);
Storyboard.SetTargetProperty(animation, path);
sb.Children.Add(animation);
sb.Begin();
}
public static void CreateStoryBoardAndBegin(DependencyObject target, params Timeline[] animation)
{
var sb = new Storyboard();
foreach (var timeline in animation)
{
Storyboard.SetTarget(timeline, target);
sb.Children.Add(timeline);
}
sb.Begin();
}
public static void CreateCanvasStoryBoardAndBegin(this DependencyObject target, double? toX, double? toY, Duration duration)
{
var xAnimation = CreateDouble(toX, duration, "(Canvas.Left)");
var yAnimation = CreateDouble(toY, duration, "(Canvas.Top)");
CreateStoryBoardAndBegin(target, xAnimation, yAnimation);
}
public static void CreateY1Y2StoryBoardAndBegin(this DependencyObject target, double? toY1, double? toY2,
Duration duration)
{
var y1Animation = CreateDouble(toY1, duration, nameof(Line.Y1));
var y2Animation = CreateDouble(toY2, duration, nameof(Line.Y2));
CreateStoryBoardAndBegin(target, y1Animation, y2Animation);
}
public static Storyboard CreateStoryBoard(DependencyObject target,params Timeline[] animation)
{
var sb = new Storyboard();
foreach (var timeline in animation)
{
Storyboard.SetTarget(timeline, target);
sb.Children.Add(timeline);
}
return sb;
}
}
}

View File

@@ -0,0 +1,243 @@
//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 Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Shapes;
using LiveCharts.Charts;
using LiveCharts.Definitions.Charts;
using LiveCharts.Dtos;
using LiveCharts.Uwp.Charts.Base;
namespace LiveCharts.Uwp.Components
{
/// <summary>
///
/// </summary>
/// <seealso cref="LiveCharts.Definitions.Charts.ISeparatorElementView" />
public class AxisSeparatorElement : ISeparatorElementView
{
/// <summary>
/// Initializes a new instance of the <see cref="AxisSeparatorElement"/> class.
/// </summary>
/// <param name="model">The model.</param>
public AxisSeparatorElement(SeparatorElementCore model)
{
Model = model;
}
internal TextBlock TextBlock { get; set; }
internal Line Line { get; set; }
/// <summary>
/// Gets the label model.
/// </summary>
/// <value>
/// The label model.
/// </value>
public LabelEvaluation LabelModel { get; private set; }
/// <summary>
/// Gets the model.
/// </summary>
/// <value>
/// The model.
/// </value>
public SeparatorElementCore Model { get; }
/// <summary>
/// Updates the label.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="axis">The axis.</param>
/// <param name="source">The source.</param>
/// <returns></returns>
public LabelEvaluation UpdateLabel(string text, AxisCore axis, AxisOrientation source)
{
TextBlock.Text = text;
TextBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var transform = new LabelEvaluation(axis.View.LabelsRotation,
TextBlock.DesiredSize.Width, TextBlock.DesiredSize.Height, axis, source);
TextBlock.RenderTransform = Math.Abs(transform.LabelAngle) > 1
? new RotateTransform { Angle = transform.LabelAngle }
: null;
LabelModel = transform;
return transform;
}
/// <summary>
/// Clears the specified chart.
/// </summary>
/// <param name="chart">The chart.</param>
public void Clear(IChartView chart)
{
chart.RemoveFromView(TextBlock);
chart.RemoveFromView(Line);
TextBlock = null;
Line = null;
}
/// <summary>
/// Places the specified chart.
/// </summary>
/// <param name="chart">The chart.</param>
/// <param name="axis">The axis.</param>
/// <param name="direction">The direction.</param>
/// <param name="axisIndex">Index of the axis.</param>
/// <param name="toLabel">To label.</param>
/// <param name="toLine">To line.</param>
/// <param name="tab">The tab.</param>
public void Place(ChartCore chart, AxisCore axis, AxisOrientation direction, int axisIndex,
double toLabel, double toLine, double tab)
{
if (direction == AxisOrientation.Y)
{
Line.X1 = chart.DrawMargin.Left;
Line.X2 = chart.DrawMargin.Left + chart.DrawMargin.Width;
Line.Y1 = toLine;
Line.Y2 = toLine;
Canvas.SetLeft(TextBlock, tab);
Canvas.SetTop(TextBlock, toLabel);
}
else
{
Line.X1 = toLine;
Line.X2 = toLine;
Line.Y1 = chart.DrawMargin.Top;
Line.Y2 = chart.DrawMargin.Top + chart.DrawMargin.Height;
Canvas.SetLeft(TextBlock, toLabel);
Canvas.SetTop(TextBlock, tab);
}
}
/// <summary>
/// Removes the specified chart.
/// </summary>
/// <param name="chart">The chart.</param>
public void Remove(ChartCore chart)
{
chart.View.RemoveFromView(TextBlock);
chart.View.RemoveFromView(Line);
TextBlock = null;
Line = null;
}
/// <summary>
/// Moves the specified chart.
/// </summary>
/// <param name="chart">The chart.</param>
/// <param name="axis">The axis.</param>
/// <param name="direction">The direction.</param>
/// <param name="axisIndex">Index of the axis.</param>
/// <param name="toLabel">To label.</param>
/// <param name="toLine">To line.</param>
/// <param name="tab">The tab.</param>
public void Move(ChartCore chart, AxisCore axis, AxisOrientation direction, int axisIndex, double toLabel, double toLine, double tab)
{
if (direction == AxisOrientation.Y)
{
var x1 = AnimationsHelper.CreateDouble(chart.DrawMargin.Left, chart.View.AnimationsSpeed, nameof(Line.X1));
var x2 = AnimationsHelper.CreateDouble(chart.DrawMargin.Left + chart.DrawMargin.Width, chart.View.AnimationsSpeed, nameof(Line.X2));
var y1 = AnimationsHelper.CreateDouble(toLine, chart.View.AnimationsSpeed, nameof(Line.Y1));
var y2 = AnimationsHelper.CreateDouble(toLine, chart.View.AnimationsSpeed, nameof(Line.Y2));
AnimationsHelper.CreateStoryBoardAndBegin(Line, x1, x2, y1, y2);
var tb1 = AnimationsHelper.CreateDouble(toLabel, chart.View.AnimationsSpeed, "(Canvas.Top)");
var tb2 = AnimationsHelper.CreateDouble(tab, chart.View.AnimationsSpeed, "(Canvas.Left)");
AnimationsHelper.CreateStoryBoardAndBegin(TextBlock, tb1, tb2);
}
else
{
var x1 = AnimationsHelper.CreateDouble(toLine, chart.View.AnimationsSpeed, nameof(Line.X1));
var x2 = AnimationsHelper.CreateDouble(toLine, chart.View.AnimationsSpeed, nameof(Line.X2));
var y1 = AnimationsHelper.CreateDouble(chart.DrawMargin.Top, chart.View.AnimationsSpeed, nameof(Line.Y1));
var y2 = AnimationsHelper.CreateDouble(chart.DrawMargin.Top + chart.DrawMargin.Height, chart.View.AnimationsSpeed, nameof(Line.Y2));
AnimationsHelper.CreateStoryBoardAndBegin(Line, x1, x2, y1, y2);
var tb1 = AnimationsHelper.CreateDouble(toLabel, chart.View.AnimationsSpeed, "(Canvas.Left)");
var tb2 = AnimationsHelper.CreateDouble(tab, chart.View.AnimationsSpeed, "(Canvas.Top)");
AnimationsHelper.CreateStoryBoardAndBegin(TextBlock, tb1, tb2);
}
}
/// <summary>
/// Fades the in.
/// </summary>
/// <param name="axis">The axis.</param>
/// <param name="chart">The chart.</param>
public void FadeIn(AxisCore axis, ChartCore chart)
{
if (TextBlock.Visibility != Visibility.Collapsed)
TextBlock.BeginDoubleAnimation("Opacity", 0, 1, chart.View.AnimationsSpeed);
if (Line.Visibility != Visibility.Collapsed)
Line.BeginDoubleAnimation("Opacity", 0, 1, chart.View.AnimationsSpeed);
}
/// <summary>
/// Fades the out and remove.
/// </summary>
/// <param name="chart">The chart.</param>
public void FadeOutAndRemove(ChartCore chart)
{
if (TextBlock.Visibility == Visibility.Collapsed &&
Line.Visibility == Visibility.Collapsed) return;
var anim = new DoubleAnimation
{
From = 1,
To = 0,
Duration = chart.View.AnimationsSpeed
};
var dispatcher = ((Chart) chart.View).Dispatcher;
anim.Completed += (sender, args) =>
{
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
chart.View.RemoveFromView(TextBlock);
chart.View.RemoveFromView(Line);
});
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
};
TextBlock.BeginAnimation(anim, "Opacity");
Line.BeginDoubleAnimation("Opacity", 1, 0, chart.View.AnimationsSpeed);
}
}
}

View File

@@ -0,0 +1,78 @@
//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 Windows.UI.Xaml.Media;
namespace LiveCharts.Uwp.Components
{
internal static class BrushCloner
{
public static Brush Clone(this Brush brush)
{
var colorBrush = brush as SolidColorBrush;
if (colorBrush != null)
{
return new SolidColorBrush
{
Color = colorBrush.Color,
Opacity = colorBrush.Opacity,
Transform = colorBrush.Transform,
RelativeTransform = colorBrush.RelativeTransform
};
}
var gradientBrush = brush as LinearGradientBrush;
if (gradientBrush != null)
{
return new LinearGradientBrush
{
ColorInterpolationMode = gradientBrush.ColorInterpolationMode,
EndPoint = gradientBrush.EndPoint,
GradientStops = gradientBrush.GradientStops,
MappingMode = gradientBrush.MappingMode,
Opacity = gradientBrush.Opacity,
RelativeTransform = gradientBrush.RelativeTransform,
StartPoint = gradientBrush.StartPoint,
SpreadMethod = gradientBrush.SpreadMethod,
Transform = gradientBrush.Transform
};
}
var imageBrush = brush as ImageBrush;
if (imageBrush != null)
{
return new ImageBrush
{
AlignmentX = imageBrush.AlignmentX,
AlignmentY = imageBrush.AlignmentY,
ImageSource = imageBrush.ImageSource,
Opacity = imageBrush.Opacity,
RelativeTransform = imageBrush.RelativeTransform,
Stretch = imageBrush.Stretch,
Transform = imageBrush.Transform
};
}
return null;
}
}
}

View File

@@ -0,0 +1,94 @@
//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 Windows.UI.Xaml;
using LiveCharts.Dtos;
using LiveCharts.Uwp.Charts.Base;
namespace LiveCharts.Uwp.Components
{
internal class ChartUpdater : LiveCharts.ChartUpdater
{
public ChartUpdater(TimeSpan frequency)
{
Timer = new DispatcherTimer {Interval = frequency};
Timer.Tick += OnTimerOnTick;
Freq = frequency;
}
public DispatcherTimer Timer { get; set; }
private bool RequiresRestart { get; set; }
private TimeSpan Freq { get; set; }
public override void Run(bool restartView = false, bool updateNow = false)
{
if (Timer == null)
{
Timer = new DispatcherTimer { Interval = Freq };
Timer.Tick += OnTimerOnTick;
IsUpdating = false;
}
if (updateNow)
{
UpdaterTick(restartView, true);
return;
}
RequiresRestart = restartView || RequiresRestart;
if (IsUpdating) return;
IsUpdating = true;
Timer.Start();
}
public override void UpdateFrequency(TimeSpan freq)
{
Timer.Interval = freq;
}
public void OnTimerOnTick(object sender, object args)
{
UpdaterTick(RequiresRestart, false);
}
private void UpdaterTick(bool restartView, bool force)
{
var uwpfChart = (Chart) Chart.View;
if (uwpfChart.Visibility != Visibility.Visible && !uwpfChart.IsMocked) return;
Chart.ControlSize = uwpfChart.IsMocked
? uwpfChart.Model.ControlSize
: new CoreSize(uwpfChart.ActualWidth, uwpfChart.ActualHeight);
Timer.Stop();
Update(restartView, force);
IsUpdating = false;
RequiresRestart = false;
uwpfChart.ChartUpdated();
uwpfChart.PrepareScrolBar();
}
}
}

View File

@@ -0,0 +1,66 @@
//The MIT License(MIT)
//copyright(c) 2016 Greg Dennis & 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 System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml.Data;
namespace LiveCharts.Uwp.Components
{
internal class SerieConverter : IValueConverter
{
public static SerieConverter Instance { get; set; }
static SerieConverter()
{
Instance = new SerieConverter();
}
private SerieConverter() { }
public object Convert(object value, Type targetType, object parameter, string language)
{
var series = value as IEnumerable<Series>;
if (series != null)
return series.Select(x => new SeriesViewModel());
var serie = value as Series;
if (serie != null)
return new SeriesViewModel
{
Title = serie.Title,
Stroke = serie.Stroke,
Fill = serie.Fill,
PointGeometry = serie.PointGeometry == DefaultGeometries.None
? new PointGeometry("M 0, 0.5 h 1, 0.5 Z").Parse()
: serie.PointGeometry.Parse()
};
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,48 @@
//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.IO;
using System.Xml;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Markup;
namespace LiveCharts.Uwp.Components
{
/// <summary>
///
/// </summary>
public static class DefaultXamlReader
{
/// <summary>
/// Creates the specified type.
/// </summary>
/// <returns></returns>
public static DataTemplate DataLabelTemplate()
{
const string markup =
@"<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
<TextBlock Text=""{Binding FormattedText}""></TextBlock>
</DataTemplate>";
return XamlReader.Load(markup) as DataTemplate;
}
}
}

View File

@@ -0,0 +1,22 @@
using Windows.UI.Xaml;
namespace LiveCharts.Uwp.Components
{
/// <summary>
///
/// </summary>
public static class DependencyObjectExtensions
{
/// <summary>
/// Sets if not set.
/// </summary>
/// <param name="o">The o.</param>
/// <param name="dp">The dp.</param>
/// <param name="value">The value.</param>
public static void SetIfNotSet(this DependencyObject o, DependencyProperty dp, object value)
{
if (o.ReadLocalValue(dp) == DependencyProperty.UnsetValue)
o.SetValue(dp, value);
}
}
}

View File

@@ -0,0 +1,43 @@
using System;
using Windows.UI.Xaml.Media;
namespace LiveCharts.Uwp.Components
{
internal static class GeometryHelper
{
public static Geometry Parse(string data)
{
var parser = new GeometryParser();
return parser.Convert(data);
}
public static Geometry Parse(this PointGeometry geometry)
{
return geometry == null ? null : Parse(geometry.Data);
}
}
/// <summary>
///
/// </summary>
/// <seealso cref="System.Attribute" />
public class GeometryData : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="GeometryData"/> class.
/// </summary>
/// <param name="data">The data.</param>
public GeometryData(string data)
{
Data = data;
}
/// <summary>
/// Gets or sets the data.
/// </summary>
/// <value>
/// The data.
/// </value>
public string Data { get; set; }
}
}

View File

@@ -0,0 +1,679 @@
using System;
using System.Globalization;
using Windows.Foundation;
using Windows.UI.Xaml.Media;
namespace LiveCharts.Uwp.Components
{
/// <summary>
/// A String-To-PathGeometry Parser from https://stringtopathgeometry.codeplex.com/
/// </summary>
internal class GeometryParser
{
#region Const & Private Variables
const bool AllowSign = true;
const bool AllowComma = true;
const bool IsFilled = true;
const bool IsClosed = true;
IFormatProvider _formatProvider;
PathFigure _figure; // Figure object, which will accept parsed segments
string _pathString; // Input string to be parsed
int _pathLength;
int _curIndex; // Location to read next character from
bool _figureStarted; // StartFigure is effective
Point _lastStart; // Last figure starting point
Point _lastPoint; // Last point
Point _secondLastPoint; // The point before last point
char _token; // Non whitespace character returned by ReadToken
#endregion
#region Public Functionality
/// <summary>
/// Main conversion routine - converts string path data definition to PathGeometry object
/// </summary>
/// <param name="path">String with path data definition</param>
/// <returns>PathGeometry object created from string definition</returns>
public PathGeometry Convert(string path)
{
if (path == null)
throw new ArgumentException("Path string cannot be null!", nameof(path));
if (path.Length == 0)
throw new ArgumentException("Path string cannot be empty!", nameof(path));
return Parse(path);
}
/// <summary>
/// Main back conversion routine - converts PathGeometry object to its string equivalent
/// </summary>
/// <param name="geometry">Path Geometry object</param>
/// <returns>String equivalent to PathGeometry contents</returns>
public string ConvertBack(PathGeometry geometry)
{
if (geometry == null)
throw new ArgumentException("Path Geometry cannot be null!", nameof(geometry));
return ParseBack(geometry);
}
#endregion
#region Private Functionality
/// <summary>
/// Main parser routine, which loops over each char in received string, and performs actions according to command/parameter being passed
/// </summary>
/// <param name="path">String with path data definition</param>
/// <returns>PathGeometry object created from string definition</returns>
private PathGeometry Parse(string path)
{
PathGeometry pathGeometry = null;
_formatProvider = CultureInfo.InvariantCulture;
_pathString = path;
_pathLength = path.Length;
_curIndex = 0;
_secondLastPoint = new Point(0, 0);
_lastPoint = new Point(0, 0);
_lastStart = new Point(0, 0);
_figureStarted = false;
var first = true;
var lastCmd = ' ';
while (ReadToken()) // Empty path is allowed in XAML
{
char cmd = _token;
if (first)
{
if ((cmd != 'M') && (cmd != 'm') && (cmd != 'f') && (cmd != 'F')) // Path starts with M|m
{
ThrowBadToken();
}
first = false;
}
switch (cmd)
{
case 'f':
case 'F':
pathGeometry = new PathGeometry();
var num = ReadNumber(!AllowComma);
// ReSharper disable once CompareOfFloatsByEqualityOperator
pathGeometry.FillRule = num == 0 ? FillRule.EvenOdd : FillRule.Nonzero;
break;
case 'm':
case 'M':
// XAML allows multiple points after M/m
_lastPoint = ReadPoint(cmd, !AllowComma);
_figure = new PathFigure
{
StartPoint = _lastPoint,
IsFilled = IsFilled,
IsClosed = !IsClosed
};
//context.BeginFigure(_lastPoint, IsFilled, !IsClosed);
_figureStarted = true;
_lastStart = _lastPoint;
lastCmd = 'M';
while (IsNumber(AllowComma))
{
_lastPoint = ReadPoint(cmd, !AllowComma);
var lineSegment = new LineSegment {Point = _lastPoint};
_figure.Segments.Add(lineSegment);
//context.LineTo(_lastPoint, IsStroked, !IsSmoothJoin);
lastCmd = 'L';
}
break;
case 'l':
case 'L':
case 'h':
case 'H':
case 'v':
case 'V':
EnsureFigure();
do
{
switch (cmd)
{
case 'l': _lastPoint = ReadPoint(cmd, !AllowComma); break;
case 'L': _lastPoint = ReadPoint(cmd, !AllowComma); break;
case 'h': _lastPoint.X += ReadNumber(!AllowComma); break;
case 'H': _lastPoint.X = ReadNumber(!AllowComma); break;
case 'v': _lastPoint.Y += ReadNumber(!AllowComma); break;
case 'V': _lastPoint.Y = ReadNumber(!AllowComma); break;
}
var lineSegment = new LineSegment {Point = _lastPoint};
_figure.Segments.Add(lineSegment);
//context.LineTo(_lastPoint, IsStroked, !IsSmoothJoin);
}
while (IsNumber(AllowComma));
lastCmd = 'L';
break;
case 'c':
case 'C': // cubic Bezier
case 's':
case 'S': // smooth cublic Bezier
EnsureFigure();
do
{
Point p;
if ((cmd == 's') || (cmd == 'S'))
{
p = lastCmd == 'C' ? Reflect() : _lastPoint;
_secondLastPoint = ReadPoint(cmd, !AllowComma);
}
else
{
p = ReadPoint(cmd, !AllowComma);
_secondLastPoint = ReadPoint(cmd, AllowComma);
}
_lastPoint = ReadPoint(cmd, AllowComma);
var bizierSegment = new BezierSegment
{
Point1 = p,
Point2 = _secondLastPoint,
Point3 = _lastPoint
};
_figure.Segments.Add(bizierSegment);
//context.BezierTo(p, _secondLastPoint, _lastPoint, IsStroked, !IsSmoothJoin);
lastCmd = 'C';
}
while (IsNumber(AllowComma));
break;
case 'q':
case 'Q': // quadratic Bezier
case 't':
case 'T': // smooth quadratic Bezier
EnsureFigure();
do
{
if ((cmd == 't') || (cmd == 'T'))
{
_secondLastPoint = lastCmd == 'Q' ? Reflect() : _lastPoint;
_lastPoint = ReadPoint(cmd, !AllowComma);
}
else
{
_secondLastPoint = ReadPoint(cmd, !AllowComma);
_lastPoint = ReadPoint(cmd, AllowComma);
}
var quadraticBezierSegment = new QuadraticBezierSegment
{
Point1 = _secondLastPoint,
Point2 = _lastPoint
};
_figure.Segments.Add(quadraticBezierSegment);
//context.QuadraticBezierTo(_secondLastPoint, _lastPoint, IsStroked, !IsSmoothJoin);
lastCmd = 'Q';
}
while (IsNumber(AllowComma));
break;
case 'a':
case 'A':
EnsureFigure();
do
{
// A 3,4 5, 0, 0, 6,7
var w = ReadNumber(!AllowComma);
var h = ReadNumber(AllowComma);
var rotation = ReadNumber(AllowComma);
var large = ReadBool();
var sweep = ReadBool();
_lastPoint = ReadPoint(cmd, AllowComma);
var arcSegment = new ArcSegment
{
Point = _lastPoint,
Size = new Size(w, h),
RotationAngle = rotation,
IsLargeArc = large,
SweepDirection = sweep ? SweepDirection.Clockwise : SweepDirection.Counterclockwise
};
_figure.Segments.Add(arcSegment);
//context.ArcTo(
// _lastPoint,
// new Size(w, h),
// rotation,
// large,
// sweep ? SweepDirection.Clockwise : SweepDirection.Counterclockwise,
// IsStroked,
// !IsSmoothJoin
// );
}
while (IsNumber(AllowComma));
lastCmd = 'A';
break;
case 'z':
case 'Z':
EnsureFigure();
_figure.IsClosed = IsClosed;
//context.SetClosedState(IsClosed);
_figureStarted = false;
lastCmd = 'Z';
_lastPoint = _lastStart; // Set reference point to be first point of current figure
break;
default:
ThrowBadToken();
break;
}
if (null != _figure)
{
if (_figure.IsClosed)
{
if (null == pathGeometry)
pathGeometry = new PathGeometry();
pathGeometry.Figures.Add(_figure);
_figure = null;
first = true;
}
}
}
if (null != _figure)
{
if (null == pathGeometry)
pathGeometry = new PathGeometry();
if (!pathGeometry.Figures.Contains(_figure))
pathGeometry.Figures.Add(_figure);
}
return pathGeometry;
}
void SkipDigits(bool signAllowed)
{
// Allow for a sign
if (signAllowed && More() && ((_pathString[_curIndex] == '-') || _pathString[_curIndex] == '+'))
{
_curIndex++;
}
while (More() && (_pathString[_curIndex] >= '0') && (_pathString[_curIndex] <= '9'))
{
_curIndex++;
}
}
bool ReadBool()
{
SkipWhiteSpace(AllowComma);
if (More())
{
_token = _pathString[_curIndex++];
if (_token == '0')
{
return false;
}
else if (_token == '1')
{
return true;
}
}
ThrowBadToken();
return false;
}
private Point Reflect()
{
return new Point(2 * _lastPoint.X - _secondLastPoint.X,
2 * _lastPoint.Y - _secondLastPoint.Y);
}
private void EnsureFigure()
{
if (!_figureStarted)
{
_figure = new PathFigure {StartPoint = _lastStart};
//_context.BeginFigure(_lastStart, IsFilled, !IsClosed);
_figureStarted = true;
}
}
double ReadNumber(bool allowComma)
{
if (!IsNumber(allowComma))
{
ThrowBadToken();
}
bool simple = true;
int start = _curIndex;
//
// Allow for a sign
//
// There are numbers that cannot be preceded with a sign, for instance, -NaN, but it's
// fine to ignore that at this point, since the CLR parser will catch this later.
//
if (More() && ((_pathString[_curIndex] == '-') || _pathString[_curIndex] == '+'))
{
_curIndex++;
}
// Check for Infinity (or -Infinity).
if (More() && (_pathString[_curIndex] == 'I'))
{
//
// Don't bother reading the characters, as the CLR parser will
// do this for us later.
//
_curIndex = Math.Min(_curIndex + 8, _pathLength); // "Infinity" has 8 characters
simple = false;
}
// Check for NaN
else if (More() && (_pathString[_curIndex] == 'N'))
{
//
// Don't bother reading the characters, as the CLR parser will
// do this for us later.
//
_curIndex = Math.Min(_curIndex + 3, _pathLength); // "NaN" has 3 characters
simple = false;
}
else
{
SkipDigits(!AllowSign);
// Optional period, followed by more digits
if (More() && (_pathString[_curIndex] == '.'))
{
simple = false;
_curIndex++;
SkipDigits(!AllowSign);
}
// Exponent
if (More() && ((_pathString[_curIndex] == 'E') || (_pathString[_curIndex] == 'e')))
{
simple = false;
_curIndex++;
SkipDigits(AllowSign);
}
}
if (simple && (_curIndex <= (start + 8))) // 32-bit integer
{
int sign = 1;
if (_pathString[start] == '+')
{
start++;
}
else if (_pathString[start] == '-')
{
start++;
sign = -1;
}
int value = 0;
while (start < _curIndex)
{
value = value * 10 + (_pathString[start] - '0');
start++;
}
return value * sign;
}
else
{
string subString = _pathString.Substring(start, _curIndex - start);
try
{
return System.Convert.ToDouble(subString, _formatProvider);
}
catch (FormatException except)
{
throw new FormatException(string.Format("Unexpected character in path '{0}' at position {1}", _pathString, _curIndex - 1), except);
}
}
}
private bool IsNumber(bool allowComma)
{
bool commaMet = SkipWhiteSpace(allowComma);
if (More())
{
_token = _pathString[_curIndex];
// Valid start of a number
if ((_token == '.') || (_token == '-') || (_token == '+') || ((_token >= '0') && (_token <= '9'))
|| (_token == 'I') // Infinity
|| (_token == 'N')) // NaN
{
return true;
}
}
if (commaMet) // Only allowed between numbers
{
ThrowBadToken();
}
return false;
}
private Point ReadPoint(char cmd, bool allowcomma)
{
double x = ReadNumber(allowcomma);
double y = ReadNumber(AllowComma);
if (cmd >= 'a') // 'A' < 'a'. lower case for relative
{
x += _lastPoint.X;
y += _lastPoint.Y;
}
return new Point(x, y);
}
private bool ReadToken()
{
SkipWhiteSpace(!AllowComma);
// Check for end of string
if (More())
{
_token = _pathString[_curIndex++];
return true;
}
else
{
return false;
}
}
bool More()
{
return _curIndex < _pathLength;
}
// Skip white space, one comma if allowed
private bool SkipWhiteSpace(bool allowComma)
{
bool commaMet = false;
while (More())
{
char ch = _pathString[_curIndex];
switch (ch)
{
case ' ':
case '\n':
case '\r':
case '\t': // SVG whitespace
break;
case ',':
if (allowComma)
{
commaMet = true;
allowComma = false; // one comma only
}
else
{
ThrowBadToken();
}
break;
default:
// Avoid calling IsWhiteSpace for ch in (' ' .. 'z']
if (((ch > ' ') && (ch <= 'z')) || !Char.IsWhiteSpace(ch))
{
return commaMet;
}
break;
}
_curIndex++;
}
return commaMet;
}
private void ThrowBadToken()
{
throw new FormatException(string.Format("Unexpected character in path '{0}' at position {1}", _pathString, _curIndex - 1));
}
internal static char GetNumericListSeparator(IFormatProvider provider)
{
var numericSeparator = ',';
// Get the NumberFormatInfo out of the provider, if possible
// If the IFormatProvider doesn't not contain a NumberFormatInfo, then
// this method returns the current culture's NumberFormatInfo.
var numberFormat = NumberFormatInfo.GetInstance(provider);
// Is the decimal separator is the same as the list separator?
// If so, we use the ";".
if ((numberFormat.NumberDecimalSeparator.Length > 0) && (numericSeparator == numberFormat.NumberDecimalSeparator[0]))
{
numericSeparator = ';';
}
return numericSeparator;
}
private string ParseBack(PathGeometry geometry)
{
var sb = new System.Text.StringBuilder();
IFormatProvider provider = new CultureInfo("en-us");
sb.Append("F" + (geometry.FillRule == FillRule.EvenOdd ? "0" : "1") + " ");
foreach (PathFigure figure in geometry.Figures)
{
sb.Append("M " + ((IFormattable)figure.StartPoint).ToString(null, provider) + " ");
foreach (PathSegment segment in figure.Segments)
{
char separator = GetNumericListSeparator(provider);
if (segment is LineSegment)
{
LineSegment lineSegment = segment as LineSegment;
sb.Append("L " + ((IFormattable)lineSegment.Point).ToString(null, provider) + " ");
}
else if (segment is BezierSegment)
{
BezierSegment bezierSegment = segment as BezierSegment;
sb.Append(string.Format(provider,
"C{1:" + null + "}{0}{2:" + null + "}{0}{3:" + null + "} ",
separator,
bezierSegment.Point1,
bezierSegment.Point2,
bezierSegment.Point3
));
}
else if (segment is QuadraticBezierSegment)
{
QuadraticBezierSegment quadraticBezierSegment = segment as QuadraticBezierSegment;
sb.Append(String.Format(provider,
"Q{1:" + null + "}{0}{2:" + null + "} ",
separator,
quadraticBezierSegment.Point1,
quadraticBezierSegment.Point2));
}
else if (segment is ArcSegment)
{
ArcSegment arcSegment = segment as ArcSegment;
sb.Append(String.Format(provider,
"A{1:" + null + "}{0}{2:" + null + "}{0}{3}{0}{4}{0}{5:" + null + "} ",
separator,
arcSegment.Size,
arcSegment.RotationAngle,
arcSegment.IsLargeArc ? "1" : "0",
arcSegment.SweepDirection == SweepDirection.Clockwise ? "1" : "0",
arcSegment.Point));
}
}
if (figure.IsClosed)
sb.Append("Z");
}
return sb.ToString();
}
#endregion
}
}

View File

@@ -0,0 +1,40 @@
//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 Windows.UI.Xaml.Media;
namespace LiveCharts.Uwp.Components
{
/// <summary>
///
/// </summary>
public interface IFondeable
{
/// <summary>
/// Gets the point foreround.
/// </summary>
/// <value>
/// The point foreround.
/// </value>
Brush PointForeround { get; }
}
}

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
namespace LiveCharts.Uwp.Components.MultiBinding
{
/// <summary>
/// Represents a collection of <see cref="DependencyObject"/> instances of a specified type.
/// </summary>
/// <typeparam name="T">The type of items in the collection.</typeparam>
public class DependencyObjectCollection<T> : DependencyObjectCollection, INotifyCollectionChanged
where T : DependencyObject
{
/// <summary>
/// Occurs when items in the collection are added, removed, or replaced.
/// </summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
private readonly List<T> _oldItems = new List<T>();
/// <summary>
/// Initializes a new instance of the <see cref="DependencyObjectCollection{T}" /> class.
/// </summary>
public DependencyObjectCollection()
{
VectorChanged += DependencyObjectCollectionVectorChanged;
}
private void DependencyObjectCollectionVectorChanged(IObservableVector<DependencyObject> sender, IVectorChangedEventArgs e)
{
var index = (int)e.Index;
switch (e.CollectionChange)
{
case CollectionChange.Reset:
foreach (var item in this)
{
VerifyType(item);
}
RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
_oldItems.Clear();
break;
case CollectionChange.ItemInserted:
VerifyType(this[index]);
RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, this[index], index));
_oldItems.Insert(index, (T)this[index]);
break;
case CollectionChange.ItemRemoved:
RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, _oldItems[index], index));
_oldItems.RemoveAt(index);
break;
case CollectionChange.ItemChanged:
VerifyType(this[index]);
RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, this[index], _oldItems[index]));
_oldItems[index] = (T)this[index];
break;
default:
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// Raises the <see cref="CollectionChanged"/> event with the provided event data.
/// </summary>
/// <param name="eventArgs">The event data.</param>
protected void RaiseCollectionChanged(NotifyCollectionChangedEventArgs eventArgs)
{
var eventHandler = CollectionChanged;
if (eventHandler != null)
{
eventHandler(this, eventArgs);
}
}
private void VerifyType(DependencyObject item)
{
if (!(item is T))
{
throw new InvalidOperationException("Invalid item type added to collection");
}
}
}
}

View File

@@ -0,0 +1,174 @@
using System;
using System.Reflection;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Markup;
using Microsoft.Xaml.Interactivity;
namespace LiveCharts.Uwp.Components.MultiBinding
{
/// <summary>
/// The behavior that enables multiple binding.
/// </summary>
[ContentProperty(Name = "Items")]
[TypeConstraint(typeof(FrameworkElement))]
public class MultiBindingBehavior : Behavior<FrameworkElement>
{
/// <summary>
/// Gets the <see cref="MultiBindingItem"/> collection within this <see cref="MultiBindingBehavior"/> instance.
/// </summary>
/// <value>One or more <see cref="MultiBindingItem"/> objects.</value>
public MultiBindingItemCollection Items
{
get { return (MultiBindingItemCollection)GetValue(ItemsProperty); }
private set { SetValue(ItemsProperty, value); }
}
/// <summary>
/// Identifier for the <see cref="Items" /> dependency property.
/// </summary>
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(nameof(Items), typeof(MultiBindingItemCollection), typeof(MultiBindingBehavior), null);
/// <summary>
/// Gets or sets the path to the binding source property.
/// </summary>
/// <value>The path to the binding source property.</value>
public string PropertyName
{
get { return (string)GetValue(PropertyNameProperty); }
set { SetValue(PropertyNameProperty, value); }
}
/// <summary>
/// Identifier for the <see cref="PropertyName" /> dependency property.
/// </summary>
public static readonly DependencyProperty PropertyNameProperty =
DependencyProperty.Register(nameof(PropertyName), typeof(string), typeof(MultiBindingBehavior), new PropertyMetadata(null, OnPropertyChanged));
/// <summary>
/// Gets or sets the converter to use to convert the source values to or from the target value.
/// </summary>
/// <value>A resource reference to a class that implements the <see cref="IValueConverter"/> interface, which includes implementations of the <see cref="IValueConverter.Convert"/> and <see cref="IValueConverter.ConvertBack"/> methods.</value>
public IValueConverter Converter
{
get { return (IValueConverter)GetValue(ConverterProperty); }
set { SetValue(ConverterProperty, value); }
}
/// <summary>
/// Identifier for the <see cref="Converter" /> dependency property.
/// </summary>
public static readonly DependencyProperty ConverterProperty =
DependencyProperty.Register(nameof(Converter), typeof(IValueConverter), typeof(MultiBindingBehavior), new PropertyMetadata(null, OnPropertyChanged));
/// <summary>
/// Gets or sets an optional parameter to pass to the converter as additional information.
/// </summary>
/// <value>A value of the type expected by the converter, which might be an object element or a string depending on the definition and XAML capabilities both of the property type being used and of the implementation of the converter.</value>
public object ConverterParameter
{
get { return GetValue(ConverterParameterProperty); }
set { SetValue(ConverterParameterProperty, value); }
}
/// <summary>
/// Identifier for the <see cref="ConverterParameter" /> dependency property.
/// </summary>
public static readonly DependencyProperty ConverterParameterProperty =
DependencyProperty.Register(nameof(ConverterParameter), typeof(object), typeof(MultiBindingBehavior), new PropertyMetadata(null, OnPropertyChanged));
/// <summary>
/// Gets or sets a value that indicates the direction of the data flow in the binding.
/// </summary>
/// <value>A value that indicates the direction of the data flow in the binding.</value>
public BindingMode Mode
{
get { return (BindingMode)GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
/// <summary>
/// Identifier for the <see cref="Mode" /> dependency property.
/// </summary>
public static readonly DependencyProperty ModeProperty =
DependencyProperty.Register(nameof(Mode), typeof(BindingMode), typeof(MultiBindingBehavior), new PropertyMetadata(BindingMode.OneWay, OnPropertyChanged));
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var multiBindingBehavior = (MultiBindingBehavior)d;
multiBindingBehavior.Update();
}
/// <summary>
/// Initializes a new instance of the <see cref="MultiBindingBehavior"/> class.
/// </summary>
public MultiBindingBehavior()
{
Items = new MultiBindingItemCollection();
}
/// <summary>
/// Called after the behavior is attached to an AssociatedObject.
/// </summary>
/// <remarks>Override this to hook up functionality to the AssociatedObject.</remarks>
protected override void OnAttached()
{
base.OnAttached();
Update();
}
private void Update()
{
if (AssociatedObject == null || string.IsNullOrEmpty(PropertyName))
{
return;
}
var targetProperty = PropertyName;
Type targetType;
if (targetProperty.Contains("."))
{
var propertyNameParts = targetProperty.Split('.');
targetType = Type.GetType(string.Format("Windows.UI.Xaml.Controls.{0}, Windows",
propertyNameParts[0]));
targetProperty = propertyNameParts[1];
}
else
{
targetType = AssociatedObject.GetType();
}
PropertyInfo targetDependencyPropertyField = null;
while (targetDependencyPropertyField == null && targetType != null)
{
var targetTypeInfo = targetType.GetTypeInfo();
targetDependencyPropertyField = targetTypeInfo.GetDeclaredProperty(targetProperty + "Property");
targetType = targetTypeInfo.BaseType;
}
if (targetDependencyPropertyField == null) return;
var targetDependencyProperty = (DependencyProperty)targetDependencyPropertyField.GetValue(null);
var binding = new Binding()
{
Path = new PropertyPath("Value"),
Source = Items,
Converter = Converter,
ConverterParameter = ConverterParameter,
Mode = Mode
};
BindingOperations.SetBinding(AssociatedObject, targetDependencyProperty, binding);
}
}
}

View File

@@ -0,0 +1,42 @@
using Windows.UI.Xaml;
namespace LiveCharts.Uwp.Components.MultiBinding
{
/// <summary>
/// A multiple binding item.
/// </summary>
public class MultiBindingItem : DependencyObject
{
/// <summary>
/// Gets or sets the binding value.
/// </summary>
/// <value>The binding value.</value>
public object Value
{
get { return GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
/// <summary>
/// Identifier for the <see cref="Value" /> dependency property.
/// </summary>
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(nameof(Value), typeof(object), typeof(MultiBindingItem), new PropertyMetadata(null, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var multiBindingItem = (MultiBindingItem)d;
multiBindingItem.Update();
}
internal MultiBindingItemCollection Parent { get; set; }
private void Update()
{
var parent = Parent;
parent?.Update();
}
}
}

View File

@@ -0,0 +1,115 @@
using System.Collections.Specialized;
using System.Linq;
using Windows.UI.Xaml;
namespace LiveCharts.Uwp.Components.MultiBinding
{
/// <summary>
/// Represents a collection of <see cref="MultiBindingBehavior" />.
/// </summary>
public class MultiBindingItemCollection : DependencyObjectCollection<MultiBindingItem>
{
private bool _updating;
/// <summary>
/// Gets or sets the multiple binding value.
/// </summary>
/// <value>The multiple binding value.</value>
public object[] Value
{
get { return (object[])GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
/// <summary>
/// Identifier for the <see cref="Value" /> dependency property.
/// </summary>
internal static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(nameof(Value), typeof(object[]), typeof(MultiBindingItemCollection), new PropertyMetadata(null, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var multiBindingItemCollection = (MultiBindingItemCollection)d;
multiBindingItemCollection.UpdateSource();
}
/// <summary>
/// Initializes a new instance of the <see cref="MultiBindingItemCollection"/> class.
/// </summary>
public MultiBindingItemCollection()
{
CollectionChanged += OnCollectionChanged;
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
{
foreach (MultiBindingItem item in e.OldItems)
{
item.Parent = null;
}
}
if (e.NewItems != null)
{
foreach (MultiBindingItem item in e.NewItems)
{
item.Parent = this;
}
}
Update();
}
internal void Update()
{
if (_updating)
{
return;
}
try
{
_updating = true;
Value = this
.OfType<MultiBindingItem>()
.Select(x => x.Value)
.ToArray();
}
finally
{
_updating = false;
}
}
private void UpdateSource()
{
if (_updating)
{
return;
}
try
{
_updating = true;
for (var index = 0; index < this.Count; index++)
{
var multiBindingItem = this[index] as MultiBindingItem;
if (multiBindingItem != null)
{
multiBindingItem.Value = Value[index];
}
}
}
finally
{
_updating = false;
}
}
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Globalization;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
namespace LiveCharts.Uwp.Components.MultiBinding
{
/// <summary>
/// An <see cref="IValueConverter"/> abstract implementation to be used with the <see cref="MultiBindingBehavior"/>.
/// </summary>
public abstract class MultiValueConverterBase : DependencyObject, IValueConverter
{
/// <summary>
/// Modifies the source data before passing it to the target for display in the UI.
/// </summary>
/// <returns>The value to be passed to the target dependency property.</returns>
/// <param name="values">The source data being passed to the target.</param>
/// <param name="targetType">The <see cref="T:System.Type"/> of data expected by the target dependency property.</param>
/// <param name="parameter">An optional parameter to be used in the converter logic.</param>
/// <param name="culture">The culture of the conversion.</param>
public abstract object Convert(object[] values, Type targetType, object parameter, CultureInfo culture);
/// <summary>
/// Modifies the target data before passing it to the source object. This method is called only in <see cref="F:System.Windows.Data.BindingMode.TwoWay"/> bindings.
/// </summary>
/// <returns>The value to be passed to the source object.</returns>
/// <param name="value">The target data being passed to the source.</param>
/// <param name="targetType">The <see cref="T:System.Type"/> of data expected by the source object.</param>
/// <param name="parameter">An optional parameter to be used in the converter logic.</param>
/// <param name="culture">The culture of the conversion.</param>
public abstract object[] ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
object IValueConverter.Convert(object value, Type targetType, object parameter, string language)
{
var cultureInfo = !string.IsNullOrEmpty(language) ? new CultureInfo(language) : null;
return Convert((object[])value, targetType, parameter, cultureInfo);
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, string language)
{
var cultureInfo = !string.IsNullOrEmpty(language) ? new CultureInfo(language) : null;
return ConvertBack(value, targetType, parameter, cultureInfo);
}
}
}

View File

@@ -0,0 +1,76 @@
//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 Windows.UI.Xaml.Media;
namespace LiveCharts.Uwp.Components
{
/// <summary>
/// A tooltip element data transfer object
/// </summary>
public class TooltipDto
{
/// <summary>
/// Gets or sets the series.
/// </summary>
/// <value>
/// The series.
/// </value>
public Series Series { get; set; }
/// <summary>
/// Gets or sets the index.
/// </summary>
/// <value>
/// The index.
/// </value>
public int Index { get; set; }
/// <summary>
/// Gets or sets the stroke.
/// </summary>
/// <value>
/// The stroke.
/// </value>
public Brush Stroke { get; set; }
/// <summary>
/// Gets or sets the fill.
/// </summary>
/// <value>
/// The fill.
/// </value>
public Brush Fill { get; set; }
/// <summary>
/// Gets or sets the point.
/// </summary>
/// <value>
/// The point.
/// </value>
public ChartPoint Point { get; set; }
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>
/// The value.
/// </value>
public string Value { get; set; }
}
}