diff --git a/.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject b/.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Direct3DInteropSample.v3.ncrunchproject b/.ncrunch/Direct3DInteropSample.v3.ncrunchproject
new file mode 100644
index 0000000000..6800b4a3fe
--- /dev/null
+++ b/.ncrunch/Direct3DInteropSample.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props
index bd6b4ebc53..a4b94cacfa 100644
--- a/build/SkiaSharp.props
+++ b/build/SkiaSharp.props
@@ -1,5 +1,5 @@
-
+
diff --git a/docs/guidelines/build.md b/docs/guidelines/build.md
index 12047ff357..822496814b 100644
--- a/docs/guidelines/build.md
+++ b/docs/guidelines/build.md
@@ -2,7 +2,7 @@
## Windows
-Avalonia requires at least Visual Studio 2015 to build on Windows.
+Avalonia requires at least Visual Studio 2017 to build on Windows.
### Install GTK Sharp
diff --git a/packages.cake b/packages.cake
index f030c73a06..2fb089ba96 100644
--- a/packages.cake
+++ b/packages.cake
@@ -425,10 +425,7 @@ public class Packages
{
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version },
new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion },
- //netstandard1.3
- new NuSpecDependency() { Id = "Avalonia", TargetFramework = "netstandard1.3", Version = parameters.Version },
- new NuSpecDependency() { Id = "SkiaSharp", TargetFramework = "netstandard1.3", Version = SkiaSharpVersion },
- new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netstandard1.3", Version = "1.6.0" }
+ new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpVersion }
},
Files = new []
{
@@ -446,11 +443,17 @@ public class Packages
Id = "Avalonia.Desktop",
Dependencies = new []
{
- new NuSpecDependency() { Id = "Avalonia.Win32", Version = parameters.Version },
- new NuSpecDependency() { Id = "Avalonia.Direct2D1", Version = parameters.Version },
- new NuSpecDependency() { Id = "Avalonia.Gtk", Version = parameters.Version },
- new NuSpecDependency() { Id = "Avalonia.Cairo", Version = parameters.Version },
- new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", Version = parameters.Version }
+ //Full .NET
+ new NuSpecDependency() { Id = "Avalonia.Direct2D1", TargetFramework="net45", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Gtk", TargetFramework="net45", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Cairo", TargetFramework="net45", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="net45", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="net45", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="net45", Version = parameters.Version },
+ //.NET Core
+ new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.1", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="netcoreapp1.1", Version = parameters.Version },
+ new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.1", Version = parameters.Version }
},
Files = new NuSpecContent[]
{
diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 7359dceae1..1492c14c5f 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -578,13 +578,13 @@ namespace Avalonia
if (notification == null)
{
- return TypeUtilities.CastOrDefault(value, type);
+ return TypeUtilities.ConvertImplicitOrDefault(value, type);
}
else
{
if (notification.HasValue)
{
- notification.SetValue(TypeUtilities.CastOrDefault(notification.Value, type));
+ notification.SetValue(TypeUtilities.ConvertImplicitOrDefault(notification.Value, type));
}
return notification;
@@ -735,7 +735,7 @@ namespace Avalonia
ThrowNotRegistered(property);
}
- if (!TypeUtilities.TryCast(property.PropertyType, value, out value))
+ if (!TypeUtilities.TryConvertImplicit(property.PropertyType, value, out value))
{
throw new ArgumentException(string.Format(
"Invalid value for Property '{0}': '{1}' ({2})",
diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs
index 61006b1173..fb78e3b2a0 100644
--- a/src/Avalonia.Base/AvaloniaProperty.cs
+++ b/src/Avalonia.Base/AvaloniaProperty.cs
@@ -476,7 +476,7 @@ namespace Avalonia
/// True if the value is valid, otherwise false.
public bool IsValidValue(object value)
{
- return TypeUtilities.TryCast(PropertyType, value, out value);
+ return TypeUtilities.TryConvertImplicit(PropertyType, value, out value);
}
///
diff --git a/src/Avalonia.Base/PriorityValue.cs b/src/Avalonia.Base/PriorityValue.cs
index 57e2854014..21467f3962 100644
--- a/src/Avalonia.Base/PriorityValue.cs
+++ b/src/Avalonia.Base/PriorityValue.cs
@@ -249,7 +249,7 @@ namespace Avalonia
value = (notification.HasValue) ? notification.Value : null;
}
- if (TypeUtilities.TryCast(_valueType, value, out castValue))
+ if (TypeUtilities.TryConvertImplicit(_valueType, value, out castValue))
{
var old = _value;
diff --git a/src/Avalonia.Base/Utilities/TypeUtilities.cs b/src/Avalonia.Base/Utilities/TypeUtilities.cs
index 7295bfa7ab..dd93811498 100644
--- a/src/Avalonia.Base/Utilities/TypeUtilities.cs
+++ b/src/Avalonia.Base/Utilities/TypeUtilities.cs
@@ -14,17 +14,61 @@ namespace Avalonia.Utilities
///
public static class TypeUtilities
{
- private static readonly Dictionary> Conversions = new Dictionary>()
+ private static int[] Conversions =
{
- { typeof(decimal), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char) } },
- { typeof(double), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
- { typeof(float), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
- { typeof(ulong), new List { typeof(byte), typeof(ushort), typeof(uint), typeof(char) } },
- { typeof(long), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char) } },
- { typeof(uint), new List { typeof(byte), typeof(ushort), typeof(char) } },
- { typeof(int), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char) } },
- { typeof(ushort), new List { typeof(byte), typeof(char) } },
- { typeof(short), new List { typeof(byte) } }
+ 0b101111111111101, // Boolean
+ 0b100001111111110, // Char
+ 0b101111111111111, // SByte
+ 0b101111111111111, // Byte
+ 0b101111111111111, // Int16
+ 0b101111111111111, // UInt16
+ 0b101111111111111, // Int32
+ 0b101111111111111, // UInt32
+ 0b101111111111111, // Int64
+ 0b101111111111111, // UInt64
+ 0b101111111111101, // Single
+ 0b101111111111101, // Double
+ 0b101111111111101, // Decimal
+ 0b110000000000000, // DateTime
+ 0b111111111111111, // String
+ };
+
+ private static int[] ImplicitConversions =
+ {
+ 0b000000000000001, // Boolean
+ 0b001110111100010, // Char
+ 0b001110101010100, // SByte
+ 0b001111111111000, // Byte
+ 0b001110101010000, // Int16
+ 0b001111111100000, // UInt16
+ 0b001110101000000, // Int32
+ 0b001111110000000, // UInt32
+ 0b001110100000000, // Int64
+ 0b001111000000000, // UInt64
+ 0b000110000000000, // Single
+ 0b000100000000000, // Double
+ 0b001000000000000, // Decimal
+ 0b010000000000000, // DateTime
+ 0b100000000000000, // String
+ };
+
+ private static Type[] InbuiltTypes =
+ {
+ typeof(Boolean),
+ typeof(Char),
+ typeof(SByte),
+ typeof(Byte),
+ typeof(Int16),
+ typeof(UInt16),
+ typeof(Int32),
+ typeof(UInt32),
+ typeof(Int64),
+ typeof(UInt64),
+ typeof(Single),
+ typeof(Double),
+ typeof(Decimal),
+ typeof(DateTime),
+ typeof(String),
};
private static readonly Type[] NumericTypes = new[]
@@ -54,49 +98,104 @@ namespace Avalonia.Utilities
}
///
- /// Try to cast a value to a type, using implicit conversions if possible.
+ /// Try to convert a value to a type by any means possible.
///
/// The type to cast to.
/// The value to cast.
+ /// The culture to use.
/// If sucessful, contains the cast value.
/// True if the cast was sucessful, otherwise false.
- public static bool TryCast(Type to, object value, out object result)
+ public static bool TryConvert(Type to, object value, CultureInfo culture, out object result)
{
- Contract.Requires(to != null);
-
if (value == null)
{
result = null;
return AcceptsNull(to);
}
- var from = value.GetType();
-
if (value == AvaloniaProperty.UnsetValue)
{
result = value;
return true;
}
- else if (to.GetTypeInfo().IsAssignableFrom(from.GetTypeInfo()))
+
+ var from = value.GetType();
+ var fromTypeInfo = from.GetTypeInfo();
+ var toTypeInfo = to.GetTypeInfo();
+
+ if (toTypeInfo.IsAssignableFrom(fromTypeInfo))
{
result = value;
return true;
}
- else if (Conversions.ContainsKey(to) && Conversions[to].Contains(from))
+
+ if (to == typeof(string))
{
- result = Convert.ChangeType(value, to);
+ result = Convert.ToString(value);
return true;
}
- else
+
+ if (toTypeInfo.IsEnum && from == typeof(string))
+ {
+ if (Enum.IsDefined(to, (string)value))
+ {
+ result = Enum.Parse(to, (string)value);
+ return true;
+ }
+ }
+
+ if (!fromTypeInfo.IsEnum && toTypeInfo.IsEnum)
{
- var cast = from.GetRuntimeMethods()
- .FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == to);
+ result = null;
+
+ if (TryConvert(Enum.GetUnderlyingType(to), value, culture, out object enumValue))
+ {
+ result = Enum.ToObject(to, enumValue);
+ return true;
+ }
+ }
- if (cast != null)
+ if (fromTypeInfo.IsEnum && IsNumeric(to))
+ {
+ try
{
- result = cast.Invoke(null, new[] { value });
+ result = Convert.ChangeType((int)value, to, culture);
return true;
}
+ catch
+ {
+ result = null;
+ return false;
+ }
+ }
+
+ var convertableFrom = Array.IndexOf(InbuiltTypes, from);
+ var convertableTo = Array.IndexOf(InbuiltTypes, to);
+
+ if (convertableFrom != -1 && convertableTo != -1)
+ {
+ if ((Conversions[convertableFrom] & 1 << convertableTo) != 0)
+ {
+ try
+ {
+ result = Convert.ChangeType(value, to, culture);
+ return true;
+ }
+ catch
+ {
+ result = null;
+ return false;
+ }
+ }
+ }
+
+ var cast = from.GetRuntimeMethods()
+ .FirstOrDefault(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.ReturnType == to);
+
+ if (cast != null)
+ {
+ result = cast.Invoke(null, new[] { value });
+ return true;
}
result = null;
@@ -104,15 +203,14 @@ namespace Avalonia.Utilities
}
///
- /// Try to convert a value to a type, using if possible,
- /// otherwise using .
+ /// Try to convert a value to a type using the implicit conversions allowed by the C#
+ /// language.
///
/// The type to cast to.
/// The value to cast.
- /// The culture to use.
/// If sucessful, contains the cast value.
/// True if the cast was sucessful, otherwise false.
- public static bool TryConvert(Type to, object value, CultureInfo culture, out object result)
+ public static bool TryConvertImplicit(Type to, object value, out object result)
{
if (value == null)
{
@@ -120,54 +218,44 @@ namespace Avalonia.Utilities
return AcceptsNull(to);
}
- var from = value.GetType();
-
if (value == AvaloniaProperty.UnsetValue)
{
result = value;
return true;
}
- if (to.GetTypeInfo().IsAssignableFrom(from.GetTypeInfo()))
- {
- result = value;
- return true;
- }
+ var from = value.GetType();
+ var fromTypeInfo = from.GetTypeInfo();
+ var toTypeInfo = to.GetTypeInfo();
- if (to == typeof(string))
+ if (toTypeInfo.IsAssignableFrom(fromTypeInfo))
{
- result = Convert.ToString(value);
+ result = value;
return true;
}
- if (to.GetTypeInfo().IsEnum && from == typeof(string))
- {
- if (Enum.IsDefined(to, (string)value))
- {
- result = Enum.Parse(to, (string)value);
- return true;
- }
- }
-
- bool containsFrom = Conversions.ContainsKey(from);
- bool containsTo = Conversions.ContainsKey(to);
+ var convertableFrom = Array.IndexOf(InbuiltTypes, from);
+ var convertableTo = Array.IndexOf(InbuiltTypes, to);
- if ((containsFrom && containsTo) || (from == typeof(string) && containsTo))
+ if (convertableFrom != -1 && convertableTo != -1)
{
- try
- {
- result = Convert.ChangeType(value, to, culture);
- return true;
- }
- catch
+ if ((ImplicitConversions[convertableFrom] & 1 << convertableTo) != 0)
{
- result = null;
- return false;
+ try
+ {
+ result = Convert.ChangeType(value, to, CultureInfo.InvariantCulture);
+ return true;
+ }
+ catch
+ {
+ result = null;
+ return false;
+ }
}
}
var cast = from.GetRuntimeMethods()
- .FirstOrDefault(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.ReturnType == to);
+ .FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == to);
if (cast != null)
{
@@ -180,29 +268,28 @@ namespace Avalonia.Utilities
}
///
- /// Casts a value to a type, returning the default for that type if the value could not be
- /// cast.
+ /// Convert a value to a type by any means possible, returning the default for that type
+ /// if the value could not be converted.
///
/// The value to cast.
/// The type to cast to..
+ /// The culture to use.
/// A value of .
- public static object CastOrDefault(object value, Type type)
+ public static object ConvertOrDefault(object value, Type type, CultureInfo culture)
{
- var typeInfo = type.GetTypeInfo();
- object result;
+ return TryConvert(type, value, culture, out object result) ? result : Default(type);
+ }
- if (TypeUtilities.TryCast(type, value, out result))
- {
- return result;
- }
- else if (typeInfo.IsValueType)
- {
- return Activator.CreateInstance(type);
- }
- else
- {
- return null;
- }
+ ///
+ /// Convert a value to a type using the implicit conversions allowed by the C# language or
+ /// return the default for the type if the value could not be converted.
+ ///
+ /// The value to cast.
+ /// The type to cast to..
+ /// A value of .
+ public static object ConvertImplicitOrDefault(object value, Type type)
+ {
+ return TryConvertImplicit(type, value, out object result) ? result : Default(type);
}
///
diff --git a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
index 2af267aa97..11fbae4321 100644
--- a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
+++ b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
@@ -189,7 +189,7 @@ namespace Avalonia.VisualTree
{
Contract.Requires(visual != null);
- return visual.VisualRoot as IRenderRoot ?? visual.VisualRoot;
+ return visual as IRenderRoot ?? visual.VisualRoot;
}
///
diff --git a/src/Markup/Avalonia.Markup/DefaultValueConverter.cs b/src/Markup/Avalonia.Markup/DefaultValueConverter.cs
index 86d37d8e13..b56291a653 100644
--- a/src/Markup/Avalonia.Markup/DefaultValueConverter.cs
+++ b/src/Markup/Avalonia.Markup/DefaultValueConverter.cs
@@ -3,10 +3,7 @@
using System;
using System.Globalization;
-using System.Linq;
-using System.Reflection;
using Avalonia.Data;
-using Avalonia.Logging;
using Avalonia.Utilities;
namespace Avalonia.Markup
@@ -32,32 +29,28 @@ namespace Avalonia.Markup
/// The converted value.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
- object result;
-
- if (value != null &&
- (TypeUtilities.TryConvert(targetType, value, culture, out result) ||
- TryConvertEnum(value, targetType, culture, out result)))
+ if (value == null)
{
- return result;
+ return AvaloniaProperty.UnsetValue;
}
- if (value != null)
+ if (TypeUtilities.TryConvert(targetType, value, culture, out object result))
{
- string message;
+ return result;
+ }
- if (TypeUtilities.IsNumeric(targetType))
- {
- message = $"'{value}' is not a valid number.";
- }
- else
- {
- message = $"Could not convert '{value}' to '{targetType.Name}'.";
- }
+ string message;
- return new BindingNotification(new InvalidCastException(message), BindingErrorType.Error);
+ if (TypeUtilities.IsNumeric(targetType))
+ {
+ message = $"'{value}' is not a valid number.";
+ }
+ else
+ {
+ message = $"Could not convert '{value}' to '{targetType.Name}'.";
}
- return AvaloniaProperty.UnsetValue;
+ return new BindingNotification(new InvalidCastException(message), BindingErrorType.Error);
}
///
@@ -72,34 +65,5 @@ namespace Avalonia.Markup
{
return Convert(value, targetType, parameter, culture);
}
-
- private bool TryConvertEnum(object value, Type targetType, CultureInfo cultur, out object result)
- {
- var valueTypeInfo = value.GetType().GetTypeInfo();
- var targetTypeInfo = targetType.GetTypeInfo();
-
- if (valueTypeInfo.IsEnum && !targetTypeInfo.IsEnum)
- {
- var enumValue = (int)value;
-
- if (TypeUtilities.TryCast(targetType, enumValue, out result))
- {
- return true;
- }
- }
- else if (!valueTypeInfo.IsEnum && targetTypeInfo.IsEnum)
- {
- object intValue;
-
- if (TypeUtilities.TryCast(typeof(int), value, out intValue))
- {
- result = Enum.ToObject(targetType, intValue);
- return true;
- }
- }
-
- result = null;
- return false;
- }
}
}
diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
index 1125863e72..4a9f2c6572 100644
--- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
@@ -285,7 +285,7 @@ namespace Avalonia.Skia
paint.StrokeCap = SKStrokeCap.Butt;
if (pen.LineJoin == PenLineJoin.Miter)
- paint.StrokeJoin = SKStrokeJoin.Mitter;
+ paint.StrokeJoin = SKStrokeJoin.Miter;
else if (pen.LineJoin == PenLineJoin.Round)
paint.StrokeJoin = SKStrokeJoin.Round;
else
@@ -397,7 +397,7 @@ namespace Avalonia.Skia
public void PopOpacityMask()
{
- Canvas.SaveLayer(new SKPaint { XferMode = SKXferMode.DstIn });
+ Canvas.SaveLayer(new SKPaint { BlendMode = SKBlendMode.DstIn });
using (var paintWrapper = maskStack.Pop())
{
Canvas.DrawPaint(paintWrapper.Paint);
diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
index 133d9cd789..8568c80c04 100644
--- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
+++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
@@ -42,7 +42,7 @@ namespace Avalonia.Skia
_paint.Typeface = skiaTypeface;
_paint.TextSize = (float)(typeface?.FontSize ?? 12);
_paint.TextAlign = textAlignment.ToSKTextAlign();
- _paint.XferMode = SKXferMode.Src;
+ _paint.BlendMode = SKBlendMode.Src;
_wrapping = wrapping;
_constraint = constraint;
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorParserTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorParserTests.cs
new file mode 100644
index 0000000000..8c0b043907
--- /dev/null
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorParserTests.cs
@@ -0,0 +1,17 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml.Parsers;
+using Xunit;
+
+namespace Avalonia.Markup.Xaml.UnitTests.Parsers
+{
+ public class SelectorParserTests
+ {
+ [Fact]
+ public void Parses_Boolean_Property_Selector()
+ {
+ var target = new SelectorParser((type, ns) => typeof(TextBlock));
+ var result = target.Parse("TextBlock[IsPointerOver=True]");
+ }
+ }
+}