diff --git a/src/Avalonia.Visuals/Media/BrushExtensions.cs b/src/Avalonia.Visuals/Media/BrushExtensions.cs
index 081029e205..265337476a 100644
--- a/src/Avalonia.Visuals/Media/BrushExtensions.cs
+++ b/src/Avalonia.Visuals/Media/BrushExtensions.cs
@@ -24,12 +24,27 @@ namespace Avalonia.Media
}
///
- /// Converts a pen to a pen with an immutable brush
+ /// Converts a dash style to an immutable dash style.
+ ///
+ /// The dash style.
+ ///
+ /// The result of calling if the style is mutable,
+ /// otherwise .
+ ///
+ public static ImmutableDashStyle ToImmutable(this IDashStyle style)
+ {
+ Contract.Requires(style != null);
+
+ return style as ImmutableDashStyle ?? ((DashStyle)style).ToImmutable();
+ }
+
+ ///
+ /// Converts a pen to an immutable pen.
///
/// The pen.
///
- /// A copy of the pen with an immutable brush, or if the pen's brush
- /// is already immutable or null.
+ /// The result of calling if the brush is mutable,
+ /// otherwise .
///
public static ImmutablePen ToImmutable(this IPen pen)
{
diff --git a/src/Avalonia.Visuals/Media/DashStyle.cs b/src/Avalonia.Visuals/Media/DashStyle.cs
index 9f03a0bd85..7784c73736 100644
--- a/src/Avalonia.Visuals/Media/DashStyle.cs
+++ b/src/Avalonia.Visuals/Media/DashStyle.cs
@@ -1,72 +1,114 @@
namespace Avalonia.Media
{
+ using System;
using System.Collections.Generic;
+ using System.Linq;
using Avalonia.Animation;
+ using Avalonia.Media.Immutable;
- public class DashStyle : Animatable
+ ///
+ /// Represents the sequence of dashes and gaps that will be applied by a .
+ ///
+ public class DashStyle : Animatable, IDashStyle, IAffectsRender
{
- private static DashStyle dash;
- public static DashStyle Dash
- {
- get
- {
- if (dash == null)
- {
- dash = new DashStyle(new double[] { 2, 2 }, 1);
- }
-
- return dash;
- }
- }
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AvaloniaProperty> DashesProperty =
+ AvaloniaProperty.Register>(nameof(Dashes));
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AvaloniaProperty OffsetProperty =
+ AvaloniaProperty.Register(nameof(Offset));
+ private static ImmutableDashStyle s_dash;
+ private static ImmutableDashStyle s_dot;
+ private static ImmutableDashStyle s_dashDot;
+ private static ImmutableDashStyle s_dashDotDot;
- private static DashStyle dot;
- public static DashStyle Dot
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DashStyle()
+ : this(null, 0)
{
- get { return dot ?? (dot = new DashStyle(new double[] {0, 2}, 0)); }
}
- private static DashStyle dashDot;
- public static DashStyle DashDot
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The dashes collection.
+ /// The dash sequence offset.
+ public DashStyle(IEnumerable dashes, double offset)
{
- get
- {
- if (dashDot == null)
- {
- dashDot = new DashStyle(new double[] { 2, 2, 0, 2 }, 1);
- }
-
- return dashDot;
- }
+ Dashes = (IReadOnlyList)dashes?.ToList() ?? Array.Empty();
+ Offset = offset;
}
- private static DashStyle dashDotDot;
- public static DashStyle DashDotDot
+ static DashStyle()
{
- get
+ void RaiseInvalidated(AvaloniaPropertyChangedEventArgs e)
{
- if (dashDotDot == null)
- {
- dashDotDot = new DashStyle(new double[] { 2, 2, 0, 2, 0, 2 }, 1);
- }
-
- return dashDotDot;
+ ((DashStyle)e.Sender).Invalidated?.Invoke(e.Sender, EventArgs.Empty);
}
+
+ DashesProperty.Changed.Subscribe(RaiseInvalidated);
+ OffsetProperty.Changed.Subscribe(RaiseInvalidated);
}
+ ///
+ /// Represents a dashed .
+ ///
+ public static IDashStyle Dash =>
+ s_dash ?? (s_dash = new ImmutableDashStyle(new double[] { 2, 2 }, 1));
+
+ ///
+ /// Represents a dotted .
+ ///
+ public static IDashStyle Dot =>
+ s_dot ?? (s_dot = new ImmutableDashStyle(new double[] { 0, 2 }, 0));
+
+ ///
+ /// Represents a dashed dotted .
+ ///
+ public static IDashStyle DashDot =>
+ s_dashDot ?? (s_dashDot = new ImmutableDashStyle(new double[] { 2, 2, 0, 2 }, 1));
+
+ ///
+ /// Represents a dashed double dotted .
+ ///
+ public static IDashStyle DashDotDot =>
+ s_dashDotDot ?? (s_dashDotDot = new ImmutableDashStyle(new double[] { 2, 2, 0, 2, 0, 2 }, 1));
- public DashStyle(IReadOnlyList dashes = null, double offset = 0.0)
+ ///
+ /// Gets or sets the length of alternating dashes and gaps.
+ ///
+ public IReadOnlyList Dashes
{
- this.Dashes = dashes;
- this.Offset = offset;
+ get => GetValue(DashesProperty);
+ set => SetValue(DashesProperty, value);
}
///
- /// Gets and sets the length of alternating dashes and gaps.
+ /// Gets or sets how far in the dash sequence the stroke will start.
///
- public IReadOnlyList Dashes { get; }
+ public double Offset
+ {
+ get => GetValue(OffsetProperty);
+ set => SetValue(OffsetProperty, value);
+ }
- public double Offset { get; }
+ ///
+ /// Raised when the dash style changes.
+ ///
+ public event EventHandler Invalidated;
+
+ ///
+ /// Returns an immutable clone of the .
+ ///
+ ///
+ public ImmutableDashStyle ToImmutable() => new ImmutableDashStyle(Dashes, Offset);
}
}
diff --git a/src/Avalonia.Visuals/Media/IDashStyle.cs b/src/Avalonia.Visuals/Media/IDashStyle.cs
new file mode 100644
index 0000000000..7835c7a1e9
--- /dev/null
+++ b/src/Avalonia.Visuals/Media/IDashStyle.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+
+namespace Avalonia.Media
+{
+ ///
+ /// Represents the sequence of dashes and gaps that will be applied by a .
+ ///
+ public interface IDashStyle
+ {
+ ///
+ /// Gets or sets the length of alternating dashes and gaps.
+ ///
+ IReadOnlyList Dashes { get; }
+
+ ///
+ /// Gets or sets how far in the dash sequence the stroke will start.
+ ///
+ double Offset { get; }
+ }
+}
diff --git a/src/Avalonia.Visuals/Media/IPen.cs b/src/Avalonia.Visuals/Media/IPen.cs
index 589595bb5c..0cdac312cc 100644
--- a/src/Avalonia.Visuals/Media/IPen.cs
+++ b/src/Avalonia.Visuals/Media/IPen.cs
@@ -13,7 +13,7 @@
///
/// Gets the style of dashed lines drawn with a object.
///
- DashStyle DashStyle { get; }
+ IDashStyle DashStyle { get; }
///
/// Gets the type of shape to use on both ends of a line.
diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs
new file mode 100644
index 0000000000..a40682babd
--- /dev/null
+++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Avalonia.Media.Immutable
+{
+ ///
+ /// Represents the sequence of dashes and gaps that will be applied by an
+ /// .
+ ///
+ public class ImmutableDashStyle : IDashStyle
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The dashes collection.
+ /// The dash sequence offset.
+ public ImmutableDashStyle(IEnumerable dashes, double offset)
+ {
+ Dashes = (IReadOnlyList)dashes?.ToList() ?? Array.Empty();
+ Offset = offset;
+ }
+
+ ///
+ public IReadOnlyList Dashes { get; }
+
+ ///
+ public double Offset { get; }
+ }
+}
diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs
index 326083dec1..ddc050aa5c 100644
--- a/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs
+++ b/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs
@@ -22,7 +22,7 @@ namespace Avalonia.Media.Immutable
public ImmutablePen(
uint color,
double thickness = 1.0,
- DashStyle dashStyle = null,
+ ImmutableDashStyle dashStyle = null,
PenLineCap lineCap = PenLineCap.Flat,
PenLineJoin lineJoin = PenLineJoin.Miter,
double miterLimit = 10.0) : this(new SolidColorBrush(color), thickness, dashStyle, lineCap, lineJoin, miterLimit)
@@ -41,7 +41,7 @@ namespace Avalonia.Media.Immutable
public ImmutablePen(
IBrush brush,
double thickness = 1.0,
- DashStyle dashStyle = null,
+ ImmutableDashStyle dashStyle = null,
PenLineCap lineCap = PenLineCap.Flat,
PenLineJoin lineJoin = PenLineJoin.Miter,
double miterLimit = 10.0)
@@ -67,7 +67,7 @@ namespace Avalonia.Media.Immutable
///
/// Specifies the style of dashed lines drawn with a object.
///
- public DashStyle DashStyle { get; }
+ public IDashStyle DashStyle { get; }
///
/// Specifies the type of graphic shape to use on both ends of a line.
diff --git a/src/Avalonia.Visuals/Media/Pen.cs b/src/Avalonia.Visuals/Media/Pen.cs
index b89ca15a0e..c0963adc08 100644
--- a/src/Avalonia.Visuals/Media/Pen.cs
+++ b/src/Avalonia.Visuals/Media/Pen.cs
@@ -28,8 +28,8 @@ namespace Avalonia.Media
///
/// Defines the property.
///
- public static readonly StyledProperty DashStyleProperty =
- AvaloniaProperty.Register(nameof(DashStyle));
+ public static readonly StyledProperty DashStyleProperty =
+ AvaloniaProperty.Register(nameof(DashStyle));
///
/// Defines the property.
@@ -68,7 +68,7 @@ namespace Avalonia.Media
public Pen(
uint color,
double thickness = 1.0,
- DashStyle dashStyle = null,
+ IDashStyle dashStyle = null,
PenLineCap lineCap = PenLineCap.Flat,
PenLineJoin lineJoin = PenLineJoin.Miter,
double miterLimit = 10.0) : this(new SolidColorBrush(color), thickness, dashStyle, lineCap, lineJoin, miterLimit)
@@ -87,7 +87,7 @@ namespace Avalonia.Media
public Pen(
IBrush brush,
double thickness = 1.0,
- DashStyle dashStyle = null,
+ IDashStyle dashStyle = null,
PenLineCap lineCap = PenLineCap.Flat,
PenLineJoin lineJoin = PenLineJoin.Miter,
double miterLimit = 10.0)
@@ -132,7 +132,7 @@ namespace Avalonia.Media
///
/// Gets or sets the style of dashed lines drawn with a object.
///
- public DashStyle DashStyle
+ public IDashStyle DashStyle
{
get => GetValue(DashStyleProperty);
set => SetValue(DashStyleProperty, value);
@@ -189,7 +189,7 @@ namespace Avalonia.Media
return new ImmutablePen(
Brush?.ToImmutable(),
Thickness,
- DashStyle,
+ DashStyle?.ToImmutable(),
LineCap,
LineJoin,
MiterLimit);
@@ -247,7 +247,7 @@ namespace Avalonia.Media
var hashCode = 1181807663;
hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(pen.Brush);
hashCode = hashCode * -1521134295 + pen.Thickness.GetHashCode();
- hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(pen.DashStyle);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(pen.DashStyle);
hashCode = hashCode * -1521134295 + pen.LineCap.GetHashCode();
hashCode = hashCode * -1521134295 + pen.LineJoin.GetHashCode();
hashCode = hashCode * -1521134295 + pen.MiterLimit.GetHashCode();
@@ -267,7 +267,7 @@ namespace Avalonia.Media
return EqualityComparer.Default.Equals(a.Brush, b.Brush) &&
a.Thickness == b.Thickness &&
- EqualityComparer.Default.Equals(a.DashStyle, b.DashStyle) &&
+ EqualityComparer.Default.Equals(a.DashStyle, b.DashStyle) &&
a.LineCap == b.LineCap &&
a.LineJoin == b.LineJoin &&
a.MiterLimit == b.MiterLimit;
diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs
index c4fe0155f9..70b4281083 100644
--- a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs
@@ -31,6 +31,19 @@ namespace Avalonia.Visuals.UnitTests.Media
Assert.True(raised);
}
+ [Fact]
+ public void Changing_DashStyle_Dashes_Raises_Invalidated()
+ {
+ var dashes = new DashStyle();
+ var target = new Pen { DashStyle = dashes };
+ var raised = false;
+
+ target.Invalidated += (s, e) => raised = true;
+ dashes.Dashes = new[] { 0.1, 0.2 };
+
+ Assert.True(raised);
+ }
+
[Fact]
public void Equality_Is_Implemented_Between_Mutable_And_Immutable_Pens()
{
@@ -45,7 +58,7 @@ namespace Avalonia.Visuals.UnitTests.Media
var target2 = new ImmutablePen(
brush: brush,
thickness: 2,
- dashStyle: DashStyle.Dash,
+ dashStyle: (ImmutableDashStyle)DashStyle.Dash,
lineCap: PenLineCap.Round,
lineJoin: PenLineJoin.Round,
miterLimit: 21);