From 7b4cd4f96d1d5afad14a7eddbb79069bada4dbc7 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 24 Mar 2018 15:59:44 +0100 Subject: [PATCH 01/16] Modify/add tests for non-registered properties. We should change to the behavior of other XAML frameworks where non-registered `AvaloniaProperty`s can be set on `AvaloniaObject`s via code. --- .../AvaloniaObjectTests_Binding.cs | 9 ++- .../AvaloniaObjectTests_GetValue.cs | 4 +- .../AvaloniaObjectTests_SetValue.cs | 39 +++++++++-- .../Xaml/AttachedPropertyOwner.cs | 14 ++++ .../Xaml/BasicTests.cs | 19 ++++++ .../Xaml/BindingTests.cs | 66 +++++++++++++++++++ .../Xaml/TestControl.cs | 17 +++++ 7 files changed, 156 insertions(+), 12 deletions(-) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Xaml/AttachedPropertyOwner.cs create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TestControl.cs diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index c75150ca6d..80cd52d529 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -92,14 +92,13 @@ namespace Avalonia.Base.UnitTests } [Fact] - public void Bind_Throws_Exception_For_Unregistered_Property() + public void Bind_Does_Not_Throw_Exception_For_Unregistered_Property() { Class1 target = new Class1(); - Assert.Throws(() => - { - target.Bind(Class2.BarProperty, Observable.Return("foo")); - }); + target.Bind(Class2.BarProperty, Observable.Never().StartWith("foo")); + + Assert.Equal("foo", target.GetValue(Class2.BarProperty)); } [Fact] diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_GetValue.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_GetValue.cs index 98f7289228..740023fd37 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_GetValue.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_GetValue.cs @@ -46,11 +46,11 @@ namespace Avalonia.Base.UnitTests } [Fact] - public void GetValue_Throws_Exception_For_Unregistered_Property() + public void GetValue_Doesnt_Throw_Exception_For_Unregistered_Property() { var target = new Class3(); - Assert.Throws(() => target.GetValue(Class1.FooProperty)); + Assert.Equal("foodefault", target.GetValue(Class1.FooProperty)); } private class Class1 : AvaloniaObject diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs index 1f9c47f8ae..a56cd717b9 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs @@ -30,6 +30,16 @@ namespace Avalonia.Base.UnitTests Assert.Equal("newvalue", target.GetValue(Class1.FooProperty)); } + [Fact] + public void SetValue_Sets_Attached_Value() + { + Class2 target = new Class2(); + + target.SetValue(AttachedOwner.AttachedProperty, "newvalue"); + + Assert.Equal("newvalue", target.GetValue(AttachedOwner.AttachedProperty)); + } + [Fact] public void SetValue_Raises_PropertyChanged() { @@ -84,14 +94,27 @@ namespace Avalonia.Base.UnitTests } [Fact] - public void SetValue_Throws_Exception_For_Unregistered_Property() + public void SetValue_Allows_Setting_Unregistered_Property() { Class1 target = new Class1(); - Assert.Throws(() => - { - target.SetValue(Class2.BarProperty, "invalid"); - }); + Assert.False(AvaloniaPropertyRegistry.Instance.IsRegistered(target, Class2.BarProperty)); + + target.SetValue(Class2.BarProperty, "bar"); + + Assert.Equal("bar", target.GetValue(Class2.BarProperty)); + } + + [Fact] + public void SetValue_Allows_Setting_Unregistered_Attached_Property() + { + Class1 target = new Class1(); + + Assert.False(AvaloniaPropertyRegistry.Instance.IsRegistered(target, AttachedOwner.AttachedProperty)); + + target.SetValue(AttachedOwner.AttachedProperty, "bar"); + + Assert.Equal("bar", target.GetValue(AttachedOwner.AttachedProperty)); } [Fact] @@ -189,6 +212,12 @@ namespace Avalonia.Base.UnitTests } } + private class AttachedOwner + { + public static readonly AttachedProperty AttachedProperty = + AvaloniaProperty.RegisterAttached("Attached"); + } + private class ImplictDouble { public ImplictDouble(double value) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/AttachedPropertyOwner.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/AttachedPropertyOwner.cs new file mode 100644 index 0000000000..aac5b01f96 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/AttachedPropertyOwner.cs @@ -0,0 +1,14 @@ +using System; +using Avalonia.Controls; + +namespace Avalonia.Markup.Xaml.UnitTests.Xaml +{ + public class AttachedPropertyOwner + { + public static readonly AttachedProperty DoubleProperty = + AvaloniaProperty.RegisterAttached("Double"); + + public static double GetDouble(Control control) => control.GetValue(DoubleProperty); + public static void SetDouble(Control control, double value) => control.SetValue(DoubleProperty, value); + } +} diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs index fb432c30d4..9bee92cd7a 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs @@ -10,6 +10,7 @@ using Avalonia.Markup.Xaml.Templates; using Avalonia.Media; using Avalonia.Styling; using Avalonia.UnitTests; +using Portable.Xaml; using System.Collections; using System.ComponentModel; using System.Linq; @@ -124,6 +125,24 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal("Foo", ToolTip.GetTip(target)); } + [Fact] + public void NonExistent_Property_Throws() + { + var xaml = + @""; + + Assert.Throws(() => AvaloniaXamlLoader.Parse(xaml)); + } + + [Fact] + public void Non_Attached_Property_With_Attached_Property_Syntax_Throws() + { + var xaml = + @""; + + Assert.Throws(() => AvaloniaXamlLoader.Parse(xaml)); + } + [Fact] public void ContentControl_ContentTemplate_Is_Functional() { diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs index a44d09dee7..568c6482f5 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs @@ -215,5 +215,71 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal("bar", textBlock.Text); } } + + [Fact] + public void Binding_To_Namespaced_Attached_Property_Works() + { + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + { + var xaml = @" + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = (TextBlock)window.Content; + + window.DataContext = 5.6; + window.ApplyTemplate(); + + Assert.Equal(5.6, AttachedPropertyOwner.GetDouble(textBlock)); + } + } + + [Fact] + public void Binding_To_AddOwnered_Attached_Property_Works() + { + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + { + var xaml = @" + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var testControl = (TestControl)window.Content; + + window.DataContext = 5.6; + window.ApplyTemplate(); + + Assert.Equal(5.6, testControl.Double); + } + } + + [Fact] + public void Binding_To_Attached_Property_Using_AddOwnered_Type_Works() + { + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + { + var xaml = @" + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = (TextBlock)window.Content; + + window.DataContext = 5.6; + window.ApplyTemplate(); + + Assert.Equal(5.6, AttachedPropertyOwner.GetDouble(textBlock)); + } + } } } \ No newline at end of file diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TestControl.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TestControl.cs new file mode 100644 index 0000000000..d0591e5ee6 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/TestControl.cs @@ -0,0 +1,17 @@ +using System; +using Avalonia.Controls; + +namespace Avalonia.Markup.Xaml.UnitTests.Xaml +{ + public class TestControl : Control + { + public static readonly StyledProperty DoubleProperty = + AttachedPropertyOwner.DoubleProperty.AddOwner(); + + public double Double + { + get => GetValue(DoubleProperty); + set => SetValue(DoubleProperty, value); + } + } +} From f8791ecad784022ff5fc30dbbd34261f0653104d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 14 Apr 2018 16:19:05 +0200 Subject: [PATCH 02/16] Make attached property accessors static. --- src/Avalonia.Controls/Primitives/TemplatedControl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index 77735f3f12..1a805a3822 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -207,7 +207,7 @@ namespace Avalonia.Controls.Primitives /// The control. /// The property value. /// - public bool GetIsTemplateFocusTarget(Control control) + public static bool GetIsTemplateFocusTarget(Control control) { return control.GetValue(IsTemplateFocusTargetProperty); } @@ -223,7 +223,7 @@ namespace Avalonia.Controls.Primitives /// attached property is set to true on an element in the control template, then the focus /// adorner will be shown around that control instead. /// - public void SetIsTemplateFocusTarget(Control control, bool value) + public static void SetIsTemplateFocusTarget(Control control, bool value) { control.SetValue(IsTemplateFocusTargetProperty, value); } From 44cfcfb04d49f46c3b2a5f5c075907e8872c6f8d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 14 Apr 2018 18:09:53 +0200 Subject: [PATCH 03/16] Allow any property to be registered anywhere. Makes Avalonia more like other XAML frameworks, in that any avalonia property can now be set anywhere. The XAML parser however will only allow you to set registered properties. --- src/Avalonia.Base/AttachedProperty.cs | 9 +- src/Avalonia.Base/AvaloniaObject.cs | 50 ++-- src/Avalonia.Base/AvaloniaProperty.cs | 8 +- src/Avalonia.Base/AvaloniaPropertyRegistry.cs | 283 ++++++++---------- src/Avalonia.Base/DirectProperty.cs | 3 + src/Avalonia.Base/IDirectPropertyAccessor.cs | 7 + .../AvaloniaPropertyTypeConverter.cs | 5 +- .../PortableXaml/AvaloniaXamlSchemaContext.cs | 3 +- .../PortableXaml/AvaloniaXamlType.cs | 22 +- .../Plugins/AvaloniaPropertyAccessorPlugin.cs | 55 +++- .../AvaloniaObjectTests_AddOwner.cs | 52 ++++ .../AvaloniaObjectTests_Attached.cs | 52 ++++ .../AvaloniaPropertyRegistryTests.cs | 108 +++---- 13 files changed, 386 insertions(+), 271 deletions(-) create mode 100644 tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_AddOwner.cs create mode 100644 tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs diff --git a/src/Avalonia.Base/AttachedProperty.cs b/src/Avalonia.Base/AttachedProperty.cs index 9d4d40bfef..fdb04b6dfc 100644 --- a/src/Avalonia.Base/AttachedProperty.cs +++ b/src/Avalonia.Base/AttachedProperty.cs @@ -9,7 +9,7 @@ namespace Avalonia /// An attached avalonia property. /// /// The type of the property's value. - public class AttachedProperty : StyledPropertyBase + public class AttachedProperty : StyledProperty { /// /// Initializes a new instance of the class. @@ -35,11 +35,10 @@ namespace Avalonia /// /// The owner type. /// The property. - public StyledProperty AddOwner() where TOwner : IAvaloniaObject + public new AttachedProperty AddOwner() where TOwner : IAvaloniaObject { - var result = new StyledProperty(this, typeof(TOwner)); - AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result); - return result; + AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), this); + return this; } } } diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index a46d567d28..4ab813333d 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -12,7 +12,6 @@ using Avalonia.Diagnostics; using Avalonia.Logging; using Avalonia.Threading; using Avalonia.Utilities; -using System.Reactive.Concurrency; namespace Avalonia { @@ -218,11 +217,6 @@ namespace Avalonia } else { - if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property)) - { - ThrowNotRegistered(property); - } - return GetValueInternal(property); } } @@ -377,11 +371,6 @@ namespace Avalonia { PriorityValue v; - if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property)) - { - ThrowNotRegistered(property); - } - if (!_values.TryGetValue(property, out v)) { v = CreatePriorityValue(property); @@ -804,11 +793,6 @@ namespace Avalonia var originalValue = value; - if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property)) - { - ThrowNotRegistered(property); - } - if (!TypeUtilities.TryConvertImplicit(property.PropertyType, value, out value)) { throw new ArgumentException(string.Format( @@ -836,18 +820,32 @@ namespace Avalonia } /// - /// Given a returns a registered avalonia property that is - /// equal or throws if not found. + /// Given a direct property, returns a registered avalonia property that is equivalent or + /// throws if not found. /// /// The property. /// The registered property. - public AvaloniaProperty GetRegistered(AvaloniaProperty property) + private AvaloniaProperty GetRegistered(AvaloniaProperty property) { - var result = AvaloniaPropertyRegistry.Instance.FindRegistered(this, property); + var direct = property as IDirectPropertyAccessor; + + if (direct == null) + { + throw new AvaloniaInternalException( + "AvaloniaObject.GetRegistered should only be called for direct properties"); + } + + if (property.OwnerType.IsAssignableFrom(GetType())) + { + return property; + } + + var result = AvaloniaPropertyRegistry.Instance.GetRegistered(this) + .FirstOrDefault(x => x == property); if (result == null) { - ThrowNotRegistered(property); + throw new ArgumentException($"Property '{property.Name} not registered on '{this.GetType()}"); } return result; @@ -898,15 +896,5 @@ namespace Avalonia value, priority); } - - /// - /// Throws an exception indicating that the specified property is not registered on this - /// object. - /// - /// The property - private void ThrowNotRegistered(AvaloniaProperty p) - { - throw new ArgumentException($"Property '{p.Name} not registered on '{this.GetType()}"); - } } } diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs index fb78e3b2a0..f7dabd3a43 100644 --- a/src/Avalonia.Base/AvaloniaProperty.cs +++ b/src/Avalonia.Base/AvaloniaProperty.cs @@ -311,7 +311,9 @@ namespace Avalonia defaultBindingMode: defaultBindingMode); var result = new AttachedProperty(name, typeof(TOwner), metadata, inherits); - AvaloniaPropertyRegistry.Instance.Register(typeof(THost), result); + var registry = AvaloniaPropertyRegistry.Instance; + registry.Register(typeof(TOwner), result); + registry.RegisterAttached(typeof(THost), result); return result; } @@ -344,7 +346,9 @@ namespace Avalonia defaultBindingMode: defaultBindingMode); var result = new AttachedProperty(name, ownerType, metadata, inherits); - AvaloniaPropertyRegistry.Instance.Register(typeof(THost), result); + var registry = AvaloniaPropertyRegistry.Instance; + registry.Register(ownerType, result); + registry.RegisterAttached(typeof(THost), result); return result; } diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs index ec1643427b..c0a4ace6ed 100644 --- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs +++ b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Runtime.CompilerServices; namespace Avalonia @@ -14,23 +13,14 @@ namespace Avalonia /// public class AvaloniaPropertyRegistry { - /// - /// The registered properties by type. - /// private readonly Dictionary> _registered = new Dictionary>(); - - /// - /// The registered properties by type cached values to increase performance. - /// - private readonly Dictionary> _registeredCache = - new Dictionary>(); - - /// - /// The registered attached properties by owner type. - /// private readonly Dictionary> _attached = new Dictionary>(); + private readonly Dictionary> _registeredCache = + new Dictionary>(); + private readonly Dictionary> _attachedCache = + new Dictionary>(); /// /// Gets the instance @@ -39,51 +29,68 @@ namespace Avalonia = new AvaloniaPropertyRegistry(); /// - /// Gets all attached s registered by an owner. + /// Gets all non-attached s registered on a type. /// - /// The owner type. + /// The type. /// A collection of definitions. - public IEnumerable GetAttached(Type ownerType) + public IEnumerable GetRegistered(Type type) { - Dictionary inner; + Contract.Requires(type != null); + + if (_registeredCache.TryGetValue(type, out var result)) + { + return result; + } - // Ensure the type's static ctor has been run. - RuntimeHelpers.RunClassConstructor(ownerType.TypeHandle); + var t = type; + result = new List(); - if (_attached.TryGetValue(ownerType, out inner)) + while (t != null) { - return inner.Values; + // Ensure the type's static ctor has been run. + RuntimeHelpers.RunClassConstructor(t.TypeHandle); + + if (_registered.TryGetValue(t, out var registered)) + { + result.AddRange(registered.Values); + } + + t = t.BaseType; } - return Enumerable.Empty(); + _registeredCache.Add(type, result); + return result; } /// - /// Gets all s registered on a type. + /// Gets all attached s registered on a type. /// /// The type. /// A collection of definitions. - public IEnumerable GetRegistered(Type type) + public IEnumerable GetRegisteredAttached(Type type) { Contract.Requires(type != null); - while (type != null) + if (_attachedCache.TryGetValue(type, out var result)) { - // Ensure the type's static ctor has been run. - RuntimeHelpers.RunClassConstructor(type.TypeHandle); + return result; + } - Dictionary inner; + var t = type; + result = new List(); - if (_registered.TryGetValue(type, out inner)) + while (t != null) + { + if (_attached.TryGetValue(t, out var attached)) { - foreach (var p in inner) - { - yield return p.Value; - } + result.AddRange(attached.Values); } - type = type.GetTypeInfo().BaseType; + t = t.BaseType; } + + _attachedCache.Add(type, result); + return result; } /// @@ -99,142 +106,92 @@ namespace Avalonia } /// - /// Finds a registered on a type. + /// Finds a registered non-attached property on a type by name. /// /// The type. - /// The property. - /// The registered property or null if not found. - /// - /// Calling AddOwner on a AvaloniaProperty creates a new AvaloniaProperty that is a - /// different object but is equal according to . - /// - public AvaloniaProperty FindRegistered(Type type, AvaloniaProperty property) + /// The property name. + /// + /// The registered property or null if no matching property found. + /// + /// + /// The property name contains a '.'. + /// + public AvaloniaProperty FindRegistered(Type type, string name) { - Type currentType = type; - Dictionary cache; - AvaloniaProperty result; + Contract.Requires(type != null); + Contract.Requires(name != null); - if (_registeredCache.TryGetValue(type, out cache)) + if (name.Contains('.')) { - if (cache.TryGetValue(property.Id, out result)) - { - return result; - } + throw new InvalidOperationException("Attached properties not supported."); } - while (currentType != null) - { - Dictionary inner; - - if (_registered.TryGetValue(currentType, out inner)) - { - if (inner.TryGetValue(property.Id, out result)) - { - if (cache == null) - { - _registeredCache[type] = cache = new Dictionary(); - } - - cache[property.Id] = result; - - return result; - } - } - - currentType = currentType.GetTypeInfo().BaseType; - } - - return null; + return GetRegistered(type).FirstOrDefault(x => x.Name == name); } /// - /// Finds registered on an object. + /// Finds a registered non-attached property on a type by name. /// /// The object. - /// The property. - /// The registered property or null if not found. - /// - /// Calling AddOwner on a AvaloniaProperty creates a new AvaloniaProperty that is a - /// different object but is equal according to . - /// - public AvaloniaProperty FindRegistered(object o, AvaloniaProperty property) + /// The property name. + /// + /// The registered property or null if no matching property found. + /// + /// + /// The property name contains a '.'. + /// + public AvaloniaProperty FindRegistered(AvaloniaObject o, string name) { - return FindRegistered(o.GetType(), property); + Contract.Requires(o != null); + Contract.Requires(name != null); + + return FindRegistered(o.GetType(), name); } /// - /// Finds a registered property on a type by name. + /// Finds a registered attached property on a type by name. /// /// The type. - /// - /// The property name. If an attached property it should be in the form - /// "OwnerType.PropertyName". - /// + /// The owner type. + /// The property name. /// /// The registered property or null if no matching property found. /// - public AvaloniaProperty FindRegistered(Type type, string name) + /// + /// The property name contains a '.'. + /// + public AvaloniaProperty FindRegisteredAttached(Type type, Type ownerType, string name) { Contract.Requires(type != null); + Contract.Requires(ownerType != null); Contract.Requires(name != null); - var parts = name.Split('.'); - var types = GetImplementedTypes(type).ToList(); - - if (parts.Length < 1 || parts.Length > 2) + if (name.Contains('.')) { - throw new ArgumentException("Invalid property name."); + throw new InvalidOperationException("Attached properties not supported."); } - string propertyName; - var results = GetRegistered(type); - - if (parts.Length == 1) - { - propertyName = parts[0]; - results = results.Where(x => !x.IsAttached || types.Contains(x.OwnerType.Name)); - } - else - { - if (!types.Contains(parts[0])) - { - results = results.Where(x => x.OwnerType.Name == parts[0]); - } - - propertyName = parts[1]; - } - - return results.FirstOrDefault(x => x.Name == propertyName); + return GetRegisteredAttached(type).FirstOrDefault(x => x.Name == name); } /// - /// Finds a registered property on an object by name. + /// Finds a registered non-attached property on a type by name. /// /// The object. - /// - /// The property name. If an attached property it should be in the form - /// "OwnerType.PropertyName". - /// + /// The owner type. + /// The property name. /// /// The registered property or null if no matching property found. /// - public AvaloniaProperty FindRegistered(AvaloniaObject o, string name) + /// + /// The property name contains a '.'. + /// + public AvaloniaProperty FindRegisteredAttached(AvaloniaObject o, Type ownerType, string name) { - return FindRegistered(o.GetType(), name); - } + Contract.Requires(o != null); + Contract.Requires(name != null); - /// - /// Returns a type and all its base types. - /// - /// The type. - /// The type and all its base types. - private IEnumerable GetImplementedTypes(Type type) - { - while (type != null) - { - yield return type.Name; - type = type.GetTypeInfo().BaseType; - } + return FindRegisteredAttached(o.GetType(), ownerType, name); } /// @@ -245,7 +202,11 @@ namespace Avalonia /// True if the property is registered, otherwise false. public bool IsRegistered(Type type, AvaloniaProperty property) { - return FindRegistered(type, property) != null; + Contract.Requires(type != null); + Contract.Requires(property != null); + + return Instance.GetRegistered(type).Any(x => x == property) || + Instance.GetRegisteredAttached(type).Any(x => x == property); } /// @@ -256,6 +217,9 @@ namespace Avalonia /// True if the property is registered, otherwise false. public bool IsRegistered(object o, AvaloniaProperty property) { + Contract.Requires(o != null); + Contract.Requires(property != null); + return IsRegistered(o.GetType(), property); } @@ -274,34 +238,53 @@ namespace Avalonia Contract.Requires(type != null); Contract.Requires(property != null); - Dictionary inner; - - if (!_registered.TryGetValue(type, out inner)) + if (!_registered.TryGetValue(type, out var inner)) { inner = new Dictionary(); + inner.Add(property.Id, property); _registered.Add(type, inner); } - - if (!inner.ContainsKey(property.Id)) + else if (!inner.ContainsKey(property.Id)) { inner.Add(property.Id, property); } + + _registeredCache.Clear(); + } - if (property.IsAttached) + /// + /// Registers an attached on a type. + /// + /// The type. + /// The property. + /// + /// You won't usually want to call this method directly, instead use the + /// + /// method. + /// + public void RegisterAttached(Type type, AvaloniaProperty property) + { + Contract.Requires(type != null); + Contract.Requires(property != null); + + if (!property.IsAttached) { - if (!_attached.TryGetValue(property.OwnerType, out inner)) - { - inner = new Dictionary(); - _attached.Add(property.OwnerType, inner); - } + throw new InvalidOperationException( + "Cannot register a non-attached property as attached."); + } - if (!inner.ContainsKey(property.Id)) - { - inner.Add(property.Id, property); - } + if (!_attached.TryGetValue(type, out var inner)) + { + inner = new Dictionary(); + inner.Add(property.Id, property); + _attached.Add(type, inner); + } + else + { + inner.Add(property.Id, property); } - _registeredCache.Clear(); + _attachedCache.Clear(); } } } \ No newline at end of file diff --git a/src/Avalonia.Base/DirectProperty.cs b/src/Avalonia.Base/DirectProperty.cs index 8352528285..1ce73c20ba 100644 --- a/src/Avalonia.Base/DirectProperty.cs +++ b/src/Avalonia.Base/DirectProperty.cs @@ -75,6 +75,9 @@ namespace Avalonia /// public Action Setter { get; } + /// + Type IDirectPropertyAccessor.Owner => typeof(TOwner); + /// /// Registers the direct property on another type. /// diff --git a/src/Avalonia.Base/IDirectPropertyAccessor.cs b/src/Avalonia.Base/IDirectPropertyAccessor.cs index 62aeef73c7..4f46652693 100644 --- a/src/Avalonia.Base/IDirectPropertyAccessor.cs +++ b/src/Avalonia.Base/IDirectPropertyAccessor.cs @@ -1,6 +1,8 @@ // 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 System; + namespace Avalonia { /// @@ -14,6 +16,11 @@ namespace Avalonia /// bool IsReadOnly { get; } + /// + /// Gets the class that registered the property. + /// + Type Owner { get; } + /// /// Gets the value of the property on the instance. /// diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs index a34ccaa413..bc3caff3b9 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs @@ -53,10 +53,7 @@ namespace Avalonia.Markup.Xaml.Converters } } - // First look for non-attached property on the type and then look for an attached property. - var property = AvaloniaPropertyRegistry.Instance.FindRegistered(type, s) ?? - AvaloniaPropertyRegistry.Instance.GetAttached(type) - .FirstOrDefault(x => x.Name == propertyName); + AvaloniaProperty property = AvaloniaPropertyRegistry.Instance.FindRegistered(type, propertyName); if (property == null) { diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs index bdb21abd77..fda5da902a 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs @@ -200,8 +200,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml var type = (getter ?? setter).DeclaringType; - var prop = AvaloniaPropertyRegistry.Instance.GetAttached(type) - .FirstOrDefault(v => v.Name == attachablePropertyName); + var prop = AvaloniaPropertyRegistry.Instance.FindRegistered(type, attachablePropertyName); if (prop != null) { diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs index 7de96ea220..59dbba7084 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs @@ -19,16 +19,36 @@ namespace Avalonia.Markup.Xaml.PortableXaml public class AvaloniaXamlType : XamlType { + static readonly AvaloniaPropertyTypeConverter propertyTypeConverter = new AvaloniaPropertyTypeConverter(); + public AvaloniaXamlType(Type underlyingType, XamlSchemaContext schemaContext) : base(underlyingType, schemaContext) { } + protected override XamlMember LookupAttachableMember(string name) + { + var m = base.LookupAttachableMember(name); + + if (m == null) + { + // Might be an AddOwnered attached property. + var avProp = AvaloniaPropertyRegistry.Instance.FindRegistered(UnderlyingType, name); + + if (avProp?.IsAttached == true) + { + return new AvaloniaPropertyXamlMember(avProp, this); + } + } + + return m; + } + protected override XamlMember LookupMember(string name, bool skipReadOnlyCheck) { var m = base.LookupMember(name, skipReadOnlyCheck); - if (m == null) + if (m == null && !name.Contains(".")) { //so far Portable.xaml haven't found the member/property //but what if we have AvaloniaProperty diff --git a/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs b/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs index 90eabc69fb..ac64459dd7 100644 --- a/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs +++ b/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Linq; using System.Reactive.Linq; using Avalonia.Data; @@ -15,9 +16,9 @@ namespace Avalonia.Markup.Data.Plugins /// public bool Match(object obj, string propertyName) { - if (obj is AvaloniaObject a) + if (obj is AvaloniaObject o) { - return AvaloniaPropertyRegistry.Instance.FindRegistered(a, propertyName) != null; + return LookupProperty(o, propertyName) != null; } return false; @@ -39,7 +40,7 @@ namespace Avalonia.Markup.Data.Plugins var instance = reference.Target; var o = (AvaloniaObject)instance; - var p = AvaloniaPropertyRegistry.Instance.FindRegistered(o, propertyName); + var p = LookupProperty(o, propertyName); if (p != null) { @@ -57,6 +58,54 @@ namespace Avalonia.Markup.Data.Plugins } } + private static AvaloniaProperty LookupProperty(AvaloniaObject o, string propertyName) + { + if (!propertyName.Contains(".")) + { + return AvaloniaPropertyRegistry.Instance.FindRegistered(o, propertyName); + } + else + { + var split = propertyName.Split('.'); + + if (split.Length == 2) + { + // HACK: We need a way to resolve types here using something like IXamlTypeResolver. + // We don't currently have that so we have to make our best guess. + var type = split[0]; + var name = split[1]; + var registry = AvaloniaPropertyRegistry.Instance; + var registered = registry.GetRegisteredAttached(o.GetType()) + .Concat(registry.GetRegistered(o.GetType())); + + foreach (var p in registered) + { + if (p.Name == name && IsOfType(p.OwnerType, type)) + { + return p; + } + } + } + } + + return null; + } + + private static bool IsOfType(Type type, string typeName) + { + while (type != null) + { + if (type.Name == typeName) + { + return true; + } + + type = type.BaseType; + } + + return false; + } + private class Accessor : PropertyAccessorBase { private readonly WeakReference _reference; diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_AddOwner.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_AddOwner.cs new file mode 100644 index 0000000000..4e033be3fb --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_AddOwner.cs @@ -0,0 +1,52 @@ +// 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 System; +using Xunit; + +namespace Avalonia.Base.UnitTests +{ + public class AvaloniaObjectTests_AddOwner + { + [Fact] + public void AddOwnered_Property_Retains_Default_Value() + { + var target = new Class2(); + + Assert.Equal("foodefault", target.GetValue(Class2.FooProperty)); + } + + [Fact] + public void AddOwnered_Property_Does_Not_Retain_Validation() + { + var target = new Class2(); + + target.SetValue(Class2.FooProperty, "throw"); + } + + private class Class1 : AvaloniaObject + { + public static readonly StyledProperty FooProperty = + AvaloniaProperty.Register( + "Foo", + "foodefault", + validate: ValidateFoo); + + private static string ValidateFoo(AvaloniaObject arg1, string arg2) + { + if (arg2 == "throw") + { + throw new IndexOutOfRangeException(); + } + + return arg2; + } + } + + private class Class2 : AvaloniaObject + { + public static readonly StyledProperty FooProperty = + Class1.FooProperty.AddOwner(); + } + } +} diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs new file mode 100644 index 0000000000..acaabc73df --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs @@ -0,0 +1,52 @@ +// 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 System; +using Xunit; + +namespace Avalonia.Base.UnitTests +{ + public class AvaloniaObjectTests_Attached + { + [Fact] + public void AddOwnered_Property_Retains_Default_Value() + { + var target = new Class2(); + + Assert.Equal("foodefault", target.GetValue(Class2.FooProperty)); + } + + [Fact] + public void AddOwnered_Property_Retains_Validation() + { + var target = new Class2(); + + Assert.Throws(() => target.SetValue(Class2.FooProperty, "throw")); + } + + private class Class1 : AvaloniaObject + { + public static readonly AttachedProperty FooProperty = + AvaloniaProperty.RegisterAttached( + "Foo", + "foodefault", + validate: ValidateFoo); + + private static string ValidateFoo(AvaloniaObject arg1, string arg2) + { + if (arg2 == "throw") + { + throw new IndexOutOfRangeException(); + } + + return arg2; + } + } + + private class Class2 : AvaloniaObject + { + public static readonly AttachedProperty FooProperty = + Class1.FooProperty.AddOwner(); + } + } +} diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs index da0b0252a3..c030657034 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs @@ -4,12 +4,15 @@ using System.Linq; using System.Reactive.Linq; using Xunit; +using Xunit.Abstractions; namespace Avalonia.Base.UnitTests { public class AvaloniaPropertyRegistryTests { - public AvaloniaPropertyRegistryTests() + ITestOutputHelper s; + + public AvaloniaPropertyRegistryTests(ITestOutputHelper s) { // Ensure properties are registered. AvaloniaProperty p; @@ -25,7 +28,7 @@ namespace Avalonia.Base.UnitTests .Select(x => x.Name) .ToArray(); - Assert.Equal(new[] { "Foo", "Baz", "Qux", "Attached" }, names); + Assert.Equal(new[] { "Foo", "Baz", "Qux" }, names); } [Fact] @@ -35,61 +38,41 @@ namespace Avalonia.Base.UnitTests .Select(x => x.Name) .ToArray(); - Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux", "Attached" }, names); + Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux" }, names); } [Fact] - public void GetAttached_Returns_Registered_Properties_For_Base_Types() + public void GetRegisteredAttached_Returns_Registered_Properties() { - string[] names = AvaloniaPropertyRegistry.Instance.GetAttached(typeof(AttachedOwner)).Select(x => x.Name).ToArray(); + string[] names = AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(typeof(Class1)) + .Select(x => x.Name) + .ToArray(); Assert.Equal(new[] { "Attached" }, names); } [Fact] - public void FindRegistered_Finds_Untyped_Property() + public void GetRegisteredAttached_Returns_Registered_Properties_For_Base_Types() { - var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Foo"); + string[] names = AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(typeof(Class2)) + .Select(x => x.Name) + .ToArray(); - Assert.Equal(Class1.FooProperty, result); + Assert.Equal(new[] { "Attached" }, names); } [Fact] - public void FindRegistered_Finds_Typed_Property() + public void FindRegistered_Finds_Property() { - var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Class1.Foo"); + var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Foo"); Assert.Equal(Class1.FooProperty, result); } [Fact] - public void FindRegistered_Finds_Typed_Inherited_Property() - { - var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class2), "Class1.Foo"); - - Assert.Equal(Class2.FooProperty, result); - } - - [Fact] - public void FindRegistered_Finds_Inherited_Property_With_Derived_Type_Name() - { - var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class2), "Class2.Foo"); - - Assert.Equal(Class2.FooProperty, result); - } - - [Fact] - public void FindRegistered_Finds_Attached_Property() - { - var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class2), "AttachedOwner.Attached"); - - Assert.Equal(AttachedOwner.AttachedProperty, result); - } - - [Fact] - public void FindRegistered_Doesnt_Finds_Unqualified_Attached_Property() + public void FindRegistered_Doesnt_Find_Nonregistered_Property() { - var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class2), "Attached"); + var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Bar"); Assert.Null(result); } @@ -99,55 +82,34 @@ namespace Avalonia.Base.UnitTests { var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(AttachedOwner), "Attached"); - Assert.True(AttachedOwner.AttachedProperty == result); + Assert.Same(AttachedOwner.AttachedProperty, result); } [Fact] - public void FindRegistered_Finds_AddOwnered_Untyped_Attached_Property() + public void FindRegistered_Finds_AddOwnered_Attached_Property() { var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class3), "Attached"); - Assert.True(AttachedOwner.AttachedProperty == result); + Assert.Same(AttachedOwner.AttachedProperty, result); } [Fact] - public void FindRegistered_Finds_AddOwnered_Typed_Attached_Property() + public void FindRegistered_Doesnt_Find_Non_AddOwnered_Attached_Property() { - var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class3), "Class3.Attached"); - - Assert.True(AttachedOwner.AttachedProperty == result); - } - - [Fact] - public void FindRegistered_Finds_AddOwnered_AttachedTyped_Attached_Property() - { - var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class3), "AttachedOwner.Attached"); - - Assert.True(AttachedOwner.AttachedProperty == result); - } - - [Fact] - public void FindRegistered_Finds_AddOwnered_BaseTyped_Attached_Property() - { - var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class3), "Class1.Attached"); - - Assert.True(AttachedOwner.AttachedProperty == result); - } - - [Fact] - public void FindRegistered_Doesnt_Find_Nonregistered_Property() - { - var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Bar"); + var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class2), "Attached"); Assert.Null(result); } [Fact] - public void FindRegistered_Doesnt_Find_Nonregistered_Attached_Property() + public void FindRegisteredAttached_Finds_Property() { - var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class4), "AttachedOwner.Attached"); + var result = AvaloniaPropertyRegistry.Instance.FindRegisteredAttached( + typeof(Class1), + typeof(AttachedOwner), + "Attached"); - Assert.Null(result); + Assert.Equal(AttachedOwner.AttachedProperty, result); } private class Class1 : AvaloniaObject @@ -176,18 +138,18 @@ namespace Avalonia.Base.UnitTests private class Class3 : Class1 { - public static readonly StyledProperty AttachedProperty = + public static readonly AttachedProperty AttachedProperty = AttachedOwner.AttachedProperty.AddOwner(); } - public class Class4 : AvaloniaObject - { - } - private class AttachedOwner : Class1 { public static readonly AttachedProperty AttachedProperty = AvaloniaProperty.RegisterAttached("Attached"); } + + private class AttachedOwner2 : AttachedOwner + { + } } } From f0d0429644c3488050389618e1ae0c9d6ee92418 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 14 Apr 2018 23:21:34 +0200 Subject: [PATCH 04/16] Remove unused member. --- tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs index c030657034..8b2e500d37 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs @@ -10,8 +10,6 @@ namespace Avalonia.Base.UnitTests { public class AvaloniaPropertyRegistryTests { - ITestOutputHelper s; - public AvaloniaPropertyRegistryTests(ITestOutputHelper s) { // Ensure properties are registered. From b02ad2be485451127e197bd285c3854cf6c59390 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 14 Apr 2018 23:22:04 +0200 Subject: [PATCH 05/16] Fix failing tests. This class was registering a global attached property which affected other tests. --- tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs index acaabc73df..ab2a2d899d 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs @@ -27,7 +27,7 @@ namespace Avalonia.Base.UnitTests private class Class1 : AvaloniaObject { public static readonly AttachedProperty FooProperty = - AvaloniaProperty.RegisterAttached( + AvaloniaProperty.RegisterAttached( "Foo", "foodefault", validate: ValidateFoo); From 73ca7334ee880714926fea387f2e32c1814c806a Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Thu, 26 Apr 2018 18:39:28 +0200 Subject: [PATCH 06/16] Initial --- .../Data/BindingExpressionTests.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs index f5c0c6ec15..f42e0daf2a 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs @@ -58,7 +58,7 @@ namespace Avalonia.Markup.UnitTests.Data [Fact] public async Task Should_Convert_Get_String_To_Double() { - var data = new Class1 { StringValue = "5.6" }; + var data = new Class1 { StringValue = $"{5.6}" }; var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double)); var result = await target.Take(1); @@ -94,12 +94,12 @@ namespace Avalonia.Markup.UnitTests.Data [Fact] public void Should_Convert_Set_String_To_Double() { - var data = new Class1 { StringValue = (5.6).ToString() }; + var data = new Class1 { StringValue = $"{5.6}" }; var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double)); target.OnNext(6.7); - Assert.Equal((6.7).ToString(), data.StringValue); + Assert.Equal($"{6.7}", data.StringValue); GC.KeepAlive(data); } @@ -111,7 +111,7 @@ namespace Avalonia.Markup.UnitTests.Data var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string)); var result = await target.Take(1); - Assert.Equal((5.6).ToString(), result); + Assert.Equal($"{5.6}", result); GC.KeepAlive(data); } @@ -122,7 +122,7 @@ namespace Avalonia.Markup.UnitTests.Data var data = new Class1 { DoubleValue = 5.6 }; var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string)); - target.OnNext("6.7"); + target.OnNext($"{6.7}"); Assert.Equal(6.7, data.DoubleValue); @@ -318,15 +318,15 @@ namespace Avalonia.Markup.UnitTests.Data target.Subscribe(x => result.Add(x)); target.OnNext(1.2); - target.OnNext("3.4"); + target.OnNext($"{3.4}"); target.OnNext("bar"); Assert.Equal( new[] { - new BindingNotification("5.6"), - new BindingNotification("1.2"), - new BindingNotification("3.4"), + new BindingNotification($"{5.6}"), + new BindingNotification($"{1.2}"), + new BindingNotification($"{3.4}"), new BindingNotification( new InvalidCastException("'bar' is not a valid number."), BindingErrorType.Error) From 3bc303c4e8fc3a1906616e12782ef5c420eed0f0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 27 Apr 2018 13:12:28 +0100 Subject: [PATCH 07/16] implement menus and submenus obey screen extremities. --- src/Avalonia.Controls/ContextMenu.cs | 7 +++--- src/Avalonia.Controls/Primitives/Popup.cs | 18 +++++++++++++- src/Avalonia.Controls/Primitives/PopupRoot.cs | 24 +++++++++++++++++++ src/Avalonia.Themes.Default/MenuItem.xaml | 6 +++-- 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index fdb04f4ade..78dc994df7 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -19,7 +19,7 @@ namespace Avalonia.Controls { ContextMenuProperty.Changed.Subscribe(ContextMenuChanged); - MenuItem.ClickEvent.AddClassHandler(x => x.OnContextMenuClick, handledEventsToo: true); + MenuItem.ClickEvent.AddClassHandler(x => x.OnContextMenuClick, handledEventsToo: true); } /// @@ -75,13 +75,14 @@ namespace Avalonia.Controls { if (control != null) { - if(_popup == null) + if (_popup == null) { _popup = new Popup() { PlacementMode = PlacementMode.Pointer, PlacementTarget = control, - StaysOpen = false + StaysOpen = false, + ObeyScreenEdges = true }; _popup.Closed += PopupClosed; diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index b8817d28f3..d34e467133 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -40,6 +40,9 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty PlacementModeProperty = AvaloniaProperty.Register(nameof(PlacementMode), defaultValue: PlacementMode.Bottom); + public static readonly StyledProperty ObeyScreenEdgesProperty = + AvaloniaProperty.Register(nameof(ObeyScreenEdges)); + /// /// Defines the property. /// @@ -136,6 +139,12 @@ namespace Avalonia.Controls.Primitives set { SetValue(PlacementModeProperty, value); } } + public bool ObeyScreenEdges + { + get => GetValue(ObeyScreenEdgesProperty); + set => SetValue(ObeyScreenEdgesProperty, value); + } + /// /// Gets or sets the Horizontal offset of the popup in relation to the /// @@ -234,6 +243,11 @@ namespace Avalonia.Controls.Primitives _popupRoot.Show(); + if (ObeyScreenEdges) + { + _popupRoot.SnapInsideScreenEdges(); + } + _ignoreIsOpenChanged = true; IsOpen = true; _ignoreIsOpenChanged = false; @@ -346,8 +360,10 @@ namespace Avalonia.Controls.Primitives /// The popup's position in screen coordinates. protected virtual Point GetPosition() { - return GetPosition(PlacementTarget ?? this.GetVisualParent(), PlacementMode, PopupRoot, + var result = GetPosition(PlacementTarget ?? this.GetVisualParent(), PlacementMode, PopupRoot, HorizontalOffset, VerticalOffset); + + return result; } internal static Point GetPosition(Control target, PlacementMode placement, PopupRoot popupRoot, double horizontalOffset, double verticalOffset) diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 507a085fed..d0279200b6 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Linq; using Avalonia.Controls.Platform; using Avalonia.Controls.Presenters; using Avalonia.Interactivity; @@ -75,6 +76,29 @@ namespace Avalonia.Controls.Primitives /// public void Dispose() => PlatformImpl?.Dispose(); + /// + /// Moves the Popups position so that it doesnt overlap screen edges. + /// This method can be called immediately after Show has been called. + /// + public void SnapInsideScreenEdges() + { + var mainWindow = Window.OpenWindows.First(); + var screen = mainWindow.Screens.ScreenFromPoint(Position); + + var screenX = Position.X + Bounds.Width - screen.Bounds.X; + var screenY = Position.Y + Bounds.Height - screen.Bounds.Y; + + if (screenX > screen.Bounds.Width) + { + Position = Position.WithX(Position.X - (screenX - screen.Bounds.Width)); + } + + if (screenY > screen.Bounds.Height) + { + Position = Position.WithY(Position.Y - (screenY - screen.Bounds.Height)); + } + } + /// protected override void OnTemplateApplied(TemplateAppliedEventArgs e) { diff --git a/src/Avalonia.Themes.Default/MenuItem.xaml b/src/Avalonia.Themes.Default/MenuItem.xaml index 66f226d2f6..efb31175fa 100644 --- a/src/Avalonia.Themes.Default/MenuItem.xaml +++ b/src/Avalonia.Themes.Default/MenuItem.xaml @@ -45,7 +45,8 @@ + IsOpen="{TemplateBinding Path=IsSubMenuOpen, Mode=TwoWay}" + ObeyScreenEdges="True"> @@ -92,7 +93,8 @@ + StaysOpen="True" + ObeyScreenEdges="True"> From 89767c8b78d122056d9a6827a6384be795a4e1ea Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sat, 28 Apr 2018 09:43:03 +0100 Subject: [PATCH 08/16] add comments to new properties. --- src/Avalonia.Controls/Primitives/Popup.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index d34e467133..5483815522 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -39,7 +39,10 @@ namespace Avalonia.Controls.Primitives /// public static readonly StyledProperty PlacementModeProperty = AvaloniaProperty.Register(nameof(PlacementMode), defaultValue: PlacementMode.Bottom); - + + /// + /// Degines the property. + /// public static readonly StyledProperty ObeyScreenEdgesProperty = AvaloniaProperty.Register(nameof(ObeyScreenEdges)); @@ -139,6 +142,10 @@ namespace Avalonia.Controls.Primitives set { SetValue(PlacementModeProperty, value); } } + /// + /// Gets or sets a value indicating whether the popup positions itself within the nearest screen boundary + /// when its opened at a position where it would otherwise overlap the screen edge. + /// public bool ObeyScreenEdges { get => GetValue(ObeyScreenEdgesProperty); From 240bb40be4075aee7efa68a829bcd8852517c573 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sat, 28 Apr 2018 09:45:08 +0100 Subject: [PATCH 09/16] whitespace fixes. --- src/Avalonia.Controls/Primitives/Popup.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 5483815522..56e09b7db6 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -39,7 +39,7 @@ namespace Avalonia.Controls.Primitives /// public static readonly StyledProperty PlacementModeProperty = AvaloniaProperty.Register(nameof(PlacementMode), defaultValue: PlacementMode.Bottom); - + /// /// Degines the property. /// @@ -232,12 +232,12 @@ namespace Avalonia.Controls.Primitives var window = _topLevel as Window; if (window != null) { - window.Deactivated += WindowDeactivated; + window.Deactivated += WindowDeactivated; } else { var parentPopuproot = _topLevel as PopupRoot; - if(parentPopuproot != null && parentPopuproot.Parent!=null) + if (parentPopuproot != null && parentPopuproot.Parent != null) { ((Popup)(parentPopuproot.Parent)).Closed += ParentClosed; } @@ -261,7 +261,7 @@ namespace Avalonia.Controls.Primitives Opened?.Invoke(this, EventArgs.Empty); } - + /// /// Closes the popup. /// @@ -367,7 +367,7 @@ namespace Avalonia.Controls.Primitives /// The popup's position in screen coordinates. protected virtual Point GetPosition() { - var result = GetPosition(PlacementTarget ?? this.GetVisualParent(), PlacementMode, PopupRoot, + var result = GetPosition(PlacementTarget ?? this.GetVisualParent(), PlacementMode, PopupRoot, HorizontalOffset, VerticalOffset); return result; @@ -422,8 +422,8 @@ namespace Avalonia.Controls.Primitives { if (!StaysOpen) { - if(!IsChildOrThis((IVisual)e.Source)) - { + if (!IsChildOrThis((IVisual)e.Source)) + { Close(); e.Handled = true; } @@ -435,12 +435,12 @@ namespace Avalonia.Controls.Primitives IVisual root = child.GetVisualRoot(); while (root is PopupRoot) { - if (root == PopupRoot) return true; + if (root == PopupRoot) return true; root = ((PopupRoot)root).Parent.GetVisualRoot(); } return false; } - + private void WindowDeactivated(object sender, EventArgs e) { if (!StaysOpen) From 35b4913c0bfe712045a6ef340daebea721f8aa14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Sat, 28 Apr 2018 18:13:44 +0100 Subject: [PATCH 10/16] Removed AvaloniaWin32Exception usings. --- src/Windows/Avalonia.Win32/ScreenImpl.cs | 7 ------- src/Windows/Avalonia.Win32/Win32Platform.cs | 18 +++++++----------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 113b2811dc..e1df24151d 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -2,16 +2,9 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Linq; -using Avalonia.Controls; using Avalonia.Platform; -using Avalonia.Utilities; using static Avalonia.Win32.Interop.UnmanagedMethods; -#if NETSTANDARD -using Win32Exception = Avalonia.Win32.NetStandard.AvaloniaWin32Exception; -#endif - namespace Avalonia.Win32 { public class ScreenImpl : IScreenImpl diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 902abaf65b..95077f82a1 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -1,27 +1,23 @@ // 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.Input.Platform; using System; using System.Collections.Generic; -using System.Reactive.Disposables; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reactive.Disposables; using System.Runtime.InteropServices; using System.Threading; +using Avalonia.Controls; using Avalonia.Controls.Platform; using Avalonia.Input; +using Avalonia.Input.Platform; using Avalonia.Platform; -using Avalonia.Win32.Input; -using Avalonia.Win32.Interop; -using Avalonia.Controls; using Avalonia.Rendering; using Avalonia.Threading; -using System.IO; -#if NETSTANDARD -using Win32Exception = Avalonia.Win32.NetStandard.AvaloniaWin32Exception; -#else -using System.ComponentModel; -#endif +using Avalonia.Win32.Input; +using Avalonia.Win32.Interop; namespace Avalonia { From c1d6fe66864e797f486e7f4b1609438b270b0330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Sat, 28 Apr 2018 22:45:11 +0100 Subject: [PATCH 11/16] Fixed possible NullReferenceException in DefaultRenderLoop. --- src/Avalonia.Visuals/Rendering/DefaultRenderLoop.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/DefaultRenderLoop.cs b/src/Avalonia.Visuals/Rendering/DefaultRenderLoop.cs index 5dff3715b3..9cf849f59b 100644 --- a/src/Avalonia.Visuals/Rendering/DefaultRenderLoop.cs +++ b/src/Avalonia.Visuals/Rendering/DefaultRenderLoop.cs @@ -41,12 +41,12 @@ namespace Avalonia.Rendering { add { + _tick += value; + if (_subscriberCount++ == 0) { Start(); } - - _tick += value; } remove From 6bb25bea93a20657c441ed6b2b0e3a53a01115c7 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 30 Apr 2018 10:22:43 +0100 Subject: [PATCH 12/16] fix typo. --- src/Avalonia.Controls/Primitives/Popup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 56e09b7db6..656f3890cd 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -41,7 +41,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(PlacementMode), defaultValue: PlacementMode.Bottom); /// - /// Degines the property. + /// Defines the property. /// public static readonly StyledProperty ObeyScreenEdgesProperty = AvaloniaProperty.Register(nameof(ObeyScreenEdges)); From fd8e1ecba7e52abfe72cfa37af8d2921d72763c4 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 30 Apr 2018 20:26:10 +0100 Subject: [PATCH 13/16] walk tree to find window. --- src/Avalonia.Controls/Primitives/PopupRoot.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index d0279200b6..457a7bd4b4 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -7,6 +7,7 @@ using Avalonia.Controls.Platform; using Avalonia.Controls.Presenters; using Avalonia.Interactivity; using Avalonia.Layout; +using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Styling; @@ -82,8 +83,9 @@ namespace Avalonia.Controls.Primitives /// public void SnapInsideScreenEdges() { - var mainWindow = Window.OpenWindows.First(); - var screen = mainWindow.Screens.ScreenFromPoint(Position); + var window = this.GetSelfAndLogicalAncestors().OfType().First(); + + var screen = window.Screens.ScreenFromPoint(Position); var screenX = Position.X + Bounds.Width - screen.Bounds.X; var screenY = Position.Y + Bounds.Height - screen.Bounds.Y; From eb273c0fcbe3a4b6e396b280afd3f6c568a0167a Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 2 May 2018 16:49:57 +0200 Subject: [PATCH 14/16] Inital --- src/Avalonia.Controls/ColumnDefinitions.cs | 2 +- src/Avalonia.Controls/GridLength.cs | 13 +++--- src/Avalonia.Controls/RowDefinitions.cs | 2 +- src/Avalonia.Visuals/CornerRadius.cs | 40 +++++++++---------- src/Avalonia.Visuals/Matrix.cs | 4 +- src/Avalonia.Visuals/Point.cs | 5 +-- src/Avalonia.Visuals/Rect.cs | 5 +-- src/Avalonia.Visuals/RelativePoint.cs | 9 ++--- src/Avalonia.Visuals/RelativeRect.cs | 12 +++--- src/Avalonia.Visuals/Size.cs | 5 +-- src/Avalonia.Visuals/Thickness.cs | 24 ++++++----- .../Converters/CornerRadiusTypeConverter.cs | 2 +- .../Converters/GridLengthTypeConverter.cs | 2 +- .../Converters/MatrixTypeConverter.cs | 2 +- .../Converters/PointTypeConverter.cs | 2 +- .../Converters/PointsListTypeConverter.cs | 2 +- .../Converters/RectTypeConverter.cs | 2 +- .../Converters/RelativePointTypeConverter.cs | 2 +- .../Converters/RelativeRectTypeConverter.cs | 2 +- .../Converters/SizeTypeConverter.cs | 2 +- .../Converters/ThicknessTypeConverter.cs | 2 +- .../GridLengthTests.cs | 18 ++++----- .../CornerRadiusTests.cs | 8 ++-- .../Media/MatrixTests.cs | 2 +- .../Media/RectTests.cs | 2 +- .../RelativePointTests.cs | 4 +- .../RelativeRectTests.cs | 6 +-- .../ThicknessTests.cs | 8 ++-- 28 files changed, 91 insertions(+), 98 deletions(-) diff --git a/src/Avalonia.Controls/ColumnDefinitions.cs b/src/Avalonia.Controls/ColumnDefinitions.cs index 32fe5a6034..972655262a 100644 --- a/src/Avalonia.Controls/ColumnDefinitions.cs +++ b/src/Avalonia.Controls/ColumnDefinitions.cs @@ -27,7 +27,7 @@ namespace Avalonia.Controls public ColumnDefinitions(string s) : this() { - AddRange(GridLength.ParseLengths(s, CultureInfo.InvariantCulture).Select(x => new ColumnDefinition(x))); + AddRange(GridLength.ParseLengths(s).Select(x => new ColumnDefinition(x))); } } } \ No newline at end of file diff --git a/src/Avalonia.Controls/GridLength.cs b/src/Avalonia.Controls/GridLength.cs index 789953a249..8a079b9837 100644 --- a/src/Avalonia.Controls/GridLength.cs +++ b/src/Avalonia.Controls/GridLength.cs @@ -180,9 +180,8 @@ namespace Avalonia.Controls /// Parses a string to return a . /// /// The string. - /// The current culture. /// The . - public static GridLength Parse(string s, CultureInfo culture) + public static GridLength Parse(string s) { s = s.ToUpperInvariant(); @@ -193,12 +192,12 @@ namespace Avalonia.Controls else if (s.EndsWith("*")) { var valueString = s.Substring(0, s.Length - 1).Trim(); - var value = valueString.Length > 0 ? double.Parse(valueString, culture) : 1; + var value = valueString.Length > 0 ? double.Parse(valueString, CultureInfo.InvariantCulture) : 1; return new GridLength(value, GridUnitType.Star); } else { - var value = double.Parse(s, culture); + var value = double.Parse(s, CultureInfo.InvariantCulture); return new GridLength(value, GridUnitType.Pixel); } } @@ -209,13 +208,13 @@ namespace Avalonia.Controls /// The string. /// The current culture. /// The . - public static IEnumerable ParseLengths(string s, CultureInfo culture) + public static IEnumerable ParseLengths(string s) { - using (var tokenizer = new StringTokenizer(s, culture)) + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture)) { while (tokenizer.TryReadString(out var item)) { - yield return Parse(item, culture); + yield return Parse(item); } } } diff --git a/src/Avalonia.Controls/RowDefinitions.cs b/src/Avalonia.Controls/RowDefinitions.cs index e677492580..2dfad7111a 100644 --- a/src/Avalonia.Controls/RowDefinitions.cs +++ b/src/Avalonia.Controls/RowDefinitions.cs @@ -27,7 +27,7 @@ namespace Avalonia.Controls public RowDefinitions(string s) : this() { - AddRange(GridLength.ParseLengths(s, CultureInfo.InvariantCulture).Select(x => new RowDefinition(x))); + AddRange(GridLength.ParseLengths(s).Select(x => new RowDefinition(x))); } } } \ No newline at end of file diff --git a/src/Avalonia.Visuals/CornerRadius.cs b/src/Avalonia.Visuals/CornerRadius.cs index db0ac97d49..33a553d477 100644 --- a/src/Avalonia.Visuals/CornerRadius.cs +++ b/src/Avalonia.Visuals/CornerRadius.cs @@ -4,6 +4,7 @@ using System; using System.Globalization; using System.Linq; +using Avalonia.Utilities; namespace Avalonia { @@ -53,31 +54,26 @@ namespace Avalonia return $"{TopLeft},{TopRight},{BottomRight},{BottomLeft}"; } - public static CornerRadius Parse(string s, CultureInfo culture) + public static CornerRadius Parse(string s) { - var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim()) - .ToList(); - - switch (parts.Count) + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Thickness")) { - case 1: - var uniform = double.Parse(parts[0], culture); - return new CornerRadius(uniform); - case 2: - var top = double.Parse(parts[0], culture); - var bottom = double.Parse(parts[1], culture); - return new CornerRadius(top, bottom); - case 4: - var topLeft = double.Parse(parts[0], culture); - var topRight = double.Parse(parts[1], culture); - var bottomRight = double.Parse(parts[2], culture); - var bottomLeft = double.Parse(parts[3], culture); - return new CornerRadius(topLeft, topRight, bottomRight, bottomLeft); - default: + if (tokenizer.TryReadDouble(out var a)) + { + if (tokenizer.TryReadDouble(out var b)) { - throw new FormatException("Invalid CornerRadius."); + if (tokenizer.TryReadDouble(out var c)) + { + return new CornerRadius(a, b, c, tokenizer.ReadDouble()); + } + + return new CornerRadius(a, b); } + + return new CornerRadius(a); + } + + throw new FormatException("Invalid CornerRadius."); } } @@ -85,7 +81,7 @@ namespace Avalonia { return cr1.TopLeft.Equals(cr2.TopLeft) && cr1.TopRight.Equals(cr2.TopRight) - && cr1.BottomRight.Equals(cr2.BottomRight) + && cr1.BottomRight.Equals(cr2.BottomRight) && cr1.BottomLeft.Equals(cr2.BottomLeft); } diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 70804ee04f..4336d24de8 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -316,9 +316,9 @@ namespace Avalonia /// The string. /// The current culture. /// The . - public static Matrix Parse(string s, CultureInfo culture) + public static Matrix Parse(string s) { - using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Matrix")) + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Matrix")) { return new Matrix( tokenizer.ReadDouble(), diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Visuals/Point.cs index d0c3edfeb2..62be3bf276 100644 --- a/src/Avalonia.Visuals/Point.cs +++ b/src/Avalonia.Visuals/Point.cs @@ -170,11 +170,10 @@ namespace Avalonia /// Parses a string. /// /// The string. - /// The current culture. /// The . - public static Point Parse(string s, CultureInfo culture) + public static Point Parse(string s) { - using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Point")) + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Point")) { return new Point( tokenizer.ReadDouble(), diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Visuals/Rect.cs index 748928ada3..73021ca29a 100644 --- a/src/Avalonia.Visuals/Rect.cs +++ b/src/Avalonia.Visuals/Rect.cs @@ -487,11 +487,10 @@ namespace Avalonia /// Parses a string. /// /// The string. - /// The current culture. /// The parsed . - public static Rect Parse(string s, CultureInfo culture) + public static Rect Parse(string s) { - using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Rect")) + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Rect")) { return new Rect( tokenizer.ReadDouble(), diff --git a/src/Avalonia.Visuals/RelativePoint.cs b/src/Avalonia.Visuals/RelativePoint.cs index a2ef0e6725..e4d7ea05cb 100644 --- a/src/Avalonia.Visuals/RelativePoint.cs +++ b/src/Avalonia.Visuals/RelativePoint.cs @@ -154,11 +154,10 @@ namespace Avalonia /// Parses a string. /// /// The string. - /// The current culture. /// The parsed . - public static RelativePoint Parse(string s, CultureInfo culture) + public static RelativePoint Parse(string s) { - using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid RelativePoint")) + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid RelativePoint")) { var x = tokenizer.ReadString(); var y = tokenizer.ReadString(); @@ -180,8 +179,8 @@ namespace Avalonia } return new RelativePoint( - double.Parse(x, culture) * scale, - double.Parse(y, culture) * scale, + double.Parse(x, CultureInfo.InvariantCulture) * scale, + double.Parse(y, CultureInfo.InvariantCulture) * scale, unit); } } diff --git a/src/Avalonia.Visuals/RelativeRect.cs b/src/Avalonia.Visuals/RelativeRect.cs index c13c3282db..6929a8f71e 100644 --- a/src/Avalonia.Visuals/RelativeRect.cs +++ b/src/Avalonia.Visuals/RelativeRect.cs @@ -169,9 +169,9 @@ namespace Avalonia /// The string. /// The current culture. /// The parsed . - public static RelativeRect Parse(string s, CultureInfo culture) + public static RelativeRect Parse(string s) { - using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid RelativeRect")) + using (var tokenizer = new StringTokenizer(s, exceptionMessage: "Invalid RelativeRect")) { var x = tokenizer.ReadString(); var y = tokenizer.ReadString(); @@ -202,10 +202,10 @@ namespace Avalonia } return new RelativeRect( - double.Parse(x, culture) * scale, - double.Parse(y, culture) * scale, - double.Parse(width, culture) * scale, - double.Parse(height, culture) * scale, + double.Parse(x, CultureInfo.InvariantCulture) * scale, + double.Parse(y, CultureInfo.InvariantCulture) * scale, + double.Parse(width, CultureInfo.InvariantCulture) * scale, + double.Parse(height, CultureInfo.InvariantCulture) * scale, unit); } } diff --git a/src/Avalonia.Visuals/Size.cs b/src/Avalonia.Visuals/Size.cs index c5eaa33b41..b6889af6f0 100644 --- a/src/Avalonia.Visuals/Size.cs +++ b/src/Avalonia.Visuals/Size.cs @@ -150,11 +150,10 @@ namespace Avalonia /// Parses a string. /// /// The string. - /// The current culture. /// The . - public static Size Parse(string s, CultureInfo culture) + public static Size Parse(string s) { - using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Size")) + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Size")) { return new Size( tokenizer.ReadDouble(), diff --git a/src/Avalonia.Visuals/Thickness.cs b/src/Avalonia.Visuals/Thickness.cs index a5ca0a04a8..43a5fed9e7 100644 --- a/src/Avalonia.Visuals/Thickness.cs +++ b/src/Avalonia.Visuals/Thickness.cs @@ -165,25 +165,27 @@ namespace Avalonia /// Parses a string. /// /// The string. - /// The current culture. /// The . - public static Thickness Parse(string s, CultureInfo culture) + public static Thickness Parse(string s) { - using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Thickness")) + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Thickness")) { - var a = tokenizer.ReadDouble(); - - if (tokenizer.TryReadDouble(out var b)) + if(tokenizer.TryReadDouble(out var a)) { - if (tokenizer.TryReadDouble(out var c)) + if (tokenizer.TryReadDouble(out var b)) { - return new Thickness(a, b, c, tokenizer.ReadDouble()); + if (tokenizer.TryReadDouble(out var c)) + { + return new Thickness(a, b, c, tokenizer.ReadDouble()); + } + + return new Thickness(a, b); } - return new Thickness(a, b); + return new Thickness(a); } - - return new Thickness(a); + + throw new FormatException("Invalid Thickness."); } } diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/CornerRadiusTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/CornerRadiusTypeConverter.cs index 5da0efae1b..d8c1ecd4eb 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/CornerRadiusTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/CornerRadiusTypeConverter.cs @@ -13,7 +13,7 @@ namespace Avalonia.Markup.Xaml.Converters public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - return CornerRadius.Parse((string)value, culture); + return CornerRadius.Parse((string)value); } } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/GridLengthTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/GridLengthTypeConverter.cs index 1f72ca325c..05f3bed04d 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/GridLengthTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/GridLengthTypeConverter.cs @@ -18,7 +18,7 @@ namespace Avalonia.Markup.Xaml.Converters public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - return GridLength.Parse((string)value, culture); + return GridLength.Parse((string)value); } } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs index c477ff5637..fec6e31771 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs @@ -17,7 +17,7 @@ namespace Avalonia.Markup.Xaml.Converters public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - return Matrix.Parse((string)value, culture); + return Matrix.Parse((string)value); } } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/PointTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/PointTypeConverter.cs index 1381fe7a75..9ca7212c68 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/PointTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/PointTypeConverter.cs @@ -17,7 +17,7 @@ namespace Avalonia.Markup.Xaml.Converters public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - return Point.Parse((string)value, culture); + return Point.Parse((string)value); } } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/PointsListTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/PointsListTypeConverter.cs index b6c6da3055..29c7dbfd39 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/PointsListTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/PointsListTypeConverter.cs @@ -23,7 +23,7 @@ namespace Avalonia.Markup.Xaml.Converters var result = new List(pointStrs.Length); foreach (var pointStr in pointStrs) { - result.Add(Point.Parse(pointStr, culture)); + result.Add(Point.Parse(pointStr)); } return result; diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs index c9c6462f89..0946f95938 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs @@ -17,7 +17,7 @@ namespace Avalonia.Markup.Xaml.Converters public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - return Rect.Parse((string)value, culture); + return Rect.Parse((string)value); } } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/RelativePointTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/RelativePointTypeConverter.cs index f68b8d66e6..b3178f1496 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/RelativePointTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/RelativePointTypeConverter.cs @@ -17,7 +17,7 @@ namespace Avalonia.Markup.Xaml.Converters public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - return RelativePoint.Parse((string)value, culture); + return RelativePoint.Parse((string)value); } } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/RelativeRectTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/RelativeRectTypeConverter.cs index 64e39e224a..38c2833815 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/RelativeRectTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/RelativeRectTypeConverter.cs @@ -17,7 +17,7 @@ namespace Avalonia.Markup.Xaml.Converters public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - return RelativeRect.Parse((string)value, culture); + return RelativeRect.Parse((string)value); } } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/SizeTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/SizeTypeConverter.cs index 73fef9ab1f..535e7948d5 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/SizeTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/SizeTypeConverter.cs @@ -17,7 +17,7 @@ namespace Avalonia.Markup.Xaml.Converters public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - return Size.Parse((string)value, culture); + return Size.Parse((string)value); } } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/ThicknessTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/ThicknessTypeConverter.cs index 6ca5ec2f66..3a7652a153 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/ThicknessTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/ThicknessTypeConverter.cs @@ -17,7 +17,7 @@ namespace Avalonia.Markup.Xaml.Converters public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - return Thickness.Parse((string)value, culture); + return Thickness.Parse((string)value); } } } \ No newline at end of file diff --git a/tests/Avalonia.Controls.UnitTests/GridLengthTests.cs b/tests/Avalonia.Controls.UnitTests/GridLengthTests.cs index 0a811333d4..ab4da0ca7e 100644 --- a/tests/Avalonia.Controls.UnitTests/GridLengthTests.cs +++ b/tests/Avalonia.Controls.UnitTests/GridLengthTests.cs @@ -13,7 +13,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Parse_Should_Parse_Auto() { - var result = GridLength.Parse("Auto", CultureInfo.InvariantCulture); + var result = GridLength.Parse("Auto"); Assert.Equal(GridLength.Auto, result); } @@ -21,7 +21,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Parse_Should_Parse_Auto_Lowercase() { - var result = GridLength.Parse("auto", CultureInfo.InvariantCulture); + var result = GridLength.Parse("auto"); Assert.Equal(GridLength.Auto, result); } @@ -29,7 +29,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Parse_Should_Parse_Star() { - var result = GridLength.Parse("*", CultureInfo.InvariantCulture); + var result = GridLength.Parse("*"); Assert.Equal(new GridLength(1, GridUnitType.Star), result); } @@ -37,7 +37,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Parse_Should_Parse_Star_Value() { - var result = GridLength.Parse("2*", CultureInfo.InvariantCulture); + var result = GridLength.Parse("2*"); Assert.Equal(new GridLength(2, GridUnitType.Star), result); } @@ -45,7 +45,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Parse_Should_Parse_Pixel_Value() { - var result = GridLength.Parse("2", CultureInfo.InvariantCulture); + var result = GridLength.Parse("2"); Assert.Equal(new GridLength(2, GridUnitType.Pixel), result); } @@ -53,13 +53,13 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Parse_Should_Throw_FormatException_For_Invalid_String() { - Assert.Throws(() => GridLength.Parse("2x", CultureInfo.InvariantCulture)); + Assert.Throws(() => GridLength.Parse("2x")); } [Fact] public void ParseLengths_Accepts_Comma_Separators() { - var result = GridLength.ParseLengths("*,Auto,2*,4", CultureInfo.InvariantCulture).ToList(); + var result = GridLength.ParseLengths("*,Auto,2*,4").ToList(); Assert.Equal( new[] @@ -75,7 +75,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void ParseLengths_Accepts_Space_Separators() { - var result = GridLength.ParseLengths("* Auto 2* 4", CultureInfo.InvariantCulture).ToList(); + var result = GridLength.ParseLengths("* Auto 2* 4").ToList(); Assert.Equal( new[] @@ -91,7 +91,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void ParseLengths_Accepts_Comma_Separators_With_Spaces() { - var result = GridLength.ParseLengths("*, Auto, 2* ,4", CultureInfo.InvariantCulture).ToList(); + var result = GridLength.ParseLengths("*, Auto, 2* ,4").ToList(); Assert.Equal( new[] diff --git a/tests/Avalonia.Visuals.UnitTests/CornerRadiusTests.cs b/tests/Avalonia.Visuals.UnitTests/CornerRadiusTests.cs index bc0bbdc867..56f9907409 100644 --- a/tests/Avalonia.Visuals.UnitTests/CornerRadiusTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/CornerRadiusTests.cs @@ -11,7 +11,7 @@ namespace Avalonia.Visuals.UnitTests [Fact] public void Parse_Parses_Single_Uniform_Radius() { - var result = CornerRadius.Parse("3.4", CultureInfo.InvariantCulture); + var result = CornerRadius.Parse("3.4"); Assert.Equal(new CornerRadius(3.4), result); } @@ -19,7 +19,7 @@ namespace Avalonia.Visuals.UnitTests [Fact] public void Parse_Parses_Top_Bottom() { - var result = CornerRadius.Parse("1.1,2.2", CultureInfo.InvariantCulture); + var result = CornerRadius.Parse("1.1,2.2"); Assert.Equal(new CornerRadius(1.1, 2.2), result); } @@ -27,7 +27,7 @@ namespace Avalonia.Visuals.UnitTests [Fact] public void Parse_Parses_TopLeft_TopRight_BottomRight_BottomLeft() { - var result = CornerRadius.Parse("1.1,2.2,3.3,4.4", CultureInfo.InvariantCulture); + var result = CornerRadius.Parse("1.1,2.2,3.3,4.4"); Assert.Equal(new CornerRadius(1.1, 2.2, 3.3, 4.4), result); } @@ -35,7 +35,7 @@ namespace Avalonia.Visuals.UnitTests [Fact] public void Parse_Accepts_Spaces() { - var result = CornerRadius.Parse("1.1 2.2 3.3 4.4", CultureInfo.InvariantCulture); + var result = CornerRadius.Parse("1.1 2.2 3.3 4.4"); Assert.Equal(new CornerRadius(1.1, 2.2, 3.3, 4.4), result); } diff --git a/tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs index 4c1e361952..ff1d17164e 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs @@ -8,7 +8,7 @@ namespace Avalonia.Visuals.UnitTests.Media [Fact] public void Parse_Parses() { - var matrix = Matrix.Parse("1,2,3,-4,5 6", CultureInfo.CurrentCulture); + var matrix = Matrix.Parse("1,2,3,-4,5 6"); var expected = new Matrix(1, 2, 3, -4, 5, 6); Assert.Equal(expected, matrix); } diff --git a/tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs index 12070bfed3..cd0c7e1ace 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs @@ -8,7 +8,7 @@ namespace Avalonia.Visuals.UnitTests.Media [Fact] public void Parse_Parses() { - var rect = Rect.Parse("1,2 3,-4", CultureInfo.CurrentCulture); + var rect = Rect.Parse("1,2 3,-4"); var expected = new Rect(1, 2, 3, -4); Assert.Equal(expected, rect); } diff --git a/tests/Avalonia.Visuals.UnitTests/RelativePointTests.cs b/tests/Avalonia.Visuals.UnitTests/RelativePointTests.cs index f4a21fb6b4..b9eecc809f 100644 --- a/tests/Avalonia.Visuals.UnitTests/RelativePointTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/RelativePointTests.cs @@ -11,7 +11,7 @@ namespace Avalonia.Visuals.UnitTests [Fact] public void Parse_Should_Accept_Absolute_Value() { - var result = RelativePoint.Parse("4,5", CultureInfo.InvariantCulture); + var result = RelativePoint.Parse("4,5"); Assert.Equal(new RelativePoint(4, 5, RelativeUnit.Absolute), result); } @@ -19,7 +19,7 @@ namespace Avalonia.Visuals.UnitTests [Fact] public void Parse_Should_Accept_Relative_Value() { - var result = RelativePoint.Parse("25%, 50%", CultureInfo.InvariantCulture); + var result = RelativePoint.Parse("25%, 50%"); Assert.Equal(new RelativePoint(0.25, 0.5, RelativeUnit.Relative), result); } diff --git a/tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs b/tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs index 9f25dcd413..68a0df2d9b 100644 --- a/tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs @@ -14,7 +14,7 @@ namespace Avalonia.Visuals.UnitTests [Fact] public void Parse_Should_Accept_Absolute_Value() { - var result = RelativeRect.Parse("4,5,50,60", CultureInfo.InvariantCulture); + var result = RelativeRect.Parse("4,5,50,60"); Assert.Equal(new RelativeRect(4, 5, 50, 60, RelativeUnit.Absolute), result, Compare); } @@ -22,7 +22,7 @@ namespace Avalonia.Visuals.UnitTests [Fact] public void Parse_Should_Accept_Relative_Value() { - var result = RelativeRect.Parse("10%, 20%, 40%, 70%", CultureInfo.InvariantCulture); + var result = RelativeRect.Parse("10%, 20%, 40%, 70%"); Assert.Equal(new RelativeRect(0.1, 0.2, 0.4, 0.7, RelativeUnit.Relative), result, Compare); } @@ -31,7 +31,7 @@ namespace Avalonia.Visuals.UnitTests public void Parse_Should_Throw_Mixed_Values() { Assert.Throws(() => - RelativeRect.Parse("10%, 20%, 40, 70%", CultureInfo.InvariantCulture)); + RelativeRect.Parse("10%, 20%, 40, 70%")); } } } diff --git a/tests/Avalonia.Visuals.UnitTests/ThicknessTests.cs b/tests/Avalonia.Visuals.UnitTests/ThicknessTests.cs index 03bf395d1e..ac4c6bc781 100644 --- a/tests/Avalonia.Visuals.UnitTests/ThicknessTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/ThicknessTests.cs @@ -11,7 +11,7 @@ namespace Avalonia.Visuals.UnitTests [Fact] public void Parse_Parses_Single_Uniform_Size() { - var result = Thickness.Parse("1.2", CultureInfo.InvariantCulture); + var result = Thickness.Parse("1.2"); Assert.Equal(new Thickness(1.2), result); } @@ -19,7 +19,7 @@ namespace Avalonia.Visuals.UnitTests [Fact] public void Parse_Parses_Horizontal_Vertical() { - var result = Thickness.Parse("1.2,3.4", CultureInfo.InvariantCulture); + var result = Thickness.Parse("1.2,3.4"); Assert.Equal(new Thickness(1.2, 3.4), result); } @@ -27,7 +27,7 @@ namespace Avalonia.Visuals.UnitTests [Fact] public void Parse_Parses_Left_Top_Right_Bottom() { - var result = Thickness.Parse("1.2, 3.4, 5, 6", CultureInfo.InvariantCulture); + var result = Thickness.Parse("1.2, 3.4, 5, 6"); Assert.Equal(new Thickness(1.2, 3.4, 5, 6), result); } @@ -35,7 +35,7 @@ namespace Avalonia.Visuals.UnitTests [Fact] public void Parse_Accepts_Spaces() { - var result = Thickness.Parse("1.2 3.4 5 6", CultureInfo.InvariantCulture); + var result = Thickness.Parse("1.2 3.4 5 6"); Assert.Equal(new Thickness(1.2, 3.4, 5, 6), result); } From 4e4c49b1e32db6b74dae1cf93a45d3e33888db47 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 2 May 2018 16:53:12 +0200 Subject: [PATCH 15/16] fix comments --- src/Avalonia.Visuals/Matrix.cs | 1 - src/Avalonia.Visuals/RelativeRect.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 4336d24de8..b09ea2b68c 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -314,7 +314,6 @@ namespace Avalonia /// Parses a string. /// /// The string. - /// The current culture. /// The . public static Matrix Parse(string s) { diff --git a/src/Avalonia.Visuals/RelativeRect.cs b/src/Avalonia.Visuals/RelativeRect.cs index 6929a8f71e..ad42e30057 100644 --- a/src/Avalonia.Visuals/RelativeRect.cs +++ b/src/Avalonia.Visuals/RelativeRect.cs @@ -167,7 +167,6 @@ namespace Avalonia /// Parses a string. /// /// The string. - /// The current culture. /// The parsed . public static RelativeRect Parse(string s) { From f2e01704b2c48406dfadd9f1eea132375e2081b1 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 2 May 2018 19:35:38 +0200 Subject: [PATCH 16/16] comment removed --- src/Avalonia.Controls/GridLength.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Controls/GridLength.cs b/src/Avalonia.Controls/GridLength.cs index 8a079b9837..608879812c 100644 --- a/src/Avalonia.Controls/GridLength.cs +++ b/src/Avalonia.Controls/GridLength.cs @@ -206,7 +206,6 @@ namespace Avalonia.Controls /// Parses a string to return a collection of s. /// /// The string. - /// The current culture. /// The . public static IEnumerable ParseLengths(string s) {