diff --git a/maui/samples/Gallery/ControlList.xml b/maui/samples/Gallery/ControlList.xml index 4efbbd20..1b6ce05e 100644 --- a/maui/samples/Gallery/ControlList.xml +++ b/maui/samples/Gallery/ControlList.xml @@ -13,6 +13,7 @@ + @@ -28,5 +29,6 @@ + \ No newline at end of file diff --git a/maui/samples/Gallery/Resources/Images/gridsplitter.png b/maui/samples/Gallery/Resources/Images/gridsplitter.png new file mode 100644 index 00000000..01e0cb8e Binary files /dev/null and b/maui/samples/Gallery/Resources/Images/gridsplitter.png differ diff --git a/maui/samples/Gallery/Resources/Images/interactiveviewer.png b/maui/samples/Gallery/Resources/Images/interactiveviewer.png new file mode 100644 index 00000000..e31ece91 Binary files /dev/null and b/maui/samples/Gallery/Resources/Images/interactiveviewer.png differ diff --git a/maui/samples/Gallery/Resources/Images/interactiveviewerimage.png b/maui/samples/Gallery/Resources/Images/interactiveviewerimage.png new file mode 100644 index 00000000..a0485655 Binary files /dev/null and b/maui/samples/Gallery/Resources/Images/interactiveviewerimage.png differ diff --git a/maui/samples/Gallery/SampleList/CartesianChartSamplesList.xml b/maui/samples/Gallery/SampleList/CartesianChartSamplesList.xml index 88f89c0a..bd24dfad 100644 --- a/maui/samples/Gallery/SampleList/CartesianChartSamplesList.xml +++ b/maui/samples/Gallery/SampleList/CartesianChartSamplesList.xml @@ -1,7 +1,7 @@  - + @@ -172,7 +172,7 @@ - + @@ -183,7 +183,10 @@ - + + @@ -191,6 +194,8 @@ + + @@ -205,6 +210,7 @@ + diff --git a/maui/samples/Gallery/SampleList/FunnelChartSamplesList.xml b/maui/samples/Gallery/SampleList/FunnelChartSamplesList.xml index 1e229fbb..38ea4f88 100644 --- a/maui/samples/Gallery/SampleList/FunnelChartSamplesList.xml +++ b/maui/samples/Gallery/SampleList/FunnelChartSamplesList.xml @@ -1,8 +1,8 @@  - - + + diff --git a/maui/samples/Gallery/SampleList/GridSplitterSamplesList.xml b/maui/samples/Gallery/SampleList/GridSplitterSamplesList.xml new file mode 100644 index 00000000..be229579 --- /dev/null +++ b/maui/samples/Gallery/SampleList/GridSplitterSamplesList.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/maui/samples/Gallery/SampleList/InteractiveViewerSamplesList.xml b/maui/samples/Gallery/SampleList/InteractiveViewerSamplesList.xml new file mode 100644 index 00000000..079b057b --- /dev/null +++ b/maui/samples/Gallery/SampleList/InteractiveViewerSamplesList.xml @@ -0,0 +1,19 @@ + + + + + + + + + \ No newline at end of file diff --git a/maui/samples/Gallery/SampleList/PyramidChartSamplesList.xml b/maui/samples/Gallery/SampleList/PyramidChartSamplesList.xml index 94d218ab..4804b1ae 100644 --- a/maui/samples/Gallery/SampleList/PyramidChartSamplesList.xml +++ b/maui/samples/Gallery/SampleList/PyramidChartSamplesList.xml @@ -1,8 +1,8 @@  - - + + diff --git a/maui/samples/Gallery/SampleList/SparkChartSamplesList.xml b/maui/samples/Gallery/SampleList/SparkChartSamplesList.xml index 3e2b9e16..c0ebf0ac 100644 --- a/maui/samples/Gallery/SampleList/SparkChartSamplesList.xml +++ b/maui/samples/Gallery/SampleList/SparkChartSamplesList.xml @@ -1,7 +1,7 @@  - + diff --git a/maui/samples/Gallery/SampleList/SunburstChartSamplesList.xml b/maui/samples/Gallery/SampleList/SunburstChartSamplesList.xml index 3cdc0001..cb9fd113 100644 --- a/maui/samples/Gallery/SampleList/SunburstChartSamplesList.xml +++ b/maui/samples/Gallery/SampleList/SunburstChartSamplesList.xml @@ -1,13 +1,15 @@  - + + diff --git a/maui/samples/Gallery/Samples/Calendar/GettingStarted/View/GettingStarted.xaml b/maui/samples/Gallery/Samples/Calendar/GettingStarted/View/GettingStarted.xaml index 58b6560d..771e0845 100644 --- a/maui/samples/Gallery/Samples/Calendar/GettingStarted/View/GettingStarted.xaml +++ b/maui/samples/Gallery/Samples/Calendar/GettingStarted/View/GettingStarted.xaml @@ -124,7 +124,8 @@ VerticalTextAlignment="Center"/> + VerticalOptions="Center" + HorizontalOptions ="Start" /> diff --git a/maui/samples/Gallery/Samples/CartesianChart/Annotation/DraggableLineAnnotation.cs b/maui/samples/Gallery/Samples/CartesianChart/Annotation/DraggableLineAnnotation.cs new file mode 100644 index 00000000..f7081921 --- /dev/null +++ b/maui/samples/Gallery/Samples/CartesianChart/Annotation/DraggableLineAnnotation.cs @@ -0,0 +1,578 @@ +using Syncfusion.Maui.Toolkit.Charts; + +namespace Syncfusion.Maui.ControlsGallery.Samples.CartesianChart.Annotation +{ + /// + /// Helper struct to store plot area bounds in axis values. + /// + internal struct PlotAreaBounds + { + public double Left { get; set; } + public double Right { get; set; } + public double Top { get; set; } + public double Bottom { get; set; } + } + + /// + /// Helper class for draggable annotation utilities. + /// + internal static class DraggableAnnotationHelper + { + /// + /// Calculates plot area bounds in axis values. + /// + public static PlotAreaBounds GetPlotAreaBounds(Syncfusion.Maui.Toolkit.Charts.SfCartesianChart chart, ChartAxis xAxis, RangeAxisBase yAxis) + { + return new PlotAreaBounds + { + Left = chart.PointToValue(xAxis, (float)chart.SeriesBounds.Left, (float)chart.SeriesBounds.Top), + Right = chart.PointToValue(xAxis, (float)chart.SeriesBounds.Right, (float)chart.SeriesBounds.Top), + Top = chart.PointToValue(yAxis, (float)chart.SeriesBounds.Left, (float)chart.SeriesBounds.Top), + Bottom = chart.PointToValue(yAxis, (float)chart.SeriesBounds.Left, (float)chart.SeriesBounds.Bottom) + }; + } + + /// + /// Clamps annotation coordinates to plot area bounds. + /// + public static void ClampCoordinatesToBounds(ref double x1, ref double x2, ref double y1, ref double y2, PlotAreaBounds bounds) + { + // Clamp X positions + if (x1 < bounds.Left) + { + var offset = bounds.Left - x1; + x1 = bounds.Left; + x2 += offset; + } + if (x2 > bounds.Right) + { + var offset = x2 - bounds.Right; + x2 = bounds.Right; + x1 -= offset; + } + + // Clamp Y positions + if (y1 < bounds.Bottom) + { + var offset = bounds.Bottom - y1; + y1 = bounds.Bottom; + y2 += offset; + } + if (y2 > bounds.Top) + { + var offset = y2 - bounds.Top; + y2 = bounds.Top; + y1 -= offset; + } + } + + /// + /// Converts object value to double, handling multiple numeric types. + /// + public static double ToNumeric(object? value) + { + return value switch + { + double d => d, + float f => f, + int i => i, + long l => l, + string s => double.TryParse(s, out var result) ? result : 0d, + _ => 0d + }; + } + + /// + /// Clamps a value between visible min and max with an extend buffer. + /// + public static double ClampToVisibleRange(double value, double visibleMin, double visibleMax, double extend) + { + if (value <= visibleMin) + return visibleMin + extend; + if (value >= visibleMax) + return visibleMax - extend; + return value; + } + } + + public sealed class DraggableLineAnnotation : LineAnnotation + { + private Syncfusion.Maui.Toolkit.Charts.SfCartesianChart? _chart; + private ChartAxis? _xAxis; + private RangeAxisBase? _yAxis; + private bool _isDragging; + private double _previousAxisX; + private double _previousAxisY; + private bool _isDragEnabled = false; + + public void SetDragEnabled(bool enabled) + { + _isDragEnabled = enabled; + } + + public void Attach(Syncfusion.Maui.Toolkit.Charts.SfCartesianChart chart) + { + _chart = chart; + _xAxis = chart.XAxes.Count > 0 ? chart.XAxes[0] : null; + _yAxis = chart.YAxes.Count > 0 ? chart.YAxes[0] : null; + } + + protected override void OnTouchDown(float pointX, float pointY) + { + if (_chart is null || _xAxis is null || _yAxis is null) + { + return; + } + + var axisX = _chart.PointToValue(_xAxis, pointX, pointY); + var axisY = _chart.PointToValue(_yAxis, pointX, pointY); + + if (double.IsNaN(axisX) || double.IsInfinity(axisX) || + double.IsNaN(axisY) || double.IsInfinity(axisY)) + { + return; + } + + _previousAxisX = axisX; + _previousAxisY = axisY; + _isDragging = _isDragEnabled; + } + + protected override void OnTouchMove(float pointX, float pointY) + { + if (!_isDragging || _chart is null || _xAxis is null || _yAxis is null) + { + return; + } + + var axisX = _chart.PointToValue(_xAxis, pointX, pointY); + var axisY = _chart.PointToValue(_yAxis, pointX, pointY); + + if (double.IsNaN(axisX) || double.IsInfinity(axisX) || + double.IsNaN(axisY) || double.IsInfinity(axisY)) + { + return; + } + + var deltaX = axisX - _previousAxisX; + var deltaY = axisY - _previousAxisY; + + ApplyDelta(deltaX, deltaY); + _previousAxisX = axisX; + _previousAxisY = axisY; + } + + protected override void OnTouchUp(float pointX, float pointY) + { + _isDragging = false; + } + + private void ApplyDelta(double deltaX, double deltaY) + { + if (_chart is null || _xAxis is null || _yAxis is null) + { + return; + } + + var newX1 = DraggableAnnotationHelper.ToNumeric(X1) + deltaX; + var newX2 = DraggableAnnotationHelper.ToNumeric(X2) + deltaX; + var newY1 = Y1 + deltaY; + var newY2 = Y2 + deltaY; + + var bounds = DraggableAnnotationHelper.GetPlotAreaBounds(_chart, _xAxis, _yAxis); + DraggableAnnotationHelper.ClampCoordinatesToBounds(ref newX1, ref newX2, ref newY1, ref newY2, bounds); + + X1 = newX1; + X2 = newX2; + Y1 = newY1; + Y2 = newY2; + } + } + + public sealed class DraggableVerticalLineAnnotation : VerticalLineAnnotation + { + private Syncfusion.Maui.Toolkit.Charts.SfCartesianChart? _chart; + private ChartAxis? _xAxis; + private bool _isDragging; + private bool _isDragEnabled = false; + + public void SetDragEnabled(bool enabled) + { + _isDragEnabled = enabled; + } + + public void Attach(Syncfusion.Maui.Toolkit.Charts.SfCartesianChart chart) + { + _chart = chart; + _xAxis = chart.XAxes.Count > 0 ? chart.XAxes[0] : null; + } + + protected override void OnTouchDown(float pointX, float pointY) + { + if (_chart is null || _xAxis is null) + { + return; + } + + var linePixel = _xAxis.ValueToPoint(Convert.ToDouble(X1)); + if (double.IsNaN(linePixel)) + { + return; + } + + _isDragging = _isDragEnabled; + } + + protected override void OnTouchMove(float pointX, float pointY) + { + if (!_isDragging || _chart is null || _xAxis is null) + { + return; + } + + var newValue = _chart.PointToValue(_xAxis, pointX, pointY); + if (double.IsNaN(newValue) || double.IsInfinity(newValue)) + { + return; + } + + X1 = DraggableAnnotationHelper.ClampToVisibleRange(newValue, _xAxis.VisibleMinimum, _xAxis.VisibleMaximum, 0.1); + } + + protected override void OnTouchUp(float pointX, float pointY) + { + _isDragging = false; + } + } + + public sealed class DraggableHorizontalLineAnnotation : HorizontalLineAnnotation + { + private Syncfusion.Maui.Toolkit.Charts.SfCartesianChart? _chart; + private RangeAxisBase? _yAxis; + private bool _isDragging; + private bool _isDragEnabled = false; + + public void SetDragEnabled(bool enabled) + { + _isDragEnabled = enabled; + } + + public void Attach(Syncfusion.Maui.Toolkit.Charts.SfCartesianChart chart) + { + _chart = chart; + _yAxis = chart.YAxes.Count > 0 ? chart.YAxes[0] : null; + } + + protected override void OnTouchDown(float pointX, float pointY) + { + if (_chart is null || _yAxis is null) + { + return; + } + + var linePixel = _yAxis.ValueToPoint(Y1); + if (double.IsNaN(linePixel)) + { + return; + } + + _isDragging = _isDragEnabled; + } + + protected override void OnTouchMove(float pointX, float pointY) + { + if (!_isDragging || _chart is null || _yAxis is null) + { + return; + } + + var newValue = _chart.PointToValue(_yAxis, pointX, pointY); + if (double.IsNaN(newValue) || double.IsInfinity(newValue)) + { + return; + } + + Y1 = DraggableAnnotationHelper.ClampToVisibleRange(newValue, _yAxis.VisibleMinimum, _yAxis.VisibleMaximum, 2); + } + + protected override void OnTouchUp(float pointX, float pointY) + { + _isDragging = false; + } + } + + public sealed class DraggableRectangleAnnotation : RectangleAnnotation + { + private Syncfusion.Maui.Toolkit.Charts.SfCartesianChart? _chart; + private ChartAxis? _xAxis; + private RangeAxisBase? _yAxis; + private bool _isDragging; + private double _previousAxisX; + private double _previousAxisY; + private bool _isDragEnabled = false; + + public void SetDragEnabled(bool enabled) + { + _isDragEnabled = enabled; + } + + public void Attach(Syncfusion.Maui.Toolkit.Charts.SfCartesianChart chart) + { + _chart = chart; + _xAxis = chart.XAxes.Count > 0 ? chart.XAxes[0] : null; + _yAxis = chart.YAxes.Count > 0 ? chart.YAxes[0] : null; + } + + protected override void OnTouchDown(float pointX, float pointY) + { + if (_chart is null || _xAxis is null || _yAxis is null) + { + return; + } + + var axisX = _chart.PointToValue(_xAxis, pointX, pointY); + var axisY = _chart.PointToValue(_yAxis, pointX, pointY); + + if (double.IsNaN(axisX) || double.IsInfinity(axisX) || + double.IsNaN(axisY) || double.IsInfinity(axisY)) + { + return; + } + + _previousAxisX = axisX; + _previousAxisY = axisY; + _isDragging = _isDragEnabled; + } + + protected override void OnTouchMove(float pointX, float pointY) + { + if (!_isDragging || _chart is null || _xAxis is null || _yAxis is null) + { + return; + } + + var axisX = _chart.PointToValue(_xAxis, pointX, pointY); + var axisY = _chart.PointToValue(_yAxis, pointX, pointY); + + if (double.IsNaN(axisX) || double.IsInfinity(axisX) || + double.IsNaN(axisY) || double.IsInfinity(axisY)) + { + return; + } + + var deltaX = axisX - _previousAxisX; + var deltaY = axisY - _previousAxisY; + + ApplyDelta(deltaX, deltaY); + _previousAxisX = axisX; + _previousAxisY = axisY; + } + + protected override void OnTouchUp(float pointX, float pointY) + { + _isDragging = false; + } + + private void ApplyDelta(double deltaX, double deltaY) + { + if (_chart is null || _xAxis is null || _yAxis is null) + { + return; + } + + var newX1 = DraggableAnnotationHelper.ToNumeric(X1) + deltaX; + var newX2 = DraggableAnnotationHelper.ToNumeric(X2) + deltaX; + var newY1 = Y1 + deltaY; + var newY2 = Y2 + deltaY; + + var bounds = DraggableAnnotationHelper.GetPlotAreaBounds(_chart, _xAxis, _yAxis); + DraggableAnnotationHelper.ClampCoordinatesToBounds(ref newX1, ref newX2, ref newY1, ref newY2, bounds); + + X1 = newX1; + X2 = newX2; + Y1 = newY1; + Y2 = newY2; + } + } + + public sealed class DraggableEllipseAnnotation : EllipseAnnotation + { + private Syncfusion.Maui.Toolkit.Charts.SfCartesianChart? _chart; + private ChartAxis? _xAxis; + private RangeAxisBase? _yAxis; + private bool _isDragging; + private double _previousAxisX; + private double _previousAxisY; + private bool _isDragEnabled = false; + + public void SetDragEnabled(bool enabled) + { + _isDragEnabled = enabled; + } + + public void Attach(Syncfusion.Maui.Toolkit.Charts.SfCartesianChart chart) + { + _chart = chart; + _xAxis = chart.XAxes.Count > 0 ? chart.XAxes[0] : null; + _yAxis = chart.YAxes.Count > 0 ? chart.YAxes[0] : null; + } + + protected override void OnTouchDown(float pointX, float pointY) + { + if (_chart is null || _xAxis is null || _yAxis is null) + { + return; + } + + var axisX = _chart.PointToValue(_xAxis, pointX, pointY); + var axisY = _chart.PointToValue(_yAxis, pointX, pointY); + + if (double.IsNaN(axisX) || double.IsInfinity(axisX) || + double.IsNaN(axisY) || double.IsInfinity(axisY)) + { + return; + } + + _previousAxisX = axisX; + _previousAxisY = axisY; + _isDragging = _isDragEnabled; + } + + protected override void OnTouchMove(float pointX, float pointY) + { + if (!_isDragging || _chart is null || _xAxis is null || _yAxis is null) + { + return; + } + + var axisX = _chart.PointToValue(_xAxis, pointX, pointY); + var axisY = _chart.PointToValue(_yAxis, pointX, pointY); + + if (double.IsNaN(axisX) || double.IsInfinity(axisX) || + double.IsNaN(axisY) || double.IsInfinity(axisY)) + { + return; + } + + var deltaX = axisX - _previousAxisX; + var deltaY = axisY - _previousAxisY; + + ApplyDelta(deltaX, deltaY); + _previousAxisX = axisX; + _previousAxisY = axisY; + } + + protected override void OnTouchUp(float pointX, float pointY) + { + _isDragging = false; + } + + private void ApplyDelta(double deltaX, double deltaY) + { + if (_chart is null || _xAxis is null || _yAxis is null) + { + return; + } + + var newX1 = DraggableAnnotationHelper.ToNumeric(X1) + deltaX; + var newX2 = DraggableAnnotationHelper.ToNumeric(X2) + deltaX; + var newY1 = Y1 + deltaY; + var newY2 = Y2 + deltaY; + + var bounds = DraggableAnnotationHelper.GetPlotAreaBounds(_chart, _xAxis, _yAxis); + DraggableAnnotationHelper.ClampCoordinatesToBounds(ref newX1, ref newX2, ref newY1, ref newY2, bounds); + + X1 = newX1; + X2 = newX2; + Y1 = newY1; + Y2 = newY2; + } + } + + public sealed class DraggableTextAnnotation : TextAnnotation + { + private Syncfusion.Maui.Toolkit.Charts.SfCartesianChart? _chart; + private ChartAxis? _xAxis; + private RangeAxisBase? _yAxis; + private bool _isDragging; + private double _previousAxisX; + private double _previousAxisY; + private bool _isDragEnabled = false; + + public void SetDragEnabled(bool enabled) + { + _isDragEnabled = enabled; + } + + public void Attach(Syncfusion.Maui.Toolkit.Charts.SfCartesianChart chart) + { + _chart = chart; + _xAxis = chart.XAxes.Count > 0 ? chart.XAxes[0] : null; + _yAxis = chart.YAxes.Count > 0 ? chart.YAxes[0] : null; + } + + protected override void OnTouchDown(float pointX, float pointY) + { + if (_chart is null || _xAxis is null || _yAxis is null) + { + return; + } + + var axisX = _chart.PointToValue(_xAxis, pointX, pointY); + var axisY = _chart.PointToValue(_yAxis, pointX, pointY); + + if (double.IsNaN(axisX) || double.IsInfinity(axisX) || + double.IsNaN(axisY) || double.IsInfinity(axisY)) + { + return; + } + + _previousAxisX = axisX; + _previousAxisY = axisY; + _isDragging = _isDragEnabled; + } + + protected override void OnTouchMove(float pointX, float pointY) + { + if (!_isDragging || _chart is null || _xAxis is null || _yAxis is null) + { + return; + } + + var axisX = _chart.PointToValue(_xAxis, pointX, pointY); + var axisY = _chart.PointToValue(_yAxis, pointX, pointY); + + if (double.IsNaN(axisX) || double.IsInfinity(axisX) || + double.IsNaN(axisY) || double.IsInfinity(axisY)) + { + return; + } + + var deltaX = axisX - _previousAxisX; + var deltaY = axisY - _previousAxisY; + + ApplyDelta(deltaX, deltaY); + _previousAxisX = axisX; + _previousAxisY = axisY; + } + + protected override void OnTouchUp(float pointX, float pointY) + { + _isDragging = false; + } + + private void ApplyDelta(double deltaX, double deltaY) + { + if (_chart is null || _xAxis is null || _yAxis is null) + { + return; + } + + double newX1 = DraggableAnnotationHelper.ToNumeric(X1) + deltaX; + double newY1 = Y1 + deltaY; + X1 = DraggableAnnotationHelper.ClampToVisibleRange(newX1, _xAxis.VisibleMinimum, _xAxis.VisibleMaximum, 0.3); + Y1 = DraggableAnnotationHelper.ClampToVisibleRange(newY1, _yAxis.VisibleMinimum, _yAxis.VisibleMaximum, 0.5); + } + } +} + diff --git a/maui/samples/Gallery/Samples/CartesianChart/Annotation/ShapeAnnotationSample.xaml b/maui/samples/Gallery/Samples/CartesianChart/Annotation/ShapeAnnotationSample.xaml index 457b020e..55369519 100644 --- a/maui/samples/Gallery/Samples/CartesianChart/Annotation/ShapeAnnotationSample.xaml +++ b/maui/samples/Gallery/Samples/CartesianChart/Annotation/ShapeAnnotationSample.xaml @@ -3,50 +3,215 @@ x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.ShapeAnnotationSample" xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart" xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery" - xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit" + xmlns:annotation="clr-namespace:Syncfusion.Maui.ControlsGallery.Samples.CartesianChart.Annotation"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +