diff --git a/Perspex.Base/PerspexProperty.cs b/Perspex.Base/PerspexProperty.cs
index 3c6333bb47..2c3f089b32 100644
--- a/Perspex.Base/PerspexProperty.cs
+++ b/Perspex.Base/PerspexProperty.cs
@@ -23,7 +23,6 @@ namespace Perspex
///
/// Represents an unset property value.
///
- [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields must be private", Justification = "It's readonly")]
public static readonly object UnsetValue = new Unset();
///
@@ -41,7 +40,11 @@ namespace Perspex
///
private Subject changed = new Subject();
- private Func validate;
+ ///
+ /// The validation functions for the property, by type.
+ ///
+ private Dictionary> validation =
+ new Dictionary>();
///
/// Initializes a new instance of the class.
@@ -74,7 +77,7 @@ namespace Perspex
this.defaultValues.Add(ownerType, defaultValue);
this.Inherits = inherits;
this.DefaultBindingMode = defaultBindingMode;
- this.validate = validate;
+ this.validation.Add(ownerType, validate);
this.IsAttached = isAttached;
}
@@ -204,7 +207,7 @@ namespace Perspex
TValue defaultValue = default(TValue),
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay,
- Func validate = null)
+ Func validate = null)
where TOwner : PerspexObject
{
Contract.Requires(name != null);
@@ -215,7 +218,7 @@ namespace Perspex
defaultValue,
inherits,
defaultBindingMode,
- validate,
+ Cast(validate),
false);
PerspexObject.Register(typeof(TOwner), result);
@@ -343,7 +346,21 @@ namespace Perspex
///
public Func GetValidationFunc(Type type)
{
- return this.validate;
+ Contract.Requires(type != null);
+
+ while (type != null)
+ {
+ Func result;
+
+ if (this.validation.TryGetValue(type, out result))
+ {
+ return result;
+ }
+
+ type = type.GetTypeInfo().BaseType;
+ }
+
+ return null;
}
///
@@ -392,6 +409,23 @@ namespace Perspex
this.defaultValues.Add(type, defaultValue);
}
+ ///
+ /// Overrides the validation function for the property on the specified type.
+ ///
+ /// The type.
+ /// The validation function.
+ public void OverrideValidation(Type type, Func validation)
+ {
+ Contract.Requires(type != null);
+
+ if (this.validation.ContainsKey(type))
+ {
+ throw new InvalidOperationException("Validation is already set for this property.");
+ }
+
+ this.validation.Add(type, validation);
+ }
+
///
/// Gets the string representation of the property.
///
@@ -419,6 +453,20 @@ namespace Perspex
this.changed.OnNext(e);
}
+ ///
+ /// Casts a validation function accepting a typed owner to one accepting a
+ /// .
+ ///
+ /// The owner type.
+ /// The property value type.
+ /// The typed function.
+ /// The untyped function.
+ private static Func Cast(Func f)
+ where TOwner : PerspexObject
+ {
+ return f != null ? (o, v) => f((TOwner)o, v) : (Func < PerspexObject, TValue, TValue > )null;
+ }
+
///
/// Class representing the .
///
diff --git a/Perspex.Base/PerspexProperty`1.cs b/Perspex.Base/PerspexProperty`1.cs
index 16e327410f..b8bb06fa2c 100644
--- a/Perspex.Base/PerspexProperty`1.cs
+++ b/Perspex.Base/PerspexProperty`1.cs
@@ -67,6 +67,19 @@ namespace Perspex
return (TValue)this.GetDefaultValue(typeof(T));
}
+ ///
+ /// Overrides the validation function for the property on the specified type.
+ ///
+ /// The type.
+ /// The validation function.
+ public void OverrideValidation(Func validation) where T : PerspexObject
+ {
+ var f = validation != null ?
+ (o, v) => validation((T)o, (TValue)v) :
+ (Func)null;
+ this.OverrideValidation(typeof(T), f);
+ }
+
///
/// Converts from a typed validation function to an untyped.
///
diff --git a/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs
index 2bbbc13d3c..2302b5a995 100644
--- a/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs
+++ b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs
@@ -14,7 +14,7 @@ namespace Perspex.Base.UnitTests
[Fact]
public void SetValue_Causes_Validation()
{
- Class1 target = new Class1();
+ var target = new Class1();
target.SetValue(Class1.QuxProperty, 5);
Assert.Throws(() => target.SetValue(Class1.QuxProperty, 25));
@@ -24,7 +24,7 @@ namespace Perspex.Base.UnitTests
[Fact]
public void SetValue_Causes_Coercion()
{
- Class1 target = new Class1();
+ var target = new Class1();
target.SetValue(Class1.QuxProperty, 5);
Assert.Equal(5, target.GetValue(Class1.QuxProperty));
@@ -37,7 +37,7 @@ namespace Perspex.Base.UnitTests
[Fact]
public void Revalidate_Causes_Recoercion()
{
- Class1 target = new Class1();
+ var target = new Class1();
target.SetValue(Class1.QuxProperty, 7);
Assert.Equal(7, target.GetValue(Class1.QuxProperty));
@@ -45,10 +45,25 @@ namespace Perspex.Base.UnitTests
target.Revalidate(Class1.QuxProperty);
}
+ [Fact]
+ public void Validation_Can_Be_Overridden()
+ {
+ var target = new Class2();
+ Assert.Throws(() => target.SetValue(Class1.QuxProperty, 5));
+ }
+
+ [Fact]
+ public void Validation_Can_Be_Overridden_With_Null()
+ {
+ var target = new Class3();
+ target.SetValue(Class1.QuxProperty, 50);
+ Assert.Equal(50, target.GetValue(Class1.QuxProperty));
+ }
+
private class Class1 : PerspexObject
{
public static readonly PerspexProperty QuxProperty =
- PerspexProperty.Register("Qux", validate: Coerce);
+ PerspexProperty.Register("Qux", validate: Validate);
public Class1()
{
@@ -60,9 +75,9 @@ namespace Perspex.Base.UnitTests
public int ErrorQux { get; set; }
- private static int Coerce(PerspexObject instance, int value)
+ private static int Validate(Class1 instance, int value)
{
- if (value > ((Class1)instance).ErrorQux)
+ if (value > instance.ErrorQux)
{
throw new ArgumentOutOfRangeException();
}
@@ -70,5 +85,34 @@ namespace Perspex.Base.UnitTests
return Math.Min(Math.Max(value, 0), ((Class1)instance).MaxQux);
}
}
+
+ private class Class2 : PerspexObject
+ {
+ public static readonly PerspexProperty QuxProperty =
+ Class1.QuxProperty.AddOwner();
+
+ static Class2()
+ {
+ QuxProperty.OverrideValidation(Validate);
+ }
+
+ private static int Validate(Class2 instance, int value)
+ {
+ if (value < 100)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ return value;
+ }
+ }
+
+ private class Class3 : Class2
+ {
+ static Class3()
+ {
+ QuxProperty.OverrideValidation(null);
+ }
+ }
}
}