diff --git a/src/Perspex.Base/PerspexObject.cs b/src/Perspex.Base/PerspexObject.cs index 03d5fecd69..696d01893a 100644 --- a/src/Perspex.Base/PerspexObject.cs +++ b/src/Perspex.Base/PerspexObject.cs @@ -605,7 +605,9 @@ namespace Perspex property, GetDescription(source)); - return source.Subscribe(x => SetValue(property, x)); + return source + .Select(x => TypeUtilities.CastOrDefault(x, property.PropertyType, false)) + .Subscribe(x => SetValue(property, x)); } else { diff --git a/src/Perspex.Base/Utilities/TypeUtilities.cs b/src/Perspex.Base/Utilities/TypeUtilities.cs index 98909d9801..d026efc622 100644 --- a/src/Perspex.Base/Utilities/TypeUtilities.cs +++ b/src/Perspex.Base/Utilities/TypeUtilities.cs @@ -32,8 +32,9 @@ namespace Perspex.Utilities /// The type to cast to. /// The value to cast. /// If sucessful, contains the cast value. + /// Allow . /// True if the cast was sucessful, otherwise false. - public static bool TryCast(Type to, object value, out object result) + public static bool TryCast(Type to, object value, out object result, bool allowUnset = true) { Contract.Requires(to != null); @@ -46,7 +47,7 @@ namespace Perspex.Utilities var from = value.GetType(); - if (value == PerspexProperty.UnsetValue) + if (allowUnset && value == PerspexProperty.UnsetValue) { result = value; return true; @@ -77,5 +78,32 @@ namespace Perspex.Utilities result = null; return false; } + + /// + /// Casts a value to a type, returning the default for that type if the value could not be + /// cast. + /// + /// The value to cast. + /// The type to cast to.. + /// Allow . + /// A value of . + public static object CastOrDefault(object value, Type type, bool allowUnset = true) + { + var typeInfo = type.GetTypeInfo(); + object result; + + if (TypeUtilities.TryCast(type, value, out result, allowUnset)) + { + return result; + } + else if (typeInfo.IsValueType) + { + return Activator.CreateInstance(type); + } + else + { + return null; + } + } } } diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs index c8b4470d13..a9a2d5c1fc 100644 --- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs +++ b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs @@ -148,6 +148,45 @@ namespace Perspex.Base.UnitTests Assert.Equal("second", target.Foo); } + [Fact] + public void Bind_Handles_Wrong_Type() + { + var target = new Class1(); + var source = new Subject(); + + var sub = target.Bind(Class1.FooProperty, source); + + source.OnNext(45); + + Assert.Equal(null, target.Foo); + } + + [Fact] + public void Bind_Handles_Wrong_Value_Type() + { + var target = new Class1(); + var source = new Subject(); + + var sub = target.Bind(Class1.BazProperty, source); + + source.OnNext("foo"); + + Assert.Equal(0, target.Baz); + } + + [Fact] + public void Bind_Handles_UnsetValue() + { + var target = new Class1(); + var source = new Subject(); + + var sub = target.Bind(Class1.BazProperty, source); + + source.OnNext(PerspexProperty.UnsetValue); + + Assert.Equal(0, target.Baz); + } + [Fact] public void ReadOnly_Property_Cannot_Be_Set() { @@ -295,9 +334,12 @@ namespace Perspex.Base.UnitTests public static readonly PerspexProperty BarProperty = PerspexProperty.RegisterDirect("Bar", o => o.Bar); - private string _foo = "initial"; + public static readonly PerspexProperty BazProperty = + PerspexProperty.RegisterDirect("Bar", o => o.Baz, (o,v) => o.Baz = v); + private string _foo = "initial"; private string _bar = "bar"; + private int _baz = 5; public string Foo { @@ -309,6 +351,12 @@ namespace Perspex.Base.UnitTests { get { return _bar; } } + + public int Baz + { + get { return _baz; } + set { SetAndRaise(BazProperty, ref _baz, value); } + } } private class Class2 : PerspexObject