diff --git a/samples/XamlTestApplicationPcl/Views/MainWindow.paml b/samples/XamlTestApplicationPcl/Views/MainWindow.paml index 3dfac47f36..69c612f526 100644 --- a/samples/XamlTestApplicationPcl/Views/MainWindow.paml +++ b/samples/XamlTestApplicationPcl/Views/MainWindow.paml @@ -5,9 +5,9 @@ Title="Perspex Test Application" Height="350" Width="525" SizeToContent="WidthAndHeight" > - + diff --git a/src/Markup/Perspex.Markup/Binding/ExpressionSubject.cs b/src/Markup/Perspex.Markup/Binding/ExpressionSubject.cs index 7cddb93cca..b70df24877 100644 --- a/src/Markup/Perspex.Markup/Binding/ExpressionSubject.cs +++ b/src/Markup/Perspex.Markup/Binding/ExpressionSubject.cs @@ -5,6 +5,7 @@ using System; using System.Globalization; using System.Reactive.Linq; using System.Reactive.Subjects; +using Perspex.Utilities; namespace Perspex.Markup.Binding { @@ -82,7 +83,14 @@ namespace Perspex.Markup.Binding { try { - return _converter.Convert(value, type, null, CultureInfo.CurrentUICulture); + if (value == null || value == PerspexProperty.UnsetValue) + { + return TypeUtilities.Default(type); + } + else + { + return _converter.Convert(value, type, null, CultureInfo.CurrentUICulture); + } } catch { @@ -95,8 +103,16 @@ namespace Perspex.Markup.Binding { try { - result = _converter.ConvertBack(value, type, null, CultureInfo.CurrentUICulture); - return true; + if (value == null || value == PerspexProperty.UnsetValue) + { + result = TypeUtilities.Default(type); + return true; + } + else + { + result = _converter.ConvertBack(value, type, null, CultureInfo.CurrentUICulture); + return true; + } } catch { diff --git a/src/Perspex.Base/PriorityValue.cs b/src/Perspex.Base/PriorityValue.cs index 011f07cfa0..0bbdcc389e 100644 --- a/src/Perspex.Base/PriorityValue.cs +++ b/src/Perspex.Base/PriorityValue.cs @@ -216,7 +216,7 @@ namespace Perspex { var old = _value; - if (_validate != null) + if (_validate != null && value != PerspexProperty.UnsetValue) { value = _validate(value); } diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs index ea1fecb513..42fcf39f3c 100644 --- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs +++ b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs @@ -54,6 +54,16 @@ namespace Perspex.Base.UnitTests Assert.Equal("newvalue", target.Foo); } + [Fact] + public void SetValue_NonGeneric_Coerces_UnsetValue_To_Default_Value() + { + var target = new Class1(); + + target.SetValue((PerspexProperty)Class1.BazProperty, PerspexProperty.UnsetValue); + + Assert.Equal(0, target.Baz); + } + [Fact] public void SetValue_Raises_PropertyChanged() { @@ -148,6 +158,21 @@ namespace Perspex.Base.UnitTests Assert.Equal("second", target.Foo); } + [Fact] + public void Bind_NonGeneric_Coerces_UnsetValue() + { + var target = new Class1(); + var source = new Subject(); + + var sub = target.Bind((PerspexProperty)Class1.BazProperty, source); + + Assert.Equal(5, target.Baz); + source.OnNext(6); + Assert.Equal(6, target.Baz); + source.OnNext(PerspexProperty.UnsetValue); + Assert.Equal(0, target.Baz); + } + [Fact] public void Bind_Handles_Wrong_Type() { diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs index d6649b01d8..422967f858 100644 --- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs +++ b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.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.Reactive.Subjects; using Xunit; namespace Perspex.Base.UnitTests @@ -57,6 +58,17 @@ namespace Perspex.Base.UnitTests Assert.Equal(50, target.GetValue(Class1.QuxProperty)); } + [Fact] + public void Binding_To_UnsetValue_Doesnt_Throw() + { + var target = new Class1(); + var source = new Subject(); + + target.Bind(Class1.QuxProperty, source); + + source.OnNext(PerspexProperty.UnsetValue); + } + private class Class1 : PerspexObject { public static readonly PerspexProperty QuxProperty = diff --git a/tests/Perspex.Markup.UnitTests/Binding/ExpressionSubjectTests.cs b/tests/Perspex.Markup.UnitTests/Binding/ExpressionSubjectTests.cs new file mode 100644 index 0000000000..36dcc6d9c7 --- /dev/null +++ b/tests/Perspex.Markup.UnitTests/Binding/ExpressionSubjectTests.cs @@ -0,0 +1,135 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System.Reactive.Linq; +using Perspex.Markup.Binding; +using Xunit; + +namespace Perspex.Markup.UnitTests.Binding +{ + public class ExpressionSubjectTests + { + [Fact] + public async void Should_Get_Simple_Property_Value() + { + var data = new Class1 { StringValue = "foo" }; + var target = new ExpressionSubject(new ExpressionObserver(data, "StringValue"), typeof(string)); + var result = await target.Take(1); + + Assert.Equal("foo", result); + } + + [Fact] + public void Should_Set_Simple_Property_Value() + { + var data = new Class1 { StringValue = "foo" }; + var target = new ExpressionSubject(new ExpressionObserver(data, "StringValue"), typeof(string)); + + target.OnNext("bar"); + + Assert.Equal("bar", data.StringValue); + } + + [Fact] + public async void Should_Convert_Get_String_To_Double() + { + var data = new Class1 { StringValue = "5.6" }; + var target = new ExpressionSubject(new ExpressionObserver(data, "StringValue"), typeof(double)); + var result = await target.Take(1); + + Assert.Equal(5.6, result); + } + + [Fact] + public async void Should_Convert_Get_Invalid_Double_String_To_UnsetValue() + { + var data = new Class1 { StringValue = "foo" }; + var target = new ExpressionSubject(new ExpressionObserver(data, "StringValue"), typeof(double)); + var result = await target.Take(1); + + Assert.Equal(PerspexProperty.UnsetValue, result); + } + + [Fact] + public async void Should_Coerce_Get_Null_String_To_Double_Deafult_Value() + { + var data = new Class1 { StringValue = null }; + var target = new ExpressionSubject(new ExpressionObserver(data, "StringValue"), typeof(double)); + var result = await target.Take(1); + + Assert.Equal(0.0, result); + } + + [Fact] + public void Should_Convert_Set_String_To_Double() + { + var data = new Class1 { StringValue = "5.6" }; + var target = new ExpressionSubject(new ExpressionObserver(data, "StringValue"), typeof(double)); + + target.OnNext(6.7); + + Assert.Equal("6.7", data.StringValue); + } + + [Fact] + public async void Should_Convert_Get_Double_To_String() + { + var data = new Class1 { DoubleValue = 5.6 }; + var target = new ExpressionSubject(new ExpressionObserver(data, "DoubleValue"), typeof(string)); + var result = await target.Take(1); + + Assert.Equal("5.6", result); + } + + [Fact] + public void Should_Convert_Set_Double_To_String() + { + var data = new Class1 { DoubleValue = 5.6 }; + var target = new ExpressionSubject(new ExpressionObserver(data, "DoubleValue"), typeof(string)); + + target.OnNext("6.7"); + + Assert.Equal(6.7, data.DoubleValue); + } + + [Fact] + public void Should_Ignore_Set_Invalid_Double_String() + { + var data = new Class1 { DoubleValue = 5.6 }; + var target = new ExpressionSubject(new ExpressionObserver(data, "DoubleValue"), typeof(string)); + + target.OnNext("foo"); + + Assert.Equal(5.6, data.DoubleValue); + } + + [Fact] + public void Should_Coerce_Set_Null_To_Default_Value() + { + var data = new Class1 { DoubleValue = 5.6 }; + var target = new ExpressionSubject(new ExpressionObserver(data, "DoubleValue"), typeof(string)); + + target.OnNext(null); + + Assert.Equal(0, data.DoubleValue); + } + + [Fact] + public void Should_Coerce_Set_UnsetValue_To_Default_Value() + { + var data = new Class1 { DoubleValue = 5.6 }; + var target = new ExpressionSubject(new ExpressionObserver(data, "DoubleValue"), typeof(string)); + + target.OnNext(PerspexProperty.UnsetValue); + + Assert.Equal(0, data.DoubleValue); + } + + private class Class1 + { + public string StringValue { get; set; } + + public double DoubleValue { get; set; } + } + } +} diff --git a/tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj b/tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj index 76e988d0e6..5fdecc1cdb 100644 --- a/tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj +++ b/tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj @@ -73,6 +73,7 @@ +