Browse Source

Handle null and UnsetValue in ExpressionSubject.

Steven Kirk 10 years ago
parent
commit
16825ac45e

+ 2 - 2
samples/XamlTestApplicationPcl/Views/MainWindow.paml

@@ -5,9 +5,9 @@
         Title="Perspex Test Application" Height="350" Width="525" SizeToContent="WidthAndHeight" >
     <Grid RowDefinitions="Auto,*,Auto" ColumnDefinitions="*,*">
         <TabControl Grid.Row="1" Grid.ColumnSpan="2" Padding="5">
-            <TabControl.Transition>
+            <!--<TabControl.Transition>
                 <PageSlide Duration="0.25" />
-            </TabControl.Transition>
+            </TabControl.Transition>-->
             <TabItem Header="Buttons">
                 <StackPanel HorizontalAlignment="Center" Width="200" VerticalAlignment="Center">
                     <StackPanel.Styles>

+ 19 - 3
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
             {

+ 1 - 1
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);
                 }

+ 25 - 0
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<object>();
+
+            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()
         {

+ 12 - 0
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<object>();
+
+            target.Bind(Class1.QuxProperty, source);
+
+            source.OnNext(PerspexProperty.UnsetValue);
+        }
+
         private class Class1 : PerspexObject
         {
             public static readonly PerspexProperty<int> QuxProperty =

+ 135 - 0
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; }
+        }
+    }
+}

+ 1 - 0
tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj

@@ -73,6 +73,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Binding\ExpressionNodeBuilderTests_Errors.cs" />
+    <Compile Include="Binding\ExpressionSubjectTests.cs" />
     <Compile Include="Binding\ExpressionObserverTests_Observable.cs" />
     <Compile Include="Binding\ExpressionObserverTests_SetValue.cs" />
     <Compile Include="Binding\ExpressionObserverTests_Task.cs" />