diff --git a/src/Avalonia.Visuals/Media/EllipseGeometry.cs b/src/Avalonia.Visuals/Media/EllipseGeometry.cs
index 414fd9ab59..591b55cf58 100644
--- a/src/Avalonia.Visuals/Media/EllipseGeometry.cs
+++ b/src/Avalonia.Visuals/Media/EllipseGeometry.cs
@@ -11,16 +11,51 @@ namespace Avalonia.Media
///
public class EllipseGeometry : Geometry
{
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty RectProperty =
+ AvaloniaProperty.Register(nameof(Rect));
+
+ public Rect Rect
+ {
+ get => GetValue(RectProperty);
+ set => SetValue(RectProperty, value);
+ }
+
+ static EllipseGeometry()
+ {
+ RectProperty.Changed.AddClassHandler(x => x.RectChanged);
+ }
+
///
/// Initializes a new instance of the class.
///
- /// The rectangle that the ellipse should fill.
- public EllipseGeometry(Rect rect)
+ public EllipseGeometry()
{
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService();
- IStreamGeometryImpl impl = factory.CreateStreamGeometry();
+ PlatformImpl = factory.CreateStreamGeometry();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The rectangle that the ellipse should fill.
+ public EllipseGeometry(Rect rect) : this()
+ {
+ Rect = rect;
+ }
+
+ ///
+ public override Geometry Clone()
+ {
+ return new EllipseGeometry(Rect);
+ }
- using (IStreamGeometryContextImpl ctx = impl.Open())
+ private void RectChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ var rect = (Rect)e.NewValue;
+ using (var ctx = ((IStreamGeometryImpl)PlatformImpl).Open())
{
double controlPointRatio = (Math.Sqrt(2) - 1) * 4 / 3;
var center = rect.Center;
@@ -45,14 +80,6 @@ namespace Avalonia.Media
ctx.CubicBezierTo(new Point(x0, y1), new Point(x1, y0), new Point(x2, y0));
ctx.EndFigure(true);
}
-
- PlatformImpl = impl;
- }
-
- ///
- public override Geometry Clone()
- {
- return new EllipseGeometry(Bounds);
}
}
}
diff --git a/src/Avalonia.Visuals/Media/LineGeometry.cs b/src/Avalonia.Visuals/Media/LineGeometry.cs
index 2783d7fb26..323bfa5a7e 100644
--- a/src/Avalonia.Visuals/Media/LineGeometry.cs
+++ b/src/Avalonia.Visuals/Media/LineGeometry.cs
@@ -10,35 +10,89 @@ namespace Avalonia.Media
///
public class LineGeometry : Geometry
{
- private Point _startPoint;
- private Point _endPoint;
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty StartPointProperty =
+ AvaloniaProperty.Register(nameof(StartPoint));
+
+ public Point StartPoint
+ {
+ get => GetValue(StartPointProperty);
+ set => SetValue(StartPointProperty, value);
+ }
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty EndPointProperty =
+ AvaloniaProperty.Register(nameof(EndPoint));
+ private bool _isDirty;
+
+ public Point EndPoint
+ {
+ get => GetValue(EndPointProperty);
+ set => SetValue(EndPointProperty, value);
+ }
+
+ static LineGeometry()
+ {
+ StartPointProperty.Changed.AddClassHandler(x => x.PointsChanged);
+ EndPointProperty.Changed.AddClassHandler(x => x.PointsChanged);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LineGeometry()
+ {
+ IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService();
+ PlatformImpl = factory.CreateStreamGeometry();
+ }
///
/// Initializes a new instance of the class.
///
/// The start point.
/// The end point.
- public LineGeometry(Point startPoint, Point endPoint)
+ public LineGeometry(Point startPoint, Point endPoint) : this()
{
- _startPoint = startPoint;
- _endPoint = endPoint;
- IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService();
- IStreamGeometryImpl impl = factory.CreateStreamGeometry();
+ StartPoint = startPoint;
+ EndPoint = endPoint;
+ }
- using (IStreamGeometryContextImpl context = impl.Open())
+ public override IGeometryImpl PlatformImpl
+ {
+ get
{
- context.BeginFigure(_startPoint, false);
- context.LineTo(_endPoint);
- context.EndFigure(false);
+ PrepareIfNeeded();
+ return base.PlatformImpl;
}
+ protected set => base.PlatformImpl = value;
+ }
- PlatformImpl = impl;
+ public void PrepareIfNeeded()
+ {
+ if (_isDirty)
+ {
+ _isDirty = false;
+
+ using (var context = ((IStreamGeometryImpl)PlatformImpl).Open())
+ {
+ context.BeginFigure(StartPoint, false);
+ context.LineTo(EndPoint);
+ context.EndFigure(false);
+ }
+ }
}
///
public override Geometry Clone()
{
- return new LineGeometry(_startPoint, _endPoint);
+ PrepareIfNeeded();
+ return new LineGeometry(StartPoint, EndPoint);
}
+
+ private void PointsChanged(AvaloniaPropertyChangedEventArgs e) => _isDirty = true;
}
}
diff --git a/src/Avalonia.Visuals/Media/PolylineGeometry.cs b/src/Avalonia.Visuals/Media/PolylineGeometry.cs
index 709ad7a9f5..c8d7ac163c 100644
--- a/src/Avalonia.Visuals/Media/PolylineGeometry.cs
+++ b/src/Avalonia.Visuals/Media/PolylineGeometry.cs
@@ -3,10 +3,9 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using Avalonia.Platform;
+using Avalonia.Metadata;
+using Avalonia.Collections;
namespace Avalonia.Media
{
@@ -15,36 +14,122 @@ namespace Avalonia.Media
///
public class PolylineGeometry : Geometry
{
- private IList _points;
- private bool _isFilled;
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty PointsProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Points), g => g.Points, (g, f) => g.Points = f);
- public PolylineGeometry(IList points, bool isFilled)
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AvaloniaProperty IsFilledProperty =
+ AvaloniaProperty.Register(nameof(IsFilled));
+
+ static PolylineGeometry()
+ {
+ PointsProperty.Changed.Subscribe(onNext: v =>
+ {
+ (v.Sender as PolylineGeometry)?.OnPointsChanged(v.OldValue as Points, v.NewValue as Points);
+ });
+ IsFilledProperty.Changed.AddClassHandler(x => a => x.NotifyChanged());
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PolylineGeometry()
{
- _points = points;
- _isFilled = isFilled;
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService();
- IStreamGeometryImpl impl = factory.CreateStreamGeometry();
+ PlatformImpl = factory.CreateStreamGeometry();
+
+ Points = new Points();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PolylineGeometry(IEnumerable points, bool isFilled) : this()
+ {
+ Points.AddRange(points);
+ IsFilled = isFilled;
+
+ PrepareIfNeeded();
+ }
- using (IStreamGeometryContextImpl context = impl.Open())
+ public void PrepareIfNeeded()
+ {
+ if (_isDirty)
{
- if (points.Count > 0)
+ _isDirty = false;
+
+ using (var context = ((IStreamGeometryImpl)PlatformImpl).Open())
{
- context.BeginFigure(points[0], isFilled);
- for (int i = 1; i < points.Count; i++)
+ var points = Points;
+ var isFilled = IsFilled;
+ if (points.Count > 0)
{
- context.LineTo(points[i]);
+ context.BeginFigure(points[0], isFilled);
+ for (int i = 1; i < points.Count; i++)
+ {
+ context.LineTo(points[i]);
+ }
+ context.EndFigure(isFilled);
}
- context.EndFigure(isFilled);
}
}
+ }
- PlatformImpl = impl;
+ ///
+ /// Gets or sets the figures.
+ ///
+ ///
+ /// The points.
+ ///
+ [Content]
+ public Points Points
+ {
+ get => _points;
+ set => SetAndRaise(PointsProperty, ref _points, value);
}
+ public bool IsFilled
+ {
+ get => GetValue(IsFilledProperty);
+ set => SetValue(IsFilledProperty, value);
+ }
+
+ public override IGeometryImpl PlatformImpl
+ {
+ get
+ {
+ PrepareIfNeeded();
+ return base.PlatformImpl;
+ }
+ protected set => base.PlatformImpl = value;
+ }
+
+ private Points _points;
+ private bool _isDirty;
+ private IDisposable _pointsObserver;
+
///
public override Geometry Clone()
{
- return new PolylineGeometry(new List(_points), _isFilled);
+ PrepareIfNeeded();
+ return new PolylineGeometry(Points, IsFilled);
+ }
+
+ private void OnPointsChanged(Points oldValue, Points newValue)
+ {
+ _pointsObserver?.Dispose();
+
+ _pointsObserver = newValue?.ForEachItem(f => NotifyChanged(), f => NotifyChanged(), () => NotifyChanged());
+ }
+
+ internal void NotifyChanged()
+ {
+ _isDirty = true;
}
}
}
diff --git a/src/Avalonia.Visuals/Media/RectangleGeometry.cs b/src/Avalonia.Visuals/Media/RectangleGeometry.cs
index ef7deaa6f6..1aa449d9e1 100644
--- a/src/Avalonia.Visuals/Media/RectangleGeometry.cs
+++ b/src/Avalonia.Visuals/Media/RectangleGeometry.cs
@@ -10,16 +10,51 @@ namespace Avalonia.Media
///
public class RectangleGeometry : Geometry
{
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty RectProperty =
+ AvaloniaProperty.Register(nameof(Rect));
+
+ public Rect Rect
+ {
+ get => GetValue(RectProperty);
+ set => SetValue(RectProperty, value);
+ }
+
+ static RectangleGeometry()
+ {
+ RectProperty.Changed.AddClassHandler(x => x.RectChanged);
+ }
+
///
/// Initializes a new instance of the class.
///
- /// The rectangle bounds.
- public RectangleGeometry(Rect rect)
+ public RectangleGeometry()
{
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService();
- IStreamGeometryImpl impl = factory.CreateStreamGeometry();
+ PlatformImpl = factory.CreateStreamGeometry();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The rectangle bounds.
+ public RectangleGeometry(Rect rect) : this()
+ {
+ Rect = rect;
+ }
+
+ ///
+ public override Geometry Clone()
+ {
+ return new RectangleGeometry(Rect);
+ }
- using (IStreamGeometryContextImpl context = impl.Open())
+ private void RectChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ var rect = (Rect)e.NewValue;
+ using (var context = ((IStreamGeometryImpl)PlatformImpl).Open())
{
context.BeginFigure(rect.TopLeft, true);
context.LineTo(rect.TopRight);
@@ -27,14 +62,6 @@ namespace Avalonia.Media
context.LineTo(rect.BottomLeft);
context.EndFigure(true);
}
-
- PlatformImpl = impl;
- }
-
- ///
- public override Geometry Clone()
- {
- return new RectangleGeometry(Bounds);
}
}
}
diff --git a/src/Avalonia.Visuals/Points.cs b/src/Avalonia.Visuals/Points.cs
new file mode 100644
index 0000000000..867d3d4d24
--- /dev/null
+++ b/src/Avalonia.Visuals/Points.cs
@@ -0,0 +1,9 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using Avalonia.Collections;
+
+namespace Avalonia
+{
+ public sealed class Points : AvaloniaList { }
+}