diff --git a/samples/AvaloniaSample/AvaloniaSample.csproj b/samples/AvaloniaSample/AvaloniaSample.csproj
index 29543f8a1..7e2afc6bc 100644
--- a/samples/AvaloniaSample/AvaloniaSample.csproj
+++ b/samples/AvaloniaSample/AvaloniaSample.csproj
@@ -118,6 +118,9 @@
%(Filename)
+
+ %(Filename)
+
%(Filename)
diff --git a/samples/AvaloniaSample/General/VisualElements/View.axaml b/samples/AvaloniaSample/General/VisualElements/View.axaml
new file mode 100644
index 000000000..a02b83b96
--- /dev/null
+++ b/samples/AvaloniaSample/General/VisualElements/View.axaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/samples/AvaloniaSample/General/VisualElements/View.axaml.cs b/samples/AvaloniaSample/General/VisualElements/View.axaml.cs
new file mode 100644
index 000000000..6d3b64875
--- /dev/null
+++ b/samples/AvaloniaSample/General/VisualElements/View.axaml.cs
@@ -0,0 +1,17 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace AvaloniaSample.General.VisualElements;
+
+public class View : UserControl
+{
+ public View()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
diff --git a/samples/BlazorSample/Pages/Design/LinearGradients.razor b/samples/BlazorSample/Pages/Design/LinearGradients.razor
index 113e31ef1..a6e9756c9 100644
--- a/samples/BlazorSample/Pages/Design/LinearGradients.razor
+++ b/samples/BlazorSample/Pages/Design/LinearGradients.razor
@@ -3,7 +3,8 @@
@using ViewModelsSamples.Design.LinearGradients
+ Series="ViewModel.Series"
+ LegendPosition="LiveChartsCore.Measure.LegendPosition.Right">
@code {
diff --git a/samples/BlazorSample/Pages/Design/RadialGradients.razor b/samples/BlazorSample/Pages/Design/RadialGradients.razor
index 905ed32b0..a09d79de1 100644
--- a/samples/BlazorSample/Pages/Design/RadialGradients.razor
+++ b/samples/BlazorSample/Pages/Design/RadialGradients.razor
@@ -3,7 +3,8 @@
@using ViewModelsSamples.Design.RadialGradients
+ Series="ViewModel.Series"
+ LegendPosition="LiveChartsCore.Measure.LegendPosition.Right">
@code {
diff --git a/samples/BlazorSample/Pages/General/VisualElements.razor b/samples/BlazorSample/Pages/General/VisualElements.razor
new file mode 100644
index 000000000..ac516582b
--- /dev/null
+++ b/samples/BlazorSample/Pages/General/VisualElements.razor
@@ -0,0 +1,12 @@
+@page "/General/VisualElements"
+@using LiveChartsCore.SkiaSharpView.Blazor
+@using ViewModelsSamples.General.VisualElements
+
+
+
+
+@code {
+ public ViewModel ViewModel { get; set; } = new();
+}
diff --git a/samples/ConsoleSample/ConsoleSample/Program.cs b/samples/ConsoleSample/ConsoleSample/Program.cs
index 20de92c33..e907a77df 100644
--- a/samples/ConsoleSample/ConsoleSample/Program.cs
+++ b/samples/ConsoleSample/ConsoleSample/Program.cs
@@ -12,7 +12,8 @@
{
new LineSeries { Values = new int[] { 1, 5, 4, 6 } },
new ColumnSeries { Values = new int[] { 4, 8, 2, 4 } }
- }
+ },
+ LegendPosition = LiveChartsCore.Measure.LegendPosition.Right
};
// you can save the image to png (by default)
@@ -28,7 +29,8 @@
new PieSeries { Values = new int[] { 10, } },
new PieSeries { Values = new int[] { 6 } },
new PieSeries { Values = new int[] { 4 } }
- }
+ },
+ LegendPosition = LiveChartsCore.Measure.LegendPosition.Right
};
pieChart.SaveImage("pieChart.png");
diff --git a/samples/EtoFormsSample/General/VisualElements/View.cs b/samples/EtoFormsSample/General/VisualElements/View.cs
new file mode 100644
index 000000000..87bfeb277
--- /dev/null
+++ b/samples/EtoFormsSample/General/VisualElements/View.cs
@@ -0,0 +1,21 @@
+using Eto.Forms;
+using LiveChartsCore.SkiaSharpView.Eto;
+using ViewModelsSamples.General.VisualElements;
+
+namespace EtoFormsSample.General.VisualElements;
+
+public class View : Panel
+{
+ public View()
+ {
+ var viewModel = new ViewModel();
+
+ var cartesianChart = new CartesianChart
+ {
+ Series = viewModel.Series,
+ VisualElements = viewModel.VisualElements,
+ };
+
+ Content = cartesianChart;
+ }
+}
diff --git a/samples/MauiSample/General/VisualElements/View.xaml b/samples/MauiSample/General/VisualElements/View.xaml
new file mode 100644
index 000000000..cf19c1780
--- /dev/null
+++ b/samples/MauiSample/General/VisualElements/View.xaml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MauiSample/General/VisualElements/View.xaml.cs b/samples/MauiSample/General/VisualElements/View.xaml.cs
new file mode 100644
index 000000000..6908f34ff
--- /dev/null
+++ b/samples/MauiSample/General/VisualElements/View.xaml.cs
@@ -0,0 +1,10 @@
+namespace MauiSample.General.VisualElements;
+
+[XamlCompilation(XamlCompilationOptions.Compile)]
+public partial class View : ContentPage
+{
+ public View()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/samples/MauiSample/MauiSample.csproj b/samples/MauiSample/MauiSample.csproj
index f209e36ef..ea5c8c7b7 100644
--- a/samples/MauiSample/MauiSample.csproj
+++ b/samples/MauiSample/MauiSample.csproj
@@ -51,6 +51,9 @@
+
+ %(Filename)
+
%(Filename)
@@ -162,6 +165,9 @@
MSBuild:Compile
+
+ MSBuild:Compile
+
MSBuild:Compile
diff --git a/samples/UWPSample/General/VisualElements/View.xaml b/samples/UWPSample/General/VisualElements/View.xaml
new file mode 100644
index 000000000..026728614
--- /dev/null
+++ b/samples/UWPSample/General/VisualElements/View.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/UWPSample/General/VisualElements/View.xaml.cs b/samples/UWPSample/General/VisualElements/View.xaml.cs
new file mode 100644
index 000000000..49e931815
--- /dev/null
+++ b/samples/UWPSample/General/VisualElements/View.xaml.cs
@@ -0,0 +1,12 @@
+using Windows.UI.Xaml.Controls;
+
+namespace UWPSample.General.VisualElements
+{
+ public sealed partial class View : UserControl
+ {
+ public View()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/samples/UWPSample/UWPSample.csproj b/samples/UWPSample/UWPSample.csproj
index 7af746017..586f9bf62 100644
--- a/samples/UWPSample/UWPSample.csproj
+++ b/samples/UWPSample/UWPSample.csproj
@@ -223,6 +223,9 @@
View.xaml
+
+ View.xaml
+
View.xaml
@@ -546,6 +549,10 @@
MSBuild:Compile
Designer
+
+ MSBuild:Compile
+ Designer
+
MSBuild:Compile
Designer
diff --git a/samples/UnoSample/UnoSample.Shared/LiveChartsSamples/General/Sections2/View.xaml b/samples/UnoSample/UnoSample.Shared/LiveChartsSamples/General/Sections2/View.xaml
index af4f6954b..1d48b10b0 100644
--- a/samples/UnoSample/UnoSample.Shared/LiveChartsSamples/General/Sections2/View.xaml
+++ b/samples/UnoSample/UnoSample.Shared/LiveChartsSamples/General/Sections2/View.xaml
@@ -13,6 +13,6 @@
+ Sections="{Binding Sections}">
diff --git a/samples/UnoSample/UnoSample.Shared/LiveChartsSamples/General/VisualElements/View.xaml b/samples/UnoSample/UnoSample.Shared/LiveChartsSamples/General/VisualElements/View.xaml
new file mode 100644
index 000000000..f19a122d8
--- /dev/null
+++ b/samples/UnoSample/UnoSample.Shared/LiveChartsSamples/General/VisualElements/View.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/UnoSample/UnoSample.Shared/LiveChartsSamples/General/VisualElements/View.xaml.cs b/samples/UnoSample/UnoSample.Shared/LiveChartsSamples/General/VisualElements/View.xaml.cs
new file mode 100644
index 000000000..979d66f63
--- /dev/null
+++ b/samples/UnoSample/UnoSample.Shared/LiveChartsSamples/General/VisualElements/View.xaml.cs
@@ -0,0 +1,11 @@
+using Windows.UI.Xaml.Controls;
+
+namespace UnoSample.General.VisualElements;
+
+public sealed partial class View : UserControl
+{
+ public View()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/samples/UnoSample/UnoSample.Shared/UnoSample.Shared.projitems b/samples/UnoSample/UnoSample.Shared/UnoSample.Shared.projitems
index c0be98bbb..02ebb0541 100644
--- a/samples/UnoSample/UnoSample.Shared/UnoSample.Shared.projitems
+++ b/samples/UnoSample/UnoSample.Shared/UnoSample.Shared.projitems
@@ -123,6 +123,9 @@
View.xaml
+
+ View.xaml
+
View.xaml
@@ -674,7 +677,9 @@
Designer
MSBuild:Compile
-
+
+ Designer
+
<_Globbed_Compile Include="$(MSBuildThisFileDirectory)**/*.xaml.cs" Exclude="@(Compile)">
%(Filename)
@@ -684,4 +689,10 @@
<_Globbed_Content Include="$(MSBuildThisFileDirectory)Assets/**/*.*" Exclude="@(Content)" />
+
+
+ Designer
+ MSBuild:Compile
+
+
\ No newline at end of file
diff --git a/samples/UnoSample/UnoSample.Shared/UnoSample.Shared.shproj b/samples/UnoSample/UnoSample.Shared/UnoSample.Shared.shproj
index 75fff3f88..491347104 100644
--- a/samples/UnoSample/UnoSample.Shared/UnoSample.Shared.shproj
+++ b/samples/UnoSample/UnoSample.Shared/UnoSample.Shared.shproj
@@ -100,6 +100,7 @@
<_Globbed_Compile Remove="LiveChartsSamples\General\MultiThreading2\View.xaml.cs" />
<_Globbed_Compile Remove="LiveChartsSamples\General\MultiThreading\View.xaml.cs" />
<_Globbed_Compile Remove="LiveChartsSamples\General\NullPoints\View.xaml.cs" />
+ <_Globbed_Compile Remove="LiveChartsSamples\General\Sections - Copy\View.xaml.cs" />
<_Globbed_Compile Remove="LiveChartsSamples\General\Sections2\View.xaml.cs" />
<_Globbed_Compile Remove="LiveChartsSamples\General\Sections\View.xaml.cs" />
<_Globbed_Compile Remove="LiveChartsSamples\General\TemplatedLegends\View.xaml.cs" />
diff --git a/samples/UnoWinUISample/UnoWinUISample/UnoWinUISample.Shared/LiveChartsSamples/General/VisualElements/View.xaml b/samples/UnoWinUISample/UnoWinUISample/UnoWinUISample.Shared/LiveChartsSamples/General/VisualElements/View.xaml
new file mode 100644
index 000000000..e97aca2a0
--- /dev/null
+++ b/samples/UnoWinUISample/UnoWinUISample/UnoWinUISample.Shared/LiveChartsSamples/General/VisualElements/View.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/UnoWinUISample/UnoWinUISample/UnoWinUISample.Shared/LiveChartsSamples/General/VisualElements/View.xaml.cs b/samples/UnoWinUISample/UnoWinUISample/UnoWinUISample.Shared/LiveChartsSamples/General/VisualElements/View.xaml.cs
new file mode 100644
index 000000000..70fbc4805
--- /dev/null
+++ b/samples/UnoWinUISample/UnoWinUISample/UnoWinUISample.Shared/LiveChartsSamples/General/VisualElements/View.xaml.cs
@@ -0,0 +1,11 @@
+using Microsoft.UI.Xaml.Controls;
+
+namespace UnoWinUISample.General.VisualElements;
+
+public sealed partial class View : UserControl
+{
+ public View()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/samples/UnoWinUISample/UnoWinUISample/UnoWinUISample.Shared/UnoWinUISample.Shared.projitems b/samples/UnoWinUISample/UnoWinUISample/UnoWinUISample.Shared/UnoWinUISample.Shared.projitems
index e13133f1d..58ea3f4b8 100644
--- a/samples/UnoWinUISample/UnoWinUISample/UnoWinUISample.Shared/UnoWinUISample.Shared.projitems
+++ b/samples/UnoWinUISample/UnoWinUISample/UnoWinUISample.Shared/UnoWinUISample.Shared.projitems
@@ -674,12 +674,12 @@
Designer
MSBuild:Compile
-
+
<_Globbed_Compile Include="$(MSBuildThisFileDirectory)**/*.xaml.cs" Exclude="@(Compile)">
%(Filename)
<_Globbed_Compile Include="$(MSBuildThisFileDirectory)**/*.cs" Exclude="@(Compile);@(_Globbed_Compile)" />
-
+
<_Globbed_PRIResource Include="$(MSBuildThisFileDirectory)**/*.resw" Exclude="@(PRIResource)" />
<_Globbed_Content Include="$(MSBuildThisFileDirectory)Assets/**/*.*" Exclude="@(Content)" />
@@ -690,4 +690,16 @@
+
+
+ View.xaml
+
+
+
+
+ WinUI
+ Designer
+ MSBuild:Compile
+
+
\ No newline at end of file
diff --git a/samples/ViewModelsSamples/Axes/LabelsRotation/ViewModel.cs b/samples/ViewModelsSamples/Axes/LabelsRotation/ViewModel.cs
index 1ec9ad66b..1e9258c18 100644
--- a/samples/ViewModelsSamples/Axes/LabelsRotation/ViewModel.cs
+++ b/samples/ViewModelsSamples/Axes/LabelsRotation/ViewModel.cs
@@ -26,7 +26,7 @@ public partial class ViewModel
{
// Use the Label property to indicate the format of the labels in the axis
// The Labeler takes the value of the label as parameter and must return it as string
- Labeler = (value) => "Day " + value,
+ Labeler = (value) => "so long label with this Day " + value,
// The MinStep property lets you define the minimum separation (in chart values scale)
// between every axis separator, in this case we don't want decimals,
@@ -44,6 +44,7 @@ public partial class ViewModel
{
new Axis
{
+ IsVisible = false,
LabelsRotation = 15,
// Now the Y axis we will display it as currency
diff --git a/samples/ViewModelsSamples/Axes/Multiple/ViewModel.cs b/samples/ViewModelsSamples/Axes/Multiple/ViewModel.cs
index ddc8418d3..63d6299ce 100644
--- a/samples/ViewModelsSamples/Axes/Multiple/ViewModel.cs
+++ b/samples/ViewModelsSamples/Axes/Multiple/ViewModel.cs
@@ -66,16 +66,26 @@ public partial class ViewModel
Name = "Tens",
NameTextSize = 14,
NamePaint = new SolidColorPaint(s_blue),
+ NamePadding = new LiveChartsCore.Drawing.Padding(0, 20),
+ Padding = new LiveChartsCore.Drawing.Padding(0, 0, 20, 0),
TextSize = 12,
LabelsPaint = new SolidColorPaint(s_blue),
+ TicksPaint = new SolidColorPaint(s_blue),
+ SubticksPaint = new SolidColorPaint(s_blue),
+ DrawTicksPath = true
},
new Axis // the "hundreds" series will be scaled on this axis
{
Name = "Hundreds",
NameTextSize = 14,
NamePaint = new SolidColorPaint(s_red),
+ NamePadding = new LiveChartsCore.Drawing.Padding(0, 20),
+ Padding = new LiveChartsCore.Drawing.Padding(20, 0, 0, 0),
TextSize = 12,
LabelsPaint = new SolidColorPaint(s_red),
+ TicksPaint = new SolidColorPaint(s_red),
+ SubticksPaint = new SolidColorPaint(s_red),
+ DrawTicksPath = true,
ShowSeparatorLines = false,
Position = LiveChartsCore.Measure.AxisPosition.End
},
@@ -83,9 +93,14 @@ public partial class ViewModel
{
Name = "Thousands",
NameTextSize = 14,
+ NamePadding = new LiveChartsCore.Drawing.Padding(0, 20),
+ Padding = new LiveChartsCore.Drawing.Padding(20, 0, 0, 0),
NamePaint = new SolidColorPaint(s_yellow),
TextSize = 12,
LabelsPaint = new SolidColorPaint(s_yellow),
+ TicksPaint = new SolidColorPaint(s_yellow),
+ SubticksPaint = new SolidColorPaint(s_yellow),
+ DrawTicksPath = true,
ShowSeparatorLines = false,
Position = LiveChartsCore.Measure.AxisPosition.End
}
diff --git a/samples/ViewModelsSamples/General/VisualElements/MyGeometry.cs b/samples/ViewModelsSamples/General/VisualElements/MyGeometry.cs
new file mode 100644
index 000000000..b7ed66be5
--- /dev/null
+++ b/samples/ViewModelsSamples/General/VisualElements/MyGeometry.cs
@@ -0,0 +1,25 @@
+using SkiaSharp;
+
+namespace ViewModelsSamples.General.VisualElements;
+
+public class MyGeometry : LiveChartsCore.SkiaSharpView.Drawing.Geometries.SVGPathGeometry
+{
+ // https://fonts.google.com/icons?icon.query=sun
+ public static SKPath svgPath = SKPath.ParseSvgPathData(
+ "M24 9.5q-.65 0-1.075-.425Q22.5 8.65 22.5 8V3.5q0-.65.425-1.075Q23.35 2 24 2q.65 0 1.075.425.425.425.425 1.075V8q0 " +
+ ".65-.425 1.075Q24.65 9.5 24 9.5Zm10.25 4.25q-.45-.45-.45-1.05 0-.6.45-1.05l3.15-3.2Q37.85 8 38.475 8t1.075.45Q40 8.9 40 9.5q0 " +
+ ".6-.45 1.05l-3.2 3.2q-.45.45-1.05.45-.6 0-1.05-.45ZM40 25.5q-.65 0-1.075-.425Q38.5 24.65 38.5 24q0-.65.425-1.075Q39.35 " +
+ "22.5 40 22.5h4.5q.65 0 1.075.425Q46 23.35 46 24q0 .65-.425 1.075-.425.425-1.075.425ZM24 46q-.65 " +
+ "0-1.075-.425-.425-.425-.425-1.075V40q0-.65.425-1.075Q23.35 38.5 24 38.5q.65 0 1.075.425.425.425.425 1.075v4.5q0 " +
+ ".65-.425 1.075Q24.65 46 24 46ZM11.65 13.75l-3.2-3.15Q8 10.15 8 9.525t.45-1.075Q8.9 8 9.5 8q.6 0 1.05.45l3.2 " +
+ "3.2q.45.45.45 1.05 0 .6-.45 1.05-.45.4-1.075.4t-1.025-.4Zm25.8 25.8-3.2-3.2q-.45-.45-.45-1.05 0-.6.45-1.05.4-.4 1.025-.4.625 " +
+ "0 1.075.4l3.25 3.15q.45.45.425 1.075Q40 39.1 39.6 39.55q-.45.45-1.075.45t-1.075-.45ZM3.5 25.5q-.65 0-1.075-.425Q2 24.65 2 " +
+ "24q0-.65.425-1.075Q2.85 22.5 3.5 22.5H8q.65 0 1.075.425Q9.5 23.35 9.5 24q0 .65-.425 1.075Q8.65 25.5 8 25.5Zm4.95 14.05Q8 " +
+ "39.1 8 38.5q0-.6.45-1.05l3.2-3.2q.4-.4 1.025-.4.625 0 1.075.4.45.45.45 1.075t-.45 1.075l-3.15 3.15q-.45.45-1.075.45t-1.075-.45ZM24 " +
+ "36q-5 0-8.5-3.5T12 24q0-5 3.5-8.5T24 12q5 0 8.5 3.5T36 24q0 5-3.5 8.5T24 36Zm0-3q3.75 0 6.375-2.625T33 24q0-3.75-2.625-6.375T24 " +
+ "15q-3.75 0-6.375 2.625T15 24q0 3.75 2.625 6.375T24 33Z");
+
+ public MyGeometry()
+ : base(svgPath)
+ { }
+}
diff --git a/samples/ViewModelsSamples/General/VisualElements/ViewModel.cs b/samples/ViewModelsSamples/General/VisualElements/ViewModel.cs
new file mode 100644
index 000000000..f554087cd
--- /dev/null
+++ b/samples/ViewModelsSamples/General/VisualElements/ViewModel.cs
@@ -0,0 +1,75 @@
+using System.Collections.Generic;
+using CommunityToolkit.Mvvm.ComponentModel;
+using LiveChartsCore;
+using LiveChartsCore.Drawing;
+using LiveChartsCore.Kernel;
+using LiveChartsCore.SkiaSharpView;
+using LiveChartsCore.SkiaSharpView.Drawing;
+using LiveChartsCore.SkiaSharpView.Drawing.Geometries;
+using LiveChartsCore.SkiaSharpView.Painting;
+using LiveChartsCore.SkiaSharpView.VisualElements;
+using SkiaSharp;
+
+namespace ViewModelsSamples.General.VisualElements;
+
+[ObservableObject]
+public partial class ViewModel
+{
+ public IEnumerable> VisualElements { get; set; } = new List>
+ {
+ new GeometryVisual
+ {
+ X = 2.5,
+ Y = 3.5,
+ LocationUnit = MeasureUnit.ChartValues,
+ Width = 4,
+ Height = 2,
+ SizeUnit = MeasureUnit.ChartValues,
+ Fill = new SolidColorPaint(new SKColor(239, 83, 80, 50)) { ZIndex = 10 },
+ Stroke = new SolidColorPaint(new SKColor(239, 83, 80)) { ZIndex = 10, StrokeThickness = 1.5f },
+ },
+ new GeometryVisual
+ {
+ X = 5.5,
+ Y = 6,
+ LocationUnit = MeasureUnit.ChartValues,
+ Width = 4,
+ Height = 5,
+ SizeUnit = MeasureUnit.ChartValues,
+ Fill = new SolidColorPaint(new SKColor(100, 221, 23, 50)) { ZIndex = - 10 },
+ Stroke = new SolidColorPaint(new SKColor(100, 221, 23)) { ZIndex = -10, StrokeThickness = 1.5f },
+ },
+ new GeometryVisual
+ {
+ X = 18,
+ Y = 6,
+ LocationUnit = MeasureUnit.ChartValues,
+ Width = 100,
+ Height = 100,
+ SizeUnit = MeasureUnit.Pixels,
+ Fill = new SolidColorPaint(new SKColor(251, 192, 45, 50)) { ZIndex = 10 },
+ Stroke = new SolidColorPaint(new SKColor(251, 192, 45)) { ZIndex = 10, StrokeThickness = 1.5f },
+ },
+ new LabelVisual
+ {
+ Text = "What happened here?",
+ X = 11,
+ Y = 1,
+ TextSize = 16,
+ Paint = new SolidColorPaint(new SKColor(250, 250, 250)) { ZIndex = 11 },
+ BackgroundColor = new LvcColor(55, 71, 79),
+ Padding = new Padding(12),
+ LocationUnit = MeasureUnit.ChartValues,
+ Translate = new LvcPoint(0, -35)
+ }
+ };
+
+ public ISeries[] Series { get; set; } =
+ {
+ new LineSeries
+ {
+ GeometrySize = 13,
+ Values = new int[] { 2,2,3,4,2,2,3,6,3,5,2,1,4,5,2,3,2,4,5,3,2,6 }
+ }
+ };
+}
diff --git a/samples/ViewModelsSamples/Index.cs b/samples/ViewModelsSamples/Index.cs
index 4474da96c..b6325e8bb 100644
--- a/samples/ViewModelsSamples/Index.cs
+++ b/samples/ViewModelsSamples/Index.cs
@@ -88,6 +88,7 @@ public static class Index
"General/Sections",
"General/Sections2",
+ "General/VisualElements",
"General/ChartToImage",
"General/Tooltips",
"General/Legends",
diff --git a/samples/WPFSample/General/VisualElements/View.xaml b/samples/WPFSample/General/VisualElements/View.xaml
new file mode 100644
index 000000000..666d8f2c6
--- /dev/null
+++ b/samples/WPFSample/General/VisualElements/View.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/samples/WPFSample/General/VisualElements/View.xaml.cs b/samples/WPFSample/General/VisualElements/View.xaml.cs
new file mode 100644
index 000000000..41332e3e3
--- /dev/null
+++ b/samples/WPFSample/General/VisualElements/View.xaml.cs
@@ -0,0 +1,14 @@
+using System.Windows.Controls;
+
+namespace WPFSample.General.VisualElements;
+
+///
+/// Interaction logic for View.xaml
+///
+public partial class View : UserControl
+{
+ public View()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/samples/WPFSample/WPFSample.csproj b/samples/WPFSample/WPFSample.csproj
index 8927025d2..f7af80729 100644
--- a/samples/WPFSample/WPFSample.csproj
+++ b/samples/WPFSample/WPFSample.csproj
@@ -116,6 +116,9 @@
Code
+
+ Code
+
Code
@@ -387,6 +390,10 @@
$(DefaultXamlRuntime)
Designer
+
+ $(DefaultXamlRuntime)
+ Designer
+
$(DefaultXamlRuntime)
Designer
diff --git a/samples/WinFormsSample/General/VisualElements/View.Designer.cs b/samples/WinFormsSample/General/VisualElements/View.Designer.cs
new file mode 100644
index 000000000..cd651000f
--- /dev/null
+++ b/samples/WinFormsSample/General/VisualElements/View.Designer.cs
@@ -0,0 +1,46 @@
+
+namespace WinFormsSample.General.VisualElements
+{
+ partial class View
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.SuspendLayout();
+ //
+ // View
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Name = "View";
+ this.Size = new System.Drawing.Size(427, 375);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+ }
+}
diff --git a/samples/WinFormsSample/General/VisualElements/View.cs b/samples/WinFormsSample/General/VisualElements/View.cs
new file mode 100644
index 000000000..039d2a38c
--- /dev/null
+++ b/samples/WinFormsSample/General/VisualElements/View.cs
@@ -0,0 +1,29 @@
+using System.Windows.Forms;
+using LiveChartsCore.SkiaSharpView.WinForms;
+using ViewModelsSamples.General.VisualElements;
+
+namespace WinFormsSample.General.VisualElements;
+
+public partial class View : UserControl
+{
+ public View()
+ {
+ InitializeComponent();
+ Size = new System.Drawing.Size(50, 50);
+
+ var viewModel = new ViewModel();
+
+ var cartesianChart = new CartesianChart
+ {
+ Series = viewModel.Series,
+ VisualElements = viewModel.VisualElements,
+
+ // out of livecharts properties...
+ Location = new System.Drawing.Point(0, 0),
+ Size = new System.Drawing.Size(50, 50),
+ Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom
+ };
+
+ Controls.Add(cartesianChart);
+ }
+}
diff --git a/samples/WinFormsSample/General/VisualElements/View.resx b/samples/WinFormsSample/General/VisualElements/View.resx
new file mode 100644
index 000000000..f298a7be8
--- /dev/null
+++ b/samples/WinFormsSample/General/VisualElements/View.resx
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/samples/WinFormsSample/WinFormsSample.csproj b/samples/WinFormsSample/WinFormsSample.csproj
index 48348c405..1bb46cb55 100644
--- a/samples/WinFormsSample/WinFormsSample.csproj
+++ b/samples/WinFormsSample/WinFormsSample.csproj
@@ -111,6 +111,9 @@
UserControl
+
+ UserControl
+
UserControl
diff --git a/samples/WinUISample/WinUISample/General/VisualElements/View.xaml b/samples/WinUISample/WinUISample/General/VisualElements/View.xaml
new file mode 100644
index 000000000..79df8e7c3
--- /dev/null
+++ b/samples/WinUISample/WinUISample/General/VisualElements/View.xaml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/WinUISample/WinUISample/General/VisualElements/View.xaml.cs b/samples/WinUISample/WinUISample/General/VisualElements/View.xaml.cs
new file mode 100644
index 000000000..bb10350f4
--- /dev/null
+++ b/samples/WinUISample/WinUISample/General/VisualElements/View.xaml.cs
@@ -0,0 +1,11 @@
+using Microsoft.UI.Xaml.Controls;
+
+namespace WinUISample.General.VisualElements;
+
+public sealed partial class View : UserControl
+{
+ public View()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/samples/WinUISample/WinUISample/WinUISample.csproj b/samples/WinUISample/WinUISample/WinUISample.csproj
index 552277b13..5877d2092 100644
--- a/samples/WinUISample/WinUISample/WinUISample.csproj
+++ b/samples/WinUISample/WinUISample/WinUISample.csproj
@@ -47,6 +47,7 @@
+
@@ -237,6 +238,9 @@
$(DefaultXamlRuntime)
+
+ $(DefaultXamlRuntime)
+
$(DefaultXamlRuntime)
diff --git a/samples/XamarinSample/XamarinSample/XamarinSample/General/VisualElements/View.xaml b/samples/XamarinSample/XamarinSample/XamarinSample/General/VisualElements/View.xaml
new file mode 100644
index 000000000..2fb9061c6
--- /dev/null
+++ b/samples/XamarinSample/XamarinSample/XamarinSample/General/VisualElements/View.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/XamarinSample/XamarinSample/XamarinSample/General/VisualElements/View.xaml.cs b/samples/XamarinSample/XamarinSample/XamarinSample/General/VisualElements/View.xaml.cs
new file mode 100644
index 000000000..38e28f2e7
--- /dev/null
+++ b/samples/XamarinSample/XamarinSample/XamarinSample/General/VisualElements/View.xaml.cs
@@ -0,0 +1,13 @@
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace XamarinSample.General.VisualElements;
+
+[XamlCompilation(XamlCompilationOptions.Compile)]
+public partial class View : ContentPage
+{
+ public View()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/samples/XamarinSample/XamarinSample/XamarinSample/XamarinSample.csproj b/samples/XamarinSample/XamarinSample/XamarinSample/XamarinSample.csproj
index 7949b2814..471dcb48e 100644
--- a/samples/XamarinSample/XamarinSample/XamarinSample/XamarinSample.csproj
+++ b/samples/XamarinSample/XamarinSample/XamarinSample/XamarinSample.csproj
@@ -128,6 +128,9 @@
%(Filename)
+
+ %(Filename)
+
%(Filename)
diff --git a/src/LiveChartsCore/Axis.cs b/src/LiveChartsCore/Axis.cs
index c9bc31d62..11d74ffe2 100644
--- a/src/LiveChartsCore/Axis.cs
+++ b/src/LiveChartsCore/Axis.cs
@@ -23,7 +23,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
-using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using LiveChartsCore.Drawing;
@@ -54,8 +53,8 @@ public abstract class Axis
///
protected readonly Dictionary>> activeSeparators = new();
- // xo (x origin) and yo (y origin) are the distance to the center of the axis to the control bounds
internal float _xo = 0f, _yo = 0f;
+ internal LvcSize _size;
internal AxisOrientation _orientation;
internal AnimatableAxisBounds _animatableBounds = new();
internal Bounds _dataBounds = new();
@@ -78,11 +77,12 @@ public abstract class Axis
private double _textSize = 16;
private IPaint? _separatorsPaint;
private IPaint? _subseparatorsPaint;
+ private bool _drawTicksPath;
+ private ILineGeometry? _ticksPath;
private IPaint? _ticksPaint;
private IPaint? _subticksPaint;
private IPaint? _zeroPaint;
private ILineGeometry? _zeroLine;
- private bool _showTicks = true;
private bool _showSeparatorLines = true;
private bool _isVisible = true;
private bool _isInverted;
@@ -96,6 +96,7 @@ public abstract class Axis
float ICartesianAxis.Xo { get => _xo; set => _xo = value; }
float ICartesianAxis.Yo { get => _yo; set => _yo = value; }
+ LvcSize ICartesianAxis.Size { get => _size; set => _size = value; }
LvcRectangle ICartesianAxis.LabelsDesiredSize { get => _labelsDesiredSize; set => _labelsDesiredSize = value; }
LvcRectangle ICartesianAxis.NameDesiredSize { get => _nameDesiredSize; set => _nameDesiredSize = value; }
@@ -158,6 +159,9 @@ public abstract class Axis
///
public IPaint? SubseparatorsPaint { get => _subseparatorsPaint; set { _subseparatorsPaint = value; OnPropertyChanged(); } }
+ ///
+ public bool DrawTicksPath { get => _drawTicksPath; set { _drawTicksPath = value; OnPropertyChanged(); } }
+
///
public IPaint? TicksPaint { get => _ticksPaint; set { _ticksPaint = value; OnPropertyChanged(); } }
@@ -167,9 +171,6 @@ public abstract class Axis
///
public IPaint? ZeroPaint { get => _zeroPaint; set { _zeroPaint = value; OnPropertyChanged(); } }
- ///
- public bool ShowTicks { get => _showTicks; set { _showTicks = value; OnPropertyChanged(); } }
-
///
public bool IsVisible { get => _isVisible; set { _isVisible = value; OnPropertyChanged(); } }
@@ -260,7 +261,7 @@ public override void Measure(Chart chart)
var scale = this.GetNextScaler(cartesianChart);
var actualScale = this.GetActualScalerScaler(cartesianChart) ?? scale;
- var axisTick = this.GetTick(drawMarginSize);
+ var axisTick = this.GetTick(drawMarginSize, null, GetPossibleMaxLabelSize());
var labeler = Labeler;
if (Labels is not null)
@@ -376,16 +377,43 @@ NamePadding is not null || SeparatorsPaint is not null || LabelsPaint is not nul
{
_zeroLine = new TLineGeometry();
ZeroPaint.AddGeometryToPaintTask(cartesianChart.Canvas, _zeroLine);
-
InitializeLine(_zeroLine, cartesianChart);
- UpdateSeparator(_zeroLine!, x, y, lxi, lxj, lyi, lyj, UpdateMode.UpdateAndComplete);
+ UpdateSeparator(_zeroLine, x, y, lxi, lxj, lyi, lyj, UpdateMode.UpdateAndComplete);
}
- if (min <= 0 && max >= 0)
+ UpdateSeparator(_zeroLine, x, y, lxi, lxj, lyi, lyj, UpdateMode.Update);
+ }
+
+ if (TicksPaint is not null && _drawTicksPath)
+ {
+ if (_ticksPath is null)
+ {
+ _ticksPath = new TLineGeometry();
+ InitializeLine(_ticksPath, cartesianChart);
+ TicksPaint.AddGeometryToPaintTask(cartesianChart.Canvas, _ticksPath);
+ }
+
+ if (_orientation == AxisOrientation.X)
+ {
+ var yp = yoo + _size.Height * 0.5f * (_position == AxisPosition.Start ? -1 : 1);
+ _ticksPath.X = lxi;
+ _ticksPath.X1 = lxj;
+ _ticksPath.Y = yp;
+ _ticksPath.Y1 = yp;
+ }
+ else
{
- UpdateSeparator(_zeroLine!, x, y, lxi, lxj, lyi, lyj, UpdateMode.Update);
+ var xp = xoo + _size.Width * 0.5f * (_position == AxisPosition.Start ? 1 : -1);
+ _ticksPath.X = xp;
+ _ticksPath.X1 = xp;
+ _ticksPath.Y = lyi;
+ _ticksPath.Y1 = lyj;
}
+
+ if (!_animatableBounds.HasPreviousState) _ticksPath.CompleteTransition(null);
}
+ if (TicksPaint is not null && _ticksPath is not null && !_drawTicksPath)
+ TicksPaint.RemoveGeometryFromPainTask(cartesianChart.Canvas, _ticksPath);
for (var i = start - s; i <= max + s; i += s)
{
@@ -416,8 +444,6 @@ NamePadding is not null || SeparatorsPaint is not null || LabelsPaint is not nul
yc = actualScale.ToPixels(i);
}
- if (_orientation == AxisOrientation.Y) Trace.WriteLine($"@{i:N2}");
-
if (!separators.TryGetValue(separatorKey, out var visualSeparator))
{
visualSeparator = new AxisVisualSeprator() { Value = i };
@@ -426,22 +452,21 @@ NamePadding is not null || SeparatorsPaint is not null || LabelsPaint is not nul
{
InitializeSeparator(visualSeparator, cartesianChart);
UpdateSeparator(visualSeparator.Separator!, xc, yc, lxi, lxj, lyi, lyj, UpdateMode.UpdateAndComplete);
- if (_orientation == AxisOrientation.Y) Trace.WriteLine($"{i:N2} => {yc:N2}");
}
if (SubseparatorsPaint is not null)
{
InitializeSubseparators(visualSeparator, cartesianChart);
UpdateSubseparators(visualSeparator.Subseparators!, actualScale, s, xc, yc, lxi, lxj, lyi, lyj, UpdateMode.UpdateAndComplete);
}
- if (TicksPaint is not null && ShowTicks)
+ if (TicksPaint is not null)
{
InitializeTick(visualSeparator, cartesianChart);
- UpdateTick(visualSeparator.Tick!, _tickLength, xc, yc, lxi, lxj, lyi, lyj, UpdateMode.UpdateAndComplete);
+ UpdateTick(visualSeparator.Tick!, _tickLength, xc, yc, UpdateMode.UpdateAndComplete);
}
if (SubticksPaint is not null && _subSections > 0)
{
InitializeSubticks(visualSeparator, cartesianChart);
- UpdateSubticks(visualSeparator.Subticks!, actualScale, s, xc, yc, lxi, lxj, lyi, lyj, UpdateMode.UpdateAndComplete);
+ UpdateSubticks(visualSeparator.Subticks!, actualScale, s, xc, yc, UpdateMode.UpdateAndComplete);
}
if (LabelsPaint is not null)
{
@@ -452,8 +477,13 @@ NamePadding is not null || SeparatorsPaint is not null || LabelsPaint is not nul
separators.Add(separatorKey, visualSeparator);
}
- if (SeparatorsPaint is not null && ShowSeparatorLines && visualSeparator.Separator is not null)
- SeparatorsPaint.AddGeometryToPaintTask(cartesianChart.Canvas, visualSeparator.Separator);
+ if (SeparatorsPaint is not null && visualSeparator.Separator is not null)
+ {
+ if (ShowSeparatorLines)
+ SeparatorsPaint.AddGeometryToPaintTask(cartesianChart.Canvas, visualSeparator.Separator);
+ else
+ SeparatorsPaint.RemoveGeometryFromPainTask(cartesianChart.Canvas, visualSeparator.Separator);
+ }
if (SubseparatorsPaint is not null && visualSeparator.Subseparators is not null)
foreach (var subtick in visualSeparator.Subseparators) SubseparatorsPaint.AddGeometryToPaintTask(cartesianChart.Canvas, subtick);
if (LabelsPaint is not null && visualSeparator.Label is not null)
@@ -465,8 +495,8 @@ NamePadding is not null || SeparatorsPaint is not null || LabelsPaint is not nul
if (visualSeparator.Separator is not null) UpdateSeparator(visualSeparator.Separator, x, y, lxi, lxj, lyi, lyj, UpdateMode.Update);
if (visualSeparator.Subseparators is not null) UpdateSubseparators(visualSeparator.Subseparators, scale, s, x, y, lxi, lxj, lyi, lyj, UpdateMode.Update);
- if (visualSeparator.Tick is not null) UpdateTick(visualSeparator.Tick, _tickLength, x, y, lxi, lxj, lyi, lyj, UpdateMode.Update);
- if (visualSeparator.Subticks is not null) UpdateSubticks(visualSeparator.Subticks, scale, s, x, y, lxi, lxj, lyi, lyj, UpdateMode.Update);
+ if (visualSeparator.Tick is not null) UpdateTick(visualSeparator.Tick, _tickLength, x, y, UpdateMode.Update);
+ if (visualSeparator.Subticks is not null) UpdateSubticks(visualSeparator.Subticks, scale, s, x, y, UpdateMode.Update);
if (visualSeparator.Label is not null) UpdateLabel(visualSeparator.Label, x, y, labelContent, hasRotation, r, UpdateMode.Update);
if (hasActivePaint) _ = measured.Add(visualSeparator);
@@ -491,8 +521,8 @@ NamePadding is not null || SeparatorsPaint is not null || LabelsPaint is not nul
if (separator.Separator is not null) UpdateSeparator(separator.Separator, x, y, lxi, lxj, lyi, lyj, UpdateMode.UpdateAndRemove);
if (separator.Subseparators is not null) UpdateSubseparators(separator.Subseparators, scale, s, x, y, lxi, lxj, lyi, lyj, UpdateMode.UpdateAndRemove);
- if (separator.Tick is not null) UpdateTick(separator.Tick, _tickLength, x, y, lxi, lxj, lyi, lyj, UpdateMode.UpdateAndRemove);
- if (separator.Subticks is not null) UpdateSubticks(separator.Subticks, scale, s, x, y, lxi, lxj, lyi, lyj, UpdateMode.UpdateAndRemove);
+ if (separator.Tick is not null) UpdateTick(separator.Tick, _tickLength, x, y, UpdateMode.UpdateAndRemove);
+ if (separator.Subticks is not null) UpdateSubticks(separator.Subticks, scale, s, x, y, UpdateMode.UpdateAndRemove);
if (separator.Label is not null) UpdateLabel(separator.Label, x, y, labeler(separator.Value - 1d + 1d), hasRotation, r, UpdateMode.UpdateAndRemove);
_ = separators.Remove(separatorValueKey.Key);
@@ -618,6 +648,7 @@ protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName
///
protected override void OnPaintChanged(string? propertyName)
{
+ base.OnPaintChanged(propertyName);
OnPropertyChanged(propertyName);
}
@@ -630,6 +661,43 @@ protected override void OnPaintChanged(string? propertyName)
return new[] { _separatorsPaint, _labelsPaint, _namePaint, _zeroPaint, _ticksPaint, _subticksPaint, _subseparatorsPaint };
}
+ private LvcSize GetPossibleMaxLabelSize()
+ {
+ if (LabelsPaint is null) return new LvcSize();
+
+ var labeler = Labeler;
+
+ if (Labels is not null)
+ {
+ labeler = Labelers.BuildNamedLabeler(Labels).Function;
+ _minStep = 1;
+ }
+
+ var max = MaxLimit is null ? _visibleDataBounds.Max : MaxLimit.Value;
+ var min = MinLimit is null ? _visibleDataBounds.Min : MinLimit.Value;
+ var s = (max - min) / 20d;
+
+ var maxLabelSize = new LvcSize();
+ for (var i = min; i <= max; i += s)
+ {
+ var textGeometry = new TTextGeometry
+ {
+ Text = labeler(i),
+ TextSize = (float)TextSize,
+ RotateTransform = (float)LabelsRotation,
+ Padding = _padding
+ };
+
+ var m = textGeometry.Measure(LabelsPaint);
+
+ maxLabelSize = new LvcSize(
+ maxLabelSize.Width > m.Width ? maxLabelSize.Width : m.Width,
+ maxLabelSize.Height > m.Height ? maxLabelSize.Height : m.Height);
+ }
+
+ return maxLabelSize;
+ }
+
private void DrawName(
CartesianChart cartesianChart,
float size,
@@ -815,19 +883,23 @@ private void UpdateSeparator(
}
private void UpdateTick(
- ILineGeometry tick, float length, float x, float y, float lxi, float lxj, float lyi, float lyj, UpdateMode mode)
+ ILineGeometry tick, float length, float x, float y, UpdateMode mode)
{
if (_orientation == AxisOrientation.X)
{
+ var lyi = y + _size.Height * 0.5f;
+ var lyj = y - _size.Height * 0.5f;
tick.X = x;
tick.X1 = x;
- tick.Y = lyj;
- tick.Y1 = lyj + length;
+ tick.Y = _position == AxisPosition.Start ? lyj : lyi - length;
+ tick.Y1 = _position == AxisPosition.Start ? lyj + length : lyi;
}
else
{
- tick.X = lxi;
- tick.X1 = lxi - length;
+ var lxi = x + _size.Width * 0.5f;
+ var lxj = x - _size.Width * 0.5f;
+ tick.X = _position == AxisPosition.Start ? lxi : lxj + length;
+ tick.X1 = _position == AxisPosition.Start ? lxi - length : lxj;
tick.Y = y;
tick.Y1 = y;
}
@@ -858,15 +930,15 @@ private void UpdateSubseparators(
}
private void UpdateSubticks(
- ILineGeometry[] subticks, Scaler scale, double s, float x, float y, float lxi, float lxj, float lyi, float lyj, UpdateMode mode)
+ ILineGeometry[] subticks, Scaler scale, double s, float x, float y, UpdateMode mode)
{
for (var j = 0; j < subticks.Length; j++)
{
var subtick = subticks[j];
- var k = 0.6f;
+ var k = 0.5f;
var kl = (j + 1) / (double)(_subSections + 1);
- if (Math.Abs(kl - 0.5f) < 0.01) k += 0.5f;
+ if (Math.Abs(kl - 0.5f) < 0.01) k += 0.25f;
float xs = 0f, ys = 0f;
if (_orientation == AxisOrientation.X)
@@ -878,7 +950,7 @@ private void UpdateSubticks(
ys = scale.MeasureInPixels(s * kl);
}
- UpdateTick(subtick, _tickLength * k, x + xs, y + ys, lxi, lxj, lyi, lyj, mode);
+ UpdateTick(subtick, _tickLength * k, x + xs, y + ys, mode);
}
}
@@ -905,7 +977,7 @@ private void SetUpdateMode(IGeometry geometry, UpdateMode mode)
switch (mode)
{
case Axis.UpdateMode.UpdateAndComplete:
- if(_animatableBounds.HasPreviousState) geometry.Opacity = 0;
+ if (_animatableBounds.HasPreviousState) geometry.Opacity = 0;
geometry.CompleteTransition(null);
break;
case Axis.UpdateMode.UpdateAndRemove:
diff --git a/src/LiveChartsCore/CartesianChart.cs b/src/LiveChartsCore/CartesianChart.cs
index 66bb943d6..c796d25fc 100644
--- a/src/LiveChartsCore/CartesianChart.cs
+++ b/src/LiveChartsCore/CartesianChart.cs
@@ -43,11 +43,11 @@ public class CartesianChart : Chart
internal readonly HashSet _everMeasuredSeries = new();
internal readonly HashSet> _everMeasuredAxes = new();
internal readonly HashSet> _everMeasuredSections = new();
+ internal readonly HashSet> _everMeasuredVisuals = new();
private readonly ICartesianChartView _chartView;
private readonly ISizedGeometry _zoomingSection;
private int _nextSeries = 0;
private double _zoomingSpeed = 0;
- private readonly bool _requiresLegendMeasureAlways = false;
private ZoomAndPanMode _zoomMode;
private DrawMarginFrame? _previousDrawMarginFrame;
private const double MaxAxisBound = 0.05;
@@ -60,17 +60,14 @@ public class CartesianChart : Chart
/// The default platform configuration.
/// The canvas.
/// The zooming section.
- /// Forces the legends to redraw with every measure request.
public CartesianChart(
ICartesianChartView view,
Action defaultPlatformConfig,
MotionCanvas canvas,
- ISizedGeometry? zoomingSection,
- bool requiresLegendMeasureAlways = false)
+ ISizedGeometry? zoomingSection)
: base(canvas, defaultPlatformConfig, view)
{
_chartView = view;
- _requiresLegendMeasureAlways = requiresLegendMeasureAlways;
_zoomingSection = zoomingSection ?? throw new Exception($"{nameof(zoomingSection)} is required.");
_zoomingSection.X = -1;
_zoomingSection.Y = -1;
@@ -110,6 +107,14 @@ public CartesianChart(
///
public Section[] Sections { get; private set; } = Array.Empty>();
+ ///
+ /// Gets the visual elements.
+ ///
+ ///
+ /// The visual elements.
+ ///
+ public ChartElement[] VisualElements { get; private set; } = Array.Empty>();
+
///
/// Gets the drawable series.
///
@@ -443,6 +448,7 @@ protected internal override void Measure()
.ToArray();
Sections = _chartView.Sections?.Where(x => x.IsVisible).ToArray() ?? Array.Empty>();
+ VisualElements = _chartView.VisualElements?.ToArray() ?? Array.Empty>();
#endregion
@@ -535,17 +541,19 @@ protected internal override void Measure()
#endregion
- if (Legend is not null && (SeriesMiniatureChanged(Series, LegendPosition) || (_requiresLegendMeasureAlways && SizeChanged())))
+ if (Legend is not null && (SeriesMiniatureChanged(Series, LegendPosition) || SizeChanged()))
{
Legend.Draw(this);
- Update();
PreviousLegendPosition = LegendPosition;
PreviousSeriesAtLegend = Series.Where(x => x.IsVisibleAtLegend).ToList();
+ foreach (var series in PreviousSeriesAtLegend.Cast()) series.PaintsChanged = false;
preserveFirstDraw = IsFirstDraw;
+ SetPreviousSize();
+ Measure();
+ return;
}
// calculate draw margin
-
var m = new Margin();
float ts = 0f, bs = 0f, ls = 0f, rs = 0f;
SetDrawMargin(ControlSize, m);
@@ -566,6 +574,8 @@ protected internal override void Measure()
var drawablePlane = (IPlane)axis;
var ns = drawablePlane.GetNameLabelSize(this);
var s = drawablePlane.GetPossibleSize(this);
+ axis.Size = s;
+
if (axis.Position == AxisPosition.Start)
{
// X Bottom
@@ -611,6 +621,7 @@ protected internal override void Measure()
var drawablePlane = (IPlane)axis;
var ns = drawablePlane.GetNameLabelSize(this);
var s = drawablePlane.GetPossibleSize(this);
+ axis.Size = s;
var w = s.Width;
if (axis.Position == AxisPosition.Start)
@@ -715,6 +726,15 @@ protected internal override void Measure()
_ = toDeleteSections.Remove(section);
}
+ var toDeleteVisualElements = new HashSet>(_everMeasuredVisuals);
+ foreach (var visual in VisualElements)
+ {
+ visual.Measure(this);
+ visual.RemoveOldPaints(View);
+ _ = _everMeasuredVisuals.Add(visual);
+ _ = toDeleteVisualElements.Remove(visual);
+ }
+
var toDeleteSeries = new HashSet(_everMeasuredSeries);
foreach (var series in Series)
{
@@ -751,6 +771,11 @@ protected internal override void Measure()
section.RemoveFromUI(this);
_ = _everMeasuredSections.Remove(section);
}
+ foreach (var visual in toDeleteVisualElements)
+ {
+ visual.RemoveFromUI(this);
+ _ = _everMeasuredVisuals.Remove(visual);
+ }
foreach (var axis in totalAxes)
{
@@ -783,6 +808,8 @@ public override void Unload()
_everMeasuredAxes.Clear();
foreach (var item in _everMeasuredSections) item.RemoveFromUI(this);
_everMeasuredSections.Clear();
+ foreach (var item in _everMeasuredVisuals) item.RemoveFromUI(this);
+ _everMeasuredVisuals.Clear();
foreach (var item in _everMeasuredSeries) ((ChartElement)item).RemoveFromUI(this);
_everMeasuredSeries.Clear();
@@ -845,6 +872,18 @@ internal override void InvokePointerUp(LvcPoint point, bool isSecondaryAction)
{
if (_sectionZoomingStart is not null)
{
+ var xy = Math.Sqrt(Math.Pow(point.X - _sectionZoomingStart.Value.X, 2) + Math.Pow(point.Y - _sectionZoomingStart.Value.Y, 2));
+ if (xy < 15)
+ {
+ _zoomingSection.X = -1;
+ _zoomingSection.Y = -1;
+ _zoomingSection.Width = 0;
+ _zoomingSection.Height = 0;
+ Update();
+ _sectionZoomingStart = null;
+ return;
+ }
+
if ((_zoomMode & ZoomAndPanMode.X) == ZoomAndPanMode.X)
{
for (var i = 0; i < XAxes.Length; i++)
@@ -916,7 +955,6 @@ internal override void InvokePointerUp(LvcPoint point, bool isSecondaryAction)
_zoomingSection.Width = 0;
_zoomingSection.Height = 0;
Update();
-
_sectionZoomingStart = null;
return;
}
diff --git a/src/LiveChartsCore/Chart.cs b/src/LiveChartsCore/Chart.cs
index a6059b9f6..c470a0ac9 100644
--- a/src/LiveChartsCore/Chart.cs
+++ b/src/LiveChartsCore/Chart.cs
@@ -408,6 +408,14 @@ protected void SetDrawMargin(LvcSize controlSize, Margin margin)
DrawMarginLocation = new LvcPoint(margin.Left, margin.Top);
}
+ ///
+ /// Saves the previous size of the chart.
+ ///
+ protected void SetPreviousSize()
+ {
+ _previousSize = ControlSize;
+ }
+
///
/// Invokes the event.
///
@@ -423,7 +431,7 @@ protected void InvokeOnMeasuring()
///
protected void InvokeOnUpdateStarted()
{
- _previousSize = ControlSize;
+ SetPreviousSize();
UpdateStarted?.Invoke(View);
}
@@ -465,7 +473,6 @@ protected virtual bool SeriesMiniatureChanged(IReadOnlyList? Fill
///
protected override void OnPaintChanged(string? propertyName)
{
+ base.OnPaintChanged(propertyName);
OnPropertyChanged(propertyName);
}
diff --git a/src/LiveChartsCore/Drawing/Animatable.cs b/src/LiveChartsCore/Drawing/Animatable.cs
index 2ead3b2ff..62de9f6d0 100644
--- a/src/LiveChartsCore/Drawing/Animatable.cs
+++ b/src/LiveChartsCore/Drawing/Animatable.cs
@@ -51,8 +51,7 @@ protected Animatable() { }
public void SetTransition(Animation? animation, params string[]? propertyName)
{
var a = animation?.Duration == 0 ? null : animation;
-
- if (propertyName is null) propertyName = MotionProperties.Keys.ToArray();
+ if (propertyName is null || propertyName.Length == 0) propertyName = MotionProperties.Keys.ToArray();
foreach (var name in propertyName)
{
@@ -63,7 +62,7 @@ public void SetTransition(Animation? animation, params string[]? propertyName)
///
public void RemoveTransition(params string[]? propertyName)
{
- if (propertyName is null) propertyName = MotionProperties.Keys.ToArray();
+ if (propertyName is null || propertyName.Length == 0) propertyName = MotionProperties.Keys.ToArray();
foreach (var name in propertyName)
{
@@ -74,7 +73,7 @@ public void RemoveTransition(params string[]? propertyName)
///
public virtual void CompleteTransition(params string[]? propertyName)
{
- if (propertyName is null) propertyName = MotionProperties.Keys.ToArray();
+ if (propertyName is null || propertyName.Length == 0) propertyName = MotionProperties.Keys.ToArray();
foreach (var property in propertyName)
{
diff --git a/src/LiveChartsCore/Drawing/DrawingContext.cs b/src/LiveChartsCore/Drawing/DrawingContext.cs
index 4c864205c..85085889c 100644
--- a/src/LiveChartsCore/Drawing/DrawingContext.cs
+++ b/src/LiveChartsCore/Drawing/DrawingContext.cs
@@ -28,7 +28,14 @@ namespace LiveChartsCore.Drawing;
public abstract class DrawingContext
{
///
- /// Clears the canvas.
+ /// Called when the frame starts.
///
- public abstract void ClearCanvas();
+ public virtual void OnBegingDraw()
+ { }
+
+ ///
+ /// Called when the frame ends.
+ ///
+ public virtual void OnEndDraw()
+ { }
}
diff --git a/src/LiveChartsCore/Drawing/TransitionBuilder.cs b/src/LiveChartsCore/Drawing/TransitionBuilder.cs
index 048d344db..9609d0162 100644
--- a/src/LiveChartsCore/Drawing/TransitionBuilder.cs
+++ b/src/LiveChartsCore/Drawing/TransitionBuilder.cs
@@ -21,6 +21,7 @@
// SOFTWARE.
using System;
+using LiveChartsCore.Kernel.Sketches;
namespace LiveChartsCore.Drawing;
@@ -66,6 +67,18 @@ public TransitionBuilder WithAnimation(Action animationBuilder)
return WithAnimation(animation);
}
+ ///
+ /// Sets the animations based on the chart and properties.
+ ///
+ /// The chart.
+ /// The transition
+ public TransitionBuilder WithAnimation(IChart chart)
+ {
+ return WithAnimation(new Animation()
+ .WithDuration(chart.View.AnimationsSpeed)
+ .WithEasingFunction(chart.View.EasingFunction));
+ }
+
///
/// Sets the current transitions.
///
diff --git a/src/LiveChartsCore/FinancialSeries.cs b/src/LiveChartsCore/FinancialSeries.cs
index 59f263f2b..28cd151bb 100644
--- a/src/LiveChartsCore/FinancialSeries.cs
+++ b/src/LiveChartsCore/FinancialSeries.cs
@@ -420,6 +420,7 @@ protected internal override void SoftDeleteOrDisposePoint(ChartPoint point, Scal
///
protected override void OnPaintChanged(string? propertyName)
{
+ base.OnPaintChanged(propertyName);
OnSeriesMiniatureChanged();
OnPropertyChanged();
}
diff --git a/src/LiveChartsCore/ISeries.cs b/src/LiveChartsCore/ISeries.cs
index 87c3a0127..30ba3ac26 100644
--- a/src/LiveChartsCore/ISeries.cs
+++ b/src/LiveChartsCore/ISeries.cs
@@ -43,7 +43,12 @@ public interface ISeries : IStopNPC
///
/// Gets the properties of the series.
///
- SeriesProperties SeriesProperties { get; }
+ SeriesProperties SeriesProperties { get; }
+
+ ///
+ /// Gets a value indicating whether any paint changed.
+ ///
+ bool PaintsChanged { get; set; }
///
/// Gets the active pints.
diff --git a/src/LiveChartsCore/Kernel/ChartElement.cs b/src/LiveChartsCore/Kernel/ChartElement.cs
index af1312d8b..1c442ed63 100644
--- a/src/LiveChartsCore/Kernel/ChartElement.cs
+++ b/src/LiveChartsCore/Kernel/ChartElement.cs
@@ -35,6 +35,9 @@ public abstract class ChartElement : IChartElement> _deletingTasks = new();
+ ///
+ public object? Tag { get; set; }
+
///
public abstract void Measure(Chart chart);
diff --git a/src/LiveChartsCore/Kernel/Extensions.cs b/src/LiveChartsCore/Kernel/Extensions.cs
index aedc535b7..db7e02bb4 100644
--- a/src/LiveChartsCore/Kernel/Extensions.cs
+++ b/src/LiveChartsCore/Kernel/Extensions.cs
@@ -110,33 +110,19 @@ public static class Extensions
///
/// The axis.
/// Size of the control.
+ /// The bounds.
+ /// The max label size.
///
- public static AxisTick GetTick(this ICartesianAxis axis, LvcSize controlSize)
+ public static AxisTick GetTick(this ICartesianAxis axis, LvcSize controlSize, Bounds? bounds = null, LvcSize? maxLabelSize = null)
{
- return GetTick(axis, controlSize, axis.VisibleDataBounds);
- }
+ bounds ??= axis.VisibleDataBounds;
- ///
- /// Gets the tick.
- ///
- /// The axis.
- /// The chart.
- ///
- public static AxisTick GetTick(this IPolarAxis axis, PolarChart chart)
- where TDrawingContext : DrawingContext
- {
- return GetTick(axis, chart, axis.VisibleDataBounds);
- }
+ var w = (maxLabelSize?.Width ?? 0d) * 0.60;
+ if (w < 20 * Cf) w = 20 * Cf;
+
+ var h = maxLabelSize?.Height ?? 0d;
+ if (h < 12 * Cf) h = 12 * Cf;
- ///
- /// Gets the tick.
- ///
- /// The axis.
- /// Size of the control.
- /// The bounds.
- ///
- public static AxisTick GetTick(this ICartesianAxis axis, LvcSize controlSize, Bounds bounds)
- {
var max = axis.MaxLimit is null ? bounds.Max : axis.MaxLimit.Value;
var min = axis.MinLimit is null ? bounds.Min : axis.MinLimit.Value;
@@ -144,8 +130,8 @@ public static AxisTick GetTick(this ICartesianAxis axis, LvcSize controlSize, Bo
if (range == 0) range = min;
var separations = axis.Orientation == AxisOrientation.Y
- ? Math.Round(controlSize.Height / (12 * Cf), 0)
- : Math.Round(controlSize.Width / (20 * Cf), 0);
+ ? Math.Round(controlSize.Height / h, 0)
+ : Math.Round(controlSize.Width / w, 0);
var minimum = range / separations;
@@ -164,9 +150,11 @@ public static AxisTick GetTick(this ICartesianAxis axis, LvcSize controlSize, Bo
/// The chart.
/// The bounds.
///
- public static AxisTick GetTick(this IPolarAxis axis, PolarChart chart, Bounds bounds)
+ public static AxisTick GetTick(this IPolarAxis axis, PolarChart chart, Bounds? bounds = null)
where TDrawingContext : DrawingContext
{
+ bounds ??= axis.VisibleDataBounds;
+
var max = axis.MaxLimit is null ? bounds.Max : axis.MaxLimit.Value;
var min = axis.MinLimit is null ? bounds.Min : axis.MinLimit.Value;
diff --git a/src/LiveChartsCore/Kernel/IChartElement.cs b/src/LiveChartsCore/Kernel/IChartElement.cs
index 9d6da960e..48c0f987b 100644
--- a/src/LiveChartsCore/Kernel/IChartElement.cs
+++ b/src/LiveChartsCore/Kernel/IChartElement.cs
@@ -31,6 +31,11 @@ namespace LiveChartsCore.Kernel;
public interface IChartElement
where TDrawingContext : DrawingContext
{
+ ///
+ /// Gets or sets the object that contains data about the control.
+ ///
+ object? Tag { get; set; }
+
///
/// Measures and schedule the draw of the element in the user interface.
///
diff --git a/src/LiveChartsCore/Kernel/Sketches/ICartesianAxis.cs b/src/LiveChartsCore/Kernel/Sketches/ICartesianAxis.cs
index 7afadd66f..8d65208e8 100644
--- a/src/LiveChartsCore/Kernel/Sketches/ICartesianAxis.cs
+++ b/src/LiveChartsCore/Kernel/Sketches/ICartesianAxis.cs
@@ -64,6 +64,14 @@ public interface ICartesianAxis : IPlane, INotifyPropertyChanged
///
float Yo { get; set; }
+ ///
+ /// Gets or sets the size of the axis, this value is used internally to calculate the axis position.
+ ///
+ ///
+ /// The length.
+ ///
+ LvcSize Size { get; set; }
+
///
/// Gets or sets the reserved area for the labels.
///
@@ -109,6 +117,11 @@ public interface ICartesianAxis : ICartesianAxis
///
IPaint? SubseparatorsPaint { get; set; }
+ ///
+ /// Gets or sets whether the ticks path should be drawn.
+ ///
+ bool DrawTicksPath { get; set; }
+
///
/// Gets or sets the separators paint.
///
@@ -132,12 +145,4 @@ public interface ICartesianAxis : ICartesianAxis
/// The separators paint.
///
IPaint? ZeroPaint { get; set; }
-
- ///
- /// Gets or sets the separators paint.
- ///
- ///
- /// The separators paint.
- ///
- bool ShowTicks { get; set; }
}
diff --git a/src/LiveChartsCore/Kernel/Sketches/ICartesianChartView.cs b/src/LiveChartsCore/Kernel/Sketches/ICartesianChartView.cs
index 629d07c7f..bfef33960 100644
--- a/src/LiveChartsCore/Kernel/Sketches/ICartesianChartView.cs
+++ b/src/LiveChartsCore/Kernel/Sketches/ICartesianChartView.cs
@@ -66,6 +66,11 @@ public interface ICartesianChartView : IChartView
IEnumerable> Sections { get; set; }
+ ///
+ /// Gets or sets the visual elements.
+ ///
+ IEnumerable> VisualElements { get; set; }
+
///
/// Gets or sets the series to plot in the user interface.
///
diff --git a/src/LiveChartsCore/Kernel/Sketches/IPieChartView.cs b/src/LiveChartsCore/Kernel/Sketches/IPieChartView.cs
index 7607c72b7..8da06a4a7 100644
--- a/src/LiveChartsCore/Kernel/Sketches/IPieChartView.cs
+++ b/src/LiveChartsCore/Kernel/Sketches/IPieChartView.cs
@@ -49,6 +49,11 @@ public interface IPieChartView : IChartView
///
IEnumerable Series { get; set; }
+ ///
+ /// Gets or sets the visual elements.
+ ///
+ IEnumerable> VisualElements { get; set; }
+
///
/// Gets or sets the initial rotation in degrees, this angle specifies where the first pie slice will be drawn, then the remaining
/// slices will stack according to its corresponding position.
diff --git a/src/LiveChartsCore/Kernel/Sketches/IPolarChartView.cs b/src/LiveChartsCore/Kernel/Sketches/IPolarChartView.cs
index aa6d16a15..047c9acc0 100644
--- a/src/LiveChartsCore/Kernel/Sketches/IPolarChartView.cs
+++ b/src/LiveChartsCore/Kernel/Sketches/IPolarChartView.cs
@@ -87,13 +87,10 @@ public interface IPolarChartView : IChartView
///
IEnumerable RadiusAxes { get; set; }
- /////
- ///// Gets or sets the sections.
- /////
- /////
- ///// The sections.
- /////
- //IEnumerable> Sections { get; set; }
+ ///
+ /// Gets or sets the visual elements.
+ ///
+ IEnumerable> VisualElements { get; set; }
///
/// Gets or sets the series to plot in the user interface.
@@ -103,22 +100,6 @@ public interface IPolarChartView : IChartView
///
IEnumerable Series { get; set; }
- /////
- ///// Gets or sets the tool tip finding strategy.
- /////
- /////
- ///// The tool tip finding strategy.
- /////
- //TooltipFindingStrategy TooltipFindingStrategy { get; set; }
-
- /////
- ///// Gets or sets the zooming speed from 0 to 1, where 0 is the fastest and 1 the slowest.
- /////
- /////
- ///// The zooming speed.
- /////
- //double ZoomingSpeed { get; set; }
-
///
/// Scales the UI point.
///
diff --git a/src/LiveChartsCore/LineSeries.cs b/src/LiveChartsCore/LineSeries.cs
index 45854a291..01ac1f771 100644
--- a/src/LiveChartsCore/LineSeries.cs
+++ b/src/LiveChartsCore/LineSeries.cs
@@ -22,7 +22,6 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
using LiveChartsCore.Drawing;
using LiveChartsCore.Drawing.Segments;
@@ -534,6 +533,16 @@ protected override void OnSeriesMiniatureChanged()
CanvasSchedule = context;
}
+ ///
+ public override bool MiniatureEquals(IChartSeries series)
+ {
+ return series is LineSeries lineSeries &&
+ Name == series.Name &&
+ !((ISeries)this).PaintsChanged &&
+ Fill == lineSeries.Fill && Stroke == lineSeries.Stroke &&
+ GeometryFill == lineSeries.GeometryFill && GeometryStroke == lineSeries.GeometryStroke;
+ }
+
///
/// Builds an spline from the given points.
///
diff --git a/src/LiveChartsCore/LiveChartsCore.csproj b/src/LiveChartsCore/LiveChartsCore.csproj
index 89fa556b0..2c85d9c0c 100644
--- a/src/LiveChartsCore/LiveChartsCore.csproj
+++ b/src/LiveChartsCore/LiveChartsCore.csproj
@@ -17,7 +17,7 @@
LiveChartsCore
LiveChartsCore
- 2.0.0-beta.361
+ 2.0.0-beta.400
icon.png
Simple, flexible, interactive and powerful data visualization for .Net, this is the core package probably you need another package also unless you are building your own backed.
MIT
diff --git a/src/LiveChartsCore/Motion/MotionCanvas.cs b/src/LiveChartsCore/Motion/MotionCanvas.cs
index e59d51561..10e3a9a61 100644
--- a/src/LiveChartsCore/Motion/MotionCanvas.cs
+++ b/src/LiveChartsCore/Motion/MotionCanvas.cs
@@ -49,13 +49,12 @@ public MotionCanvas()
_stopwatch.Start();
}
+ internal bool DisableAnimations { get; set; }
+
///
- /// Gets a value indicating whether the animations are disabled.
+ /// Gets or sets the point where the draw starts.
///
- ///
- /// true if [disable animations]; otherwise, false.
- ///
- internal bool DisableAnimations { get; set; }
+ public LvcPoint? StartPoint { get; set; }
///
/// Occurs when the visual is invalidated.
@@ -104,9 +103,10 @@ public void DrawFrame(TDrawingContext context)
lock (Sync)
{
+ context.OnBegingDraw();
+
var isValid = true;
var frameTime = _stopwatch.ElapsedMilliseconds;
- context.ClearCanvas();
var toRemoveGeometries = new List, IDrawable>>();
@@ -170,6 +170,8 @@ public void DrawFrame(TDrawingContext context)
_previousFrameTime = frameTime;
IsValid = isValid;
+
+ context.OnEndDraw();
}
if (IsValid) Validated?.Invoke(this);
diff --git a/src/LiveChartsCore/PieChart.cs b/src/LiveChartsCore/PieChart.cs
index d3c896ef0..06df7db6d 100644
--- a/src/LiveChartsCore/PieChart.cs
+++ b/src/LiveChartsCore/PieChart.cs
@@ -41,6 +41,7 @@ public class PieChart : Chart
where TDrawingContext : DrawingContext
{
private readonly HashSet _everMeasuredSeries = new();
+ internal readonly HashSet> _everMeasuredVisuals = new();
private readonly IPieChartView _chartView;
private int _nextSeries = 0;
private readonly bool _requiresLegendMeasureAlways = false;
@@ -71,6 +72,14 @@ public PieChart(
///
public IPieSeries[] Series { get; private set; } = Array.Empty>();
+ ///
+ /// Gets the visual elements.
+ ///
+ ///
+ /// The visual elements.
+ ///
+ public ChartElement[] VisualElements { get; private set; } = Array.Empty>();
+
///
/// Gets the drawable series.
///
@@ -163,6 +172,8 @@ protected internal override void Measure()
.Cast>()
.ToArray();
+ VisualElements = _chartView.VisualElements?.ToArray() ?? Array.Empty>();
+
LegendPosition = _chartView.LegendPosition;
LegendOrientation = _chartView.LegendOrientation;
Legend = _chartView.Legend;
@@ -221,6 +232,15 @@ protected internal override void Measure()
// or it is initializing in the UI and has no dimensions yet
if (DrawMarginSize.Width <= 0 || DrawMarginSize.Height <= 0) return;
+ var toDeleteVisualElements = new HashSet>(_everMeasuredVisuals);
+ foreach (var visual in VisualElements)
+ {
+ visual.Measure(this);
+ visual.RemoveOldPaints(View);
+ _ = _everMeasuredVisuals.Add(visual);
+ _ = toDeleteVisualElements.Remove(visual);
+ }
+
var toDeleteSeries = new HashSet(_everMeasuredSeries);
foreach (var series in Series)
{
@@ -235,6 +255,11 @@ protected internal override void Measure()
series.SoftDeleteOrDispose(View);
_ = _everMeasuredSeries.Remove(series);
}
+ foreach (var visual in toDeleteVisualElements)
+ {
+ visual.RemoveFromUI(this);
+ _ = _everMeasuredVisuals.Remove(visual);
+ }
InvokeOnUpdateStarted();
IsFirstDraw = false;
@@ -252,6 +277,8 @@ public override void Unload()
foreach (var item in _everMeasuredSeries) ((ChartElement)item).RemoveFromUI(this);
_everMeasuredSeries.Clear();
+ foreach (var item in _everMeasuredVisuals) item.RemoveFromUI(this);
+ _everMeasuredVisuals.Clear();
IsFirstDraw = true;
}
}
diff --git a/src/LiveChartsCore/PieSeries.cs b/src/LiveChartsCore/PieSeries.cs
index fd95d62fd..a511302b9 100644
--- a/src/LiveChartsCore/PieSeries.cs
+++ b/src/LiveChartsCore/PieSeries.cs
@@ -37,12 +37,14 @@ namespace LiveChartsCore;
/// The type of the model.
/// The type of the visual.
/// The type of the label.
+/// The type of the miniature geometry, used in tool tips and legends.
/// The type of the drawing context.
-public abstract class PieSeries
+public abstract class PieSeries
: ChartSeries, IPieSeries
where TDrawingContext : DrawingContext
where TVisual : class, IDoughnutVisualChartPoint, new()
where TLabel : class, ILabelGeometry, new()
+ where TMiniatureGeometry : ISizedGeometry, new()
{
private IPaint? _stroke = null;
private IPaint? _fill = null;
@@ -60,7 +62,7 @@ public abstract class PieSeries
private PolarLabelsPosition _labelsPosition = PolarLabelsPosition.Middle;
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
protected PieSeries(bool isGauge = false, bool isGaugeFill = false)
: base(SeriesProperties.PieSeries | SeriesProperties.Stacked |
@@ -456,16 +458,12 @@ protected override void OnSeriesMiniatureChanged()
strokeClone.StrokeThickness = MaxSeriesStroke;
}
- var visual = new TVisual
+ var visual = new TMiniatureGeometry
{
X = st + MaxSeriesStroke - st,
Y = st + MaxSeriesStroke - st,
Height = (float)LegendShapeSize,
- Width = (float)LegendShapeSize,
- CenterX = (float)LegendShapeSize * 0.5f,
- CenterY = (float)LegendShapeSize * 0.5f,
- StartAngle = 0,
- SweepAngle = 359.9999f
+ Width = (float)LegendShapeSize
};
sh = st;
strokeClone.ZIndex = 1;
@@ -475,16 +473,12 @@ protected override void OnSeriesMiniatureChanged()
if (Fill is not null)
{
var fillClone = Fill.CloneTask();
- var visual = new TVisual
+ var visual = new TMiniatureGeometry
{
X = sh + MaxSeriesStroke - sh,
Y = sh + MaxSeriesStroke - sh,
Height = (float)LegendShapeSize,
- Width = (float)LegendShapeSize,
- CenterX = (float)LegendShapeSize * 0.5f,
- CenterY = (float)LegendShapeSize * 0.5f,
- StartAngle = 0,
- SweepAngle = 359.9999f
+ Width = (float)LegendShapeSize
};
context.PaintSchedules.Add(new PaintSchedule(fillClone, visual));
}
@@ -538,6 +532,7 @@ protected override void WhenPointerLeaves(ChartPoint point)
///
protected override void OnPaintChanged(string? propertyName)
{
+ base.OnPaintChanged(propertyName);
OnSeriesMiniatureChanged();
OnPropertyChanged(propertyName);
}
@@ -545,7 +540,7 @@ protected override void OnPaintChanged(string? propertyName)
///
public override bool MiniatureEquals(IChartSeries instance)
{
- return instance is PieSeries pieSeries &&
+ return instance is PieSeries pieSeries &&
Name == pieSeries.Name && Fill == pieSeries.Fill && Stroke == pieSeries.Stroke;
}
diff --git a/src/LiveChartsCore/PolarAxis.cs b/src/LiveChartsCore/PolarAxis.cs
index cb9910899..4237ca060 100644
--- a/src/LiveChartsCore/PolarAxis.cs
+++ b/src/LiveChartsCore/PolarAxis.cs
@@ -656,6 +656,7 @@ protected virtual void SoftDeleteSeparator(
///
protected override void OnPaintChanged(string? propertyName)
{
+ base.OnPaintChanged(propertyName);
OnPropertyChanged(propertyName);
}
diff --git a/src/LiveChartsCore/PolarChart.cs b/src/LiveChartsCore/PolarChart.cs
index 3ce0a1bc7..5a5833623 100644
--- a/src/LiveChartsCore/PolarChart.cs
+++ b/src/LiveChartsCore/PolarChart.cs
@@ -43,6 +43,7 @@ public class PolarChart : Chart
internal readonly HashSet _everMeasuredSeries = new();
internal readonly HashSet> _everMeasuredAxes = new();
internal readonly HashSet> _everMeasuredSections = new();
+ internal readonly HashSet> _everMeasuredVisuals = new();
private readonly IPolarChartView _chartView;
private int _nextSeries = 0;
private readonly bool _requiresLegendMeasureAlways = false;
@@ -89,6 +90,14 @@ public PolarChart(
///
public IPolarSeries[] Series { get; private set; } = Array.Empty>();
+ ///
+ /// Gets the visual elements.
+ ///
+ ///
+ /// The visual elements.
+ ///
+ public ChartElement[] VisualElements { get; private set; } = Array.Empty>();
+
///
/// Gets whether the series fit to bounds or not.
///
@@ -208,6 +217,8 @@ protected internal override void Measure()
.Cast>()
.ToArray();
+ VisualElements = _chartView.VisualElements?.ToArray() ?? Array.Empty>();
+
#endregion
SeriesContext = new SeriesContext(Series);
@@ -476,6 +487,15 @@ protected internal override void Measure()
drawablePlane.RemoveOldPaints(View);
}
+ var toDeleteVisualElements = new HashSet>(_everMeasuredVisuals);
+ foreach (var visual in VisualElements)
+ {
+ visual.Measure(this);
+ visual.RemoveOldPaints(View);
+ _ = _everMeasuredVisuals.Add(visual);
+ _ = toDeleteVisualElements.Remove(visual);
+ }
+
var toDeleteSeries = new HashSet(_everMeasuredSeries);
foreach (var series in Series)
{
@@ -495,6 +515,11 @@ protected internal override void Measure()
axis.RemoveFromUI(this);
_ = _everMeasuredAxes.Remove(axis);
}
+ foreach (var visual in toDeleteVisualElements)
+ {
+ visual.RemoveFromUI(this);
+ _ = _everMeasuredVisuals.Remove(visual);
+ }
foreach (var axis in totalAxes)
{
@@ -545,6 +570,8 @@ public override void Unload()
_everMeasuredAxes.Clear();
foreach (var item in _everMeasuredSections) item.RemoveFromUI(this);
_everMeasuredSections.Clear();
+ foreach (var item in _everMeasuredVisuals) item.RemoveFromUI(this);
+ _everMeasuredVisuals.Clear();
foreach (var item in _everMeasuredSeries) ((ChartElement)item).RemoveFromUI(this);
_everMeasuredSeries.Clear();
IsFirstDraw = true;
diff --git a/src/LiveChartsCore/PolarLineSeries.cs b/src/LiveChartsCore/PolarLineSeries.cs
index 6e8c24f8f..f06f3963d 100644
--- a/src/LiveChartsCore/PolarLineSeries.cs
+++ b/src/LiveChartsCore/PolarLineSeries.cs
@@ -489,6 +489,7 @@ public override bool MiniatureEquals(IChartSeries series)
///
protected override void OnPaintChanged(string? propertyName)
{
+ base.OnPaintChanged(propertyName);
OnSeriesMiniatureChanged();
OnPropertyChanged();
}
diff --git a/src/LiveChartsCore/Section.cs b/src/LiveChartsCore/Section.cs
index 3b6b4e874..5eab64bbd 100644
--- a/src/LiveChartsCore/Section.cs
+++ b/src/LiveChartsCore/Section.cs
@@ -160,6 +160,7 @@ public IPaint? Fill
///
protected override void OnPaintChanged(string? propertyName)
{
+ base.OnPaintChanged(propertyName);
OnPropertyChanged(propertyName);
}
diff --git a/src/LiveChartsCore/Series.cs b/src/LiveChartsCore/Series.cs
index 3e19b1377..d424831fd 100644
--- a/src/LiveChartsCore/Series.cs
+++ b/src/LiveChartsCore/Series.cs
@@ -117,6 +117,8 @@ protected Series(SeriesProperties properties)
(sender, e) => NotifySubscribers());
}
+ bool ISeries.PaintsChanged { get; set; }
+
///
public HashSet ActivePoints => everFetched;
@@ -455,6 +457,13 @@ protected virtual void WhenPointerLeaves(ChartPoint point)
ChartPointPointerHoverLost?.Invoke(point.Context.Chart, new ChartPoint(point));
}
+ ///
+ protected override void OnPaintChanged(string? propertyName)
+ {
+ base.OnPaintChanged(propertyName);
+ ((ISeries)this).PaintsChanged = true;
+ }
+
///
public override void RemoveFromUI(Chart chart)
{
diff --git a/src/LiveChartsCore/StepLineSeries.cs b/src/LiveChartsCore/StepLineSeries.cs
index 39300a3a4..0d3d8defc 100644
--- a/src/LiveChartsCore/StepLineSeries.cs
+++ b/src/LiveChartsCore/StepLineSeries.cs
@@ -483,6 +483,16 @@ protected override void OnSeriesMiniatureChanged()
CanvasSchedule = context;
}
+ ///
+ public override bool MiniatureEquals(IChartSeries series)
+ {
+ return series is StepLineSeries stepSeries &&
+ Name == series.Name &&
+ !((ISeries)this).PaintsChanged &&
+ Fill == stepSeries.Fill && Stroke == stepSeries.Stroke &&
+ GeometryFill == stepSeries.GeometryFill && GeometryStroke == stepSeries.GeometryStroke;
+ }
+
///
protected override void SetDefaultPointTransitions(ChartPoint chartPoint)
{
diff --git a/src/LiveChartsCore/StrokeAndFillCartesianSeries.cs b/src/LiveChartsCore/StrokeAndFillCartesianSeries.cs
index 91a33da77..15ef2e5c6 100644
--- a/src/LiveChartsCore/StrokeAndFillCartesianSeries.cs
+++ b/src/LiveChartsCore/StrokeAndFillCartesianSeries.cs
@@ -82,6 +82,7 @@ public IPaint? Fill
///
protected override void OnPaintChanged(string? propertyName)
{
+ base.OnPaintChanged(propertyName);
OnSeriesMiniatureChanged();
OnPropertyChanged();
}
@@ -100,6 +101,6 @@ protected override void OnPaintChanged(string? propertyName)
public override bool MiniatureEquals(IChartSeries series)
{
return series is StrokeAndFillCartesianSeries sfSeries &&
- Name == series.Name && Fill == sfSeries.Fill && Stroke == sfSeries.Stroke;
+ Name == series.Name && !((ISeries)this).PaintsChanged && Fill == sfSeries.Fill && Stroke == sfSeries.Stroke;
}
}
diff --git a/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/CartesianChart.axaml.cs b/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/CartesianChart.axaml.cs
index 937f8420c..e6444db4a 100644
--- a/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/CartesianChart.axaml.cs
+++ b/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/CartesianChart.axaml.cs
@@ -67,6 +67,7 @@ public class CartesianChart : UserControl, ICartesianChartView _xObserver;
private readonly CollectionDeepObserver _yObserver;
private readonly CollectionDeepObserver> _sectionsObserver;
+ private readonly CollectionDeepObserver> _visualsObserver;
#endregion
@@ -99,6 +100,8 @@ public CartesianChart()
_yObserver = new CollectionDeepObserver(OnDeepCollectionChanged, OnDeepCollectionPropertyChanged, true);
_sectionsObserver = new CollectionDeepObserver>(
OnDeepCollectionChanged, OnDeepCollectionPropertyChanged, true);
+ _visualsObserver = new CollectionDeepObserver>(
+ OnDeepCollectionChanged, OnDeepCollectionPropertyChanged, true);
XAxes = new List()
{
@@ -109,6 +112,7 @@ public CartesianChart()
LiveCharts.CurrentSettings.GetProvider().GetDefaultCartesianAxis()
};
Series = new ObservableCollection();
+ VisualElements = new ObservableCollection>();
PointerPressed += CartesianChart_PointerPressed;
PointerMoved += CartesianChart_PointerMoved;
@@ -158,6 +162,13 @@ public CartesianChart()
AvaloniaProperty.Register>>(
nameof(Sections), Enumerable.Empty>(), inherits: true);
+ ///
+ /// The visual elements property
+ ///
+ public static readonly AvaloniaProperty>> VisualElementsProperty =
+ AvaloniaProperty.Register>>(
+ nameof(VisualElements), Enumerable.Empty>(), inherits: true);
+
///
/// The draw margin frame property
///
@@ -418,6 +429,13 @@ public IEnumerable> Sections
set => SetValue(SectionsProperty, value);
}
+ ///
+ public IEnumerable> VisualElements
+ {
+ get => (IEnumerable>)GetValue(VisualElementsProperty);
+ set => SetValue(VisualElementsProperty, value);
+ }
+
///
public DrawMarginFrame? DrawMarginFrame
{
@@ -801,6 +819,12 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs
_sectionsObserver?.Initialize((IEnumerable>)change.NewValue.Value);
}
+ if (change.Property.Name == nameof(VisualElementsProperty))
+ {
+ _visualsObserver?.Dispose((IEnumerable>)change.OldValue.Value);
+ _visualsObserver?.Initialize((IEnumerable>)change.NewValue.Value);
+ }
+
_core.Update();
}
diff --git a/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/LiveChartsCore.SkiaSharpView.Avalonia.csproj b/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/LiveChartsCore.SkiaSharpView.Avalonia.csproj
index 66efb3895..faedf3156 100644
--- a/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/LiveChartsCore.SkiaSharpView.Avalonia.csproj
+++ b/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/LiveChartsCore.SkiaSharpView.Avalonia.csproj
@@ -6,7 +6,7 @@
netcoreapp2.0;netstandard2.0;net462;
LiveChartsCore.SkiaSharpView.Avalonia
LiveChartsCore.SkiaSharpView.Avalonia
- 2.0.0-beta.361
+ 2.0.0-beta.400
icon.png
Simple, flexible, interactive and powerful data visualization for AvaloniaUI.
MIT
diff --git a/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/PieChart.axaml.cs b/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/PieChart.axaml.cs
index 26955ce9a..1516bc9d3 100644
--- a/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/PieChart.axaml.cs
+++ b/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/PieChart.axaml.cs
@@ -25,6 +25,7 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
+using System.Linq;
using System.Windows.Input;
using Avalonia;
using Avalonia.Controls;
@@ -59,7 +60,8 @@ public class PieChart : UserControl, IPieChartView, IAv
protected IChartTooltip? tooltip;
private Chart? _core;
- private CollectionDeepObserver _seriesObserver;
+ private readonly CollectionDeepObserver _seriesObserver;
+ private readonly CollectionDeepObserver> _visualsObserver;
private MotionCanvas? _avaloniaCanvas;
#endregion
@@ -98,9 +100,21 @@ public PieChart()
{
if (_core is null || (sender is IStopNPC stop && !stop.IsNotifyingChanges)) return;
_core.Update();
- });
+ }, true);
+ _visualsObserver = new CollectionDeepObserver>(
+ (object? sender, NotifyCollectionChangedEventArgs e) =>
+ {
+ if (_core is null || (sender is IStopNPC stop && !stop.IsNotifyingChanges)) return;
+ _core.Update();
+ },
+ (object? sender, PropertyChangedEventArgs e) =>
+ {
+ if (_core is null || (sender is IStopNPC stop && !stop.IsNotifyingChanges)) return;
+ _core.Update();
+ }, true);
Series = new ObservableCollection();
+ VisualElements = new ObservableCollection>();
PointerLeave += Chart_PointerLeave;
PointerMoved += Chart_PointerMoved;
@@ -128,6 +142,13 @@ public PieChart()
public static readonly AvaloniaProperty> SeriesProperty =
AvaloniaProperty.Register>(nameof(Series), new List(), inherits: true);
+ ///
+ /// The visual elements property
+ ///
+ public static readonly AvaloniaProperty>> VisualElementsProperty =
+ AvaloniaProperty.Register>>(
+ nameof(VisualElements), Enumerable.Empty>(), inherits: true);
+
///
/// The initial rotation property
///
@@ -356,6 +377,13 @@ public IEnumerable Series
set => SetValue(SeriesProperty, value);
}
+ ///
+ public IEnumerable> VisualElements
+ {
+ get => (IEnumerable>)GetValue(VisualElementsProperty);
+ set => SetValue(VisualElementsProperty, value);
+ }
+
///
public double InitialRotation
{
@@ -695,6 +723,13 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs
return;
}
+ if (change.Property.Name == nameof(VisualElements))
+ {
+ _visualsObserver?.Dispose((IEnumerable>)change.OldValue.Value);
+ _visualsObserver?.Initialize((IEnumerable>)change.NewValue.Value);
+ return;
+ }
+
_core.Update();
}
diff --git a/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/PolarChart.axaml.cs b/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/PolarChart.axaml.cs
index bdf997ff7..54e2b0c18 100644
--- a/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/PolarChart.axaml.cs
+++ b/src/skiasharp/LiveChartsCore.SkiaSharp.Avalonia/PolarChart.axaml.cs
@@ -62,9 +62,10 @@ public class PolarChart : UserControl, IPolarChartView,
private MotionCanvas? _avaloniaCanvas;
private Chart? _core;
- private CollectionDeepObserver _seriesObserver;
- private CollectionDeepObserver _angleObserver;
- private CollectionDeepObserver _radiusObserver;
+ private readonly CollectionDeepObserver _seriesObserver;
+ private readonly CollectionDeepObserver _angleObserver;
+ private readonly CollectionDeepObserver _radiusObserver;
+ private readonly CollectionDeepObserver> _visualsObserver;
#endregion
@@ -95,6 +96,8 @@ public PolarChart()
_seriesObserver = new CollectionDeepObserver(OnDeepCollectionChanged, OnDeepCollectionPropertyChanged, true);
_angleObserver = new CollectionDeepObserver(OnDeepCollectionChanged, OnDeepCollectionPropertyChanged, true);
_radiusObserver = new CollectionDeepObserver(OnDeepCollectionChanged, OnDeepCollectionPropertyChanged, true);
+ _visualsObserver = new CollectionDeepObserver>(
+ OnDeepCollectionChanged, OnDeepCollectionPropertyChanged, true);
AngleAxes = new List()
{
@@ -105,6 +108,7 @@ public PolarChart()
LiveCharts.CurrentSettings.GetProvider().GetDefaultPolarAxis()
};
Series = new ObservableCollection();
+ VisualElements = new ObservableCollection>();
PointerWheelChanged += PolarChart_PointerWheelChanged;
PointerPressed += PolarChart_PointerPressed;
@@ -128,6 +132,13 @@ public PolarChart()
public static readonly AvaloniaProperty> SeriesProperty =
AvaloniaProperty.Register>(nameof(Series), Enumerable.Empty(), inherits: true);
+ ///
+ /// The visual elements property
+ ///
+ public static readonly AvaloniaProperty>> VisualElementsProperty =
+ AvaloniaProperty.Register>>(
+ nameof(VisualElements), Enumerable.Empty>(), inherits: true);
+
///
/// The fit to bounds property.
///
@@ -421,6 +432,13 @@ public IEnumerable RadiusAxes
set => SetValue(RadiusAxesProperty, value);
}
+ ///
+ public IEnumerable> VisualElements
+ {
+ get => (IEnumerable>)GetValue(VisualElementsProperty);
+ set => SetValue(VisualElementsProperty, value);
+ }
+
///
public TimeSpan AnimationsSpeed
{
@@ -760,6 +778,12 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs
_radiusObserver?.Initialize((IEnumerable)change.NewValue.Value);
}
+ if (change.Property.Name == nameof(VisualElements))
+ {
+ _visualsObserver?.Dispose((IEnumerable>)change.OldValue.Value);
+ _visualsObserver?.Initialize((IEnumerable>)change.NewValue.Value);
+ }
+
_core.Update();
}
diff --git a/src/skiasharp/LiveChartsCore.SkiaSharp.WPF/CartesianChart.cs b/src/skiasharp/LiveChartsCore.SkiaSharp.WPF/CartesianChart.cs
index 0c439c1ef..c59eefeaa 100644
--- a/src/skiasharp/LiveChartsCore.SkiaSharp.WPF/CartesianChart.cs
+++ b/src/skiasharp/LiveChartsCore.SkiaSharp.WPF/CartesianChart.cs
@@ -46,6 +46,7 @@ public class CartesianChart : Chart, ICartesianChartView _xObserver;
private readonly CollectionDeepObserver _yObserver;
private readonly CollectionDeepObserver> _sectionsObserver;
+ private readonly CollectionDeepObserver> _visualsObserver;
private readonly RectangleGeometry _zoomingSection = new();
#endregion
@@ -65,6 +66,8 @@ public CartesianChart()
_yObserver = new CollectionDeepObserver(OnDeepCollectionChanged, OnDeepCollectionPropertyChanged, true);
_sectionsObserver = new CollectionDeepObserver>(
OnDeepCollectionChanged, OnDeepCollectionPropertyChanged, true);
+ _visualsObserver = new CollectionDeepObserver>(
+ OnDeepCollectionChanged, OnDeepCollectionPropertyChanged, true);
SetCurrentValue(XAxesProperty, new ObservableCollection()
{
@@ -76,6 +79,7 @@ public CartesianChart()
});
SetCurrentValue(SeriesProperty, new ObservableCollection());
SetCurrentValue(SectionsProperty, new ObservableCollection>());
+ SetCurrentValue(VisualElementsProperty, new ObservableCollection>());
MouseWheel += OnMouseWheel;
MouseDown += OnMouseDown;
@@ -178,6 +182,28 @@ public CartesianChart()
: new List>();
}));
+ ///
+ /// The visual elements property
+ ///
+ public static readonly DependencyProperty VisualElementsProperty =
+ DependencyProperty.Register(
+ nameof(VisualElements), typeof(IEnumerable>), typeof(CartesianChart), new PropertyMetadata(null,
+ (DependencyObject o, DependencyPropertyChangedEventArgs args) =>
+ {
+ var chart = (CartesianChart)o;
+ var observer = chart._visualsObserver;
+ observer?.Dispose((IEnumerable>)args.OldValue);
+ observer?.Initialize((IEnumerable>)args.NewValue);
+ if (chart.core is null) return;
+ chart.core.Update();
+ },
+ (DependencyObject o, object value) =>
+ {
+ return value is IEnumerable>
+ ? value
+ : new List>();
+ }));
+
///
/// The zoom mode property
///
@@ -244,6 +270,13 @@ public IEnumerable> Sections
set => SetValue(SectionsProperty, value);
}
+ ///
+ public IEnumerable> VisualElements
+ {
+ get => (IEnumerable