Browse Source

Handle invalid values in direct property bindings.

Invalid values will be converted to default(TPropertyValue).
pull/242/merge
Steven Kirk 11 years ago
parent
commit
a400a89003
  1. 4
      src/Perspex.Base/PerspexObject.cs
  2. 32
      src/Perspex.Base/Utilities/TypeUtilities.cs
  3. 50
      tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs

4
src/Perspex.Base/PerspexObject.cs

@ -605,7 +605,9 @@ namespace Perspex
property, property,
GetDescription(source)); 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 else
{ {

32
src/Perspex.Base/Utilities/TypeUtilities.cs

@ -32,8 +32,9 @@ namespace Perspex.Utilities
/// <param name="to">The type to cast to.</param> /// <param name="to">The type to cast to.</param>
/// <param name="value">The value to cast.</param> /// <param name="value">The value to cast.</param>
/// <param name="result">If sucessful, contains the cast value.</param> /// <param name="result">If sucessful, contains the cast value.</param>
/// <param name="allowUnset">Allow <see cref="PerspexProperty.UnsetValue"/>.</param>
/// <returns>True if the cast was sucessful, otherwise false.</returns> /// <returns>True if the cast was sucessful, otherwise false.</returns>
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<ArgumentNullException>(to != null); Contract.Requires<ArgumentNullException>(to != null);
@ -46,7 +47,7 @@ namespace Perspex.Utilities
var from = value.GetType(); var from = value.GetType();
if (value == PerspexProperty.UnsetValue) if (allowUnset && value == PerspexProperty.UnsetValue)
{ {
result = value; result = value;
return true; return true;
@ -77,5 +78,32 @@ namespace Perspex.Utilities
result = null; result = null;
return false; return false;
} }
/// <summary>
/// Casts a value to a type, returning the default for that type if the value could not be
/// cast.
/// </summary>
/// <param name="value">The value to cast.</param>
/// <param name="type">The type to cast to..</param>
/// <param name="allowUnset">Allow <see cref="PerspexProperty.UnsetValue"/>.</param>
/// <returns>A value of <paramref name="type"/>.</returns>
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;
}
}
} }
} }

50
tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs

@ -148,6 +148,45 @@ namespace Perspex.Base.UnitTests
Assert.Equal("second", target.Foo); Assert.Equal("second", target.Foo);
} }
[Fact]
public void Bind_Handles_Wrong_Type()
{
var target = new Class1();
var source = new Subject<object>();
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<object>();
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<object>();
var sub = target.Bind(Class1.BazProperty, source);
source.OnNext(PerspexProperty.UnsetValue);
Assert.Equal(0, target.Baz);
}
[Fact] [Fact]
public void ReadOnly_Property_Cannot_Be_Set() public void ReadOnly_Property_Cannot_Be_Set()
{ {
@ -295,9 +334,12 @@ namespace Perspex.Base.UnitTests
public static readonly PerspexProperty<string> BarProperty = public static readonly PerspexProperty<string> BarProperty =
PerspexProperty.RegisterDirect<Class1, string>("Bar", o => o.Bar); PerspexProperty.RegisterDirect<Class1, string>("Bar", o => o.Bar);
private string _foo = "initial"; public static readonly PerspexProperty<int> BazProperty =
PerspexProperty.RegisterDirect<Class1, int>("Bar", o => o.Baz, (o,v) => o.Baz = v);
private string _foo = "initial";
private string _bar = "bar"; private string _bar = "bar";
private int _baz = 5;
public string Foo public string Foo
{ {
@ -309,6 +351,12 @@ namespace Perspex.Base.UnitTests
{ {
get { return _bar; } get { return _bar; }
} }
public int Baz
{
get { return _baz; }
set { SetAndRaise(BazProperty, ref _baz, value); }
}
} }
private class Class2 : PerspexObject private class Class2 : PerspexObject

Loading…
Cancel
Save