Browse Source

Renamed PerpexProperty coercion as validation.

As it can be used to invalidate a property change by throwing an
exception as well as coerce the value.
pull/63/head
Steven Kirk 11 years ago
parent
commit
ee8c211683
  1. 23
      Perspex.Base/PerspexObject.cs
  2. 46
      Perspex.Base/PerspexProperty.cs
  3. 12
      Perspex.Base/PerspexProperty`1.cs
  4. 20
      Perspex.Base/PriorityValue.cs
  5. 8
      Perspex.Controls/Primitives/SelectingItemsControl.cs
  6. 8
      Perspex.Controls/ScrollViewer.cs
  7. 8
      Perspex.Controls/TextBox.cs
  8. 4
      Perspex.Controls/TreeView.cs
  9. 2
      Tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj
  10. 37
      Tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs
  11. 4
      Tests/Perspex.Base.UnitTests/PriorityValueTests.cs

23
Perspex.Base/PerspexObject.cs

@ -647,16 +647,16 @@ namespace Perspex
}
/// <summary>
/// Forces the specified property to be re-coerced.
/// Forces the specified property to be revalidated.
/// </summary>
/// <param name="property">The property.</param>
public void CoerceValue(PerspexProperty property)
public void Revalidate(PerspexProperty property)
{
PriorityValue value;
if (this.values.TryGetValue(property, out value))
{
value.Coerce();
value.Revalidate();
}
}
@ -670,17 +670,17 @@ namespace Perspex
}
/// <summary>
/// Forces re-coercion of properties when a property value changes.
/// Forces revalidation of properties when a property value changes.
/// </summary>
/// <param name="property">The property to that affects coercion.</param>
/// <param name="property">The property to that affects validation.</param>
/// <param name="affected">The affected properties.</param>
protected static void AffectsCoercion(PerspexProperty property, params PerspexProperty[] affected)
protected static void AffectsValidation(PerspexProperty property, params PerspexProperty[] affected)
{
property.Changed.Subscribe(e =>
{
foreach (var p in affected)
{
e.Sender.CoerceValue(p);
e.Sender.Revalidate(p);
}
});
}
@ -700,14 +700,15 @@ namespace Perspex
/// <returns>The <see cref="PriorityValue"/>.</returns>
private PriorityValue CreatePriorityValue(PerspexProperty property)
{
Func<object, object> coerce = null;
Func<PerspexObject, object, object> validate = property.GetValidationFunc(this.GetType());
Func<object, object> validate2 = null;
if (property.Coerce != null)
if (validate != null)
{
coerce = v => property.Coerce(this, v);
validate2 = v => validate(this, v);
}
PriorityValue result = new PriorityValue(property.Name, property.PropertyType, coerce);
PriorityValue result = new PriorityValue(property.Name, property.PropertyType, validate2);
result.Changed.Subscribe(x =>
{

46
Perspex.Base/PerspexProperty.cs

@ -41,6 +41,8 @@ namespace Perspex
/// </summary>
private Subject<PerspexPropertyChangedEventArgs> changed = new Subject<PerspexPropertyChangedEventArgs>();
private Func<PerspexObject, object, object> validate;
/// <summary>
/// Initializes a new instance of the <see cref="PerspexProperty"/> class.
/// </summary>
@ -50,7 +52,7 @@ namespace Perspex
/// <param name="defaultValue">The default value of the property.</param>
/// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="defaultBindingMode">The default binding mode for the property.</param>
/// <param name="coerce">A coercion function.</param>
/// <param name="validate">A validation function.</param>
/// <param name="isAttached">Whether the property is an attached property.</param>
public PerspexProperty(
string name,
@ -59,7 +61,7 @@ namespace Perspex
object defaultValue,
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.Default,
Func<PerspexObject, object, object> coerce = null,
Func<PerspexObject, object, object> validate = null,
bool isAttached = false)
{
Contract.Requires<NullReferenceException>(name != null);
@ -72,7 +74,7 @@ namespace Perspex
this.defaultValues.Add(ownerType, defaultValue);
this.Inherits = inherits;
this.DefaultBindingMode = defaultBindingMode;
this.Coerce = coerce;
this.validate = validate;
this.IsAttached = isAttached;
}
@ -116,14 +118,6 @@ namespace Perspex
/// </value>
public BindingMode DefaultBindingMode { get; }
/// <summary>
/// Gets the property's coerce function.
/// </summary>
/// <value>
/// The property's coerce function.
/// </value>
public Func<PerspexObject, object, object> Coerce { get; }
/// <summary>
/// Gets a value indicating whether this is an attached property.
/// </summary>
@ -203,14 +197,14 @@ namespace Perspex
/// <param name="defaultValue">The default value of the property.</param>
/// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="defaultBindingMode">The default binding mode for the property.</param>
/// <param name="coerce">A coercion function.</param>
/// <param name="validate">A validation function.</param>
/// <returns>A <see cref="PerspexProperty{TValue}"/></returns>
public static PerspexProperty<TValue> Register<TOwner, TValue>(
string name,
TValue defaultValue = default(TValue),
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay,
Func<PerspexObject, TValue, TValue> coerce = null)
Func<PerspexObject, TValue, TValue> validate = null)
where TOwner : PerspexObject
{
Contract.Requires<NullReferenceException>(name != null);
@ -221,7 +215,7 @@ namespace Perspex
defaultValue,
inherits,
defaultBindingMode,
coerce,
validate,
false);
PerspexObject.Register(typeof(TOwner), result);
@ -239,14 +233,14 @@ namespace Perspex
/// <param name="defaultValue">The default value of the property.</param>
/// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="defaultBindingMode">The default binding mode for the property.</param>
/// <param name="coerce">A coercion function.</param>
/// <param name="validate">A validation function.</param>
/// <returns>A <see cref="PerspexProperty{TValue}"/></returns>
public static PerspexProperty<TValue> RegisterAttached<TOwner, THost, TValue>(
string name,
TValue defaultValue = default(TValue),
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay,
Func<PerspexObject, TValue, TValue> coerce = null)
Func<PerspexObject, TValue, TValue> validate = null)
{
Contract.Requires<NullReferenceException>(name != null);
@ -256,7 +250,7 @@ namespace Perspex
defaultValue,
inherits,
defaultBindingMode,
coerce,
validate,
true);
PerspexObject.Register(typeof(THost), result);
@ -274,7 +268,7 @@ namespace Perspex
/// <param name="defaultValue">The default value of the property.</param>
/// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="defaultBindingMode">The default binding mode for the property.</param>
/// <param name="coerce">A coercion function.</param>
/// <param name="validate">A validation function.</param>
/// <returns>A <see cref="PerspexProperty{TValue}"/></returns>
public static PerspexProperty<TValue> RegisterAttached<THost, TValue>(
string name,
@ -282,7 +276,7 @@ namespace Perspex
TValue defaultValue = default(TValue),
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay,
Func<PerspexObject, TValue, TValue> coerce = null)
Func<PerspexObject, TValue, TValue> validate = null)
{
Contract.Requires<NullReferenceException>(name != null);
@ -292,7 +286,7 @@ namespace Perspex
defaultValue,
inherits,
defaultBindingMode,
coerce,
validate,
true);
PerspexObject.Register(typeof(THost), result);
@ -340,6 +334,18 @@ namespace Perspex
return this.defaultValues[this.OwnerType];
}
/// <summary>
/// Gets the validation function for the property on the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>
/// The validation function, or null if no validation function registered for this type.
/// </returns>
public Func<PerspexObject, object, object> GetValidationFunc(Type type)
{
return this.validate;
}
/// <summary>
/// Checks whether the <paramref name="value"/> is valid for the property.
/// </summary>

12
Perspex.Base/PerspexProperty`1.cs

@ -22,7 +22,7 @@ namespace Perspex
/// <param name="defaultValue">The default value of the property.</param>
/// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="defaultBindingMode">The default binding mode for the property.</param>
/// <param name="coerce">A coercion function.</param>
/// <param name="validate">A validation function.</param>
/// <param name="isAttached">Whether the property is an attached property.</param>
public PerspexProperty(
string name,
@ -30,7 +30,7 @@ namespace Perspex
TValue defaultValue = default(TValue),
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.Default,
Func<PerspexObject, TValue, TValue> coerce = null,
Func<PerspexObject, TValue, TValue> validate = null,
bool isAttached = false)
: base(
name,
@ -39,7 +39,7 @@ namespace Perspex
defaultValue,
inherits,
defaultBindingMode,
Convert(coerce),
Convert(validate),
isAttached)
{
Contract.Requires<NullReferenceException>(name != null);
@ -68,10 +68,10 @@ namespace Perspex
}
/// <summary>
/// Converts from a typed coercion function to an untyped.
/// Converts from a typed validation function to an untyped.
/// </summary>
/// <param name="f">The typed coercion function.</param>
/// <returns>Te untyped coercion function.</returns>
/// <param name="f">The typed validation function.</param>
/// <returns>The untyped validation function.</returns>
private static Func<PerspexObject, object, object> Convert(Func<PerspexObject, TValue, TValue> f)
{
return f != null ? (o, v) => f(o, (TValue)v) : (Func<PerspexObject, object, object>)null;

20
Perspex.Base/PriorityValue.cs

@ -54,23 +54,23 @@ namespace Perspex
private object value;
/// <summary>
/// The function used to coerce the value, if any.
/// The function used to validate the value, if any.
/// </summary>
private Func<object, object> coerce;
private Func<object, object> validate;
/// <summary>
/// Initializes a new instance of the <see cref="PriorityValue"/> class.
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="valueType">The value type.</param>
/// <param name="coerce">An optional coercion function.</param>
public PriorityValue(string name, Type valueType, Func<object, object> coerce = null)
/// <param name="validate">An optional validation function.</param>
public PriorityValue(string name, Type valueType, Func<object, object> validate = null)
{
this.name = name;
this.valueType = valueType;
this.value = PerspexProperty.UnsetValue;
this.ValuePriority = int.MaxValue;
this.coerce = coerce;
this.validate = validate;
}
/// <summary>
@ -180,11 +180,11 @@ namespace Perspex
}
/// <summary>
/// Causes a re-coercion of the value.
/// Causes a revalidation of the value.
/// </summary>
public void Coerce()
public void Revalidate()
{
if (this.coerce != null)
if (this.validate != null)
{
PriorityLevel level;
@ -233,9 +233,9 @@ namespace Perspex
var old = this.value;
if (this.coerce != null)
if (this.validate != null)
{
value = this.coerce(value);
value = this.validate(value);
}
this.ValuePriority = priority;

8
Perspex.Controls/Primitives/SelectingItemsControl.cs

@ -30,7 +30,7 @@ namespace Perspex.Controls.Primitives
PerspexProperty.Register<SelectingItemsControl, int>(
nameof(SelectedIndex),
defaultValue: -1,
coerce: CoerceSelectedIndex);
validate: ValidateSelectedIndex);
/// <summary>
/// Defines the <see cref="SelectedItem"/> property.
@ -38,7 +38,7 @@ namespace Perspex.Controls.Primitives
public static readonly PerspexProperty<object> SelectedItemProperty =
PerspexProperty.Register<SelectingItemsControl, object>(
nameof(SelectedItem),
coerce: CoerceSelectedItem);
validate: ValidateSelectedItem);
/// <summary>
/// Event that should be raised by items that implement <see cref="ISelectable"/> to
@ -258,7 +258,7 @@ namespace Perspex.Controls.Primitives
/// <param name="o">The object on which the property has changed.</param>
/// <param name="value">The proposed value.</param>
/// <returns>The coerced value.</returns>
private static int CoerceSelectedIndex(PerspexObject o, int value)
private static int ValidateSelectedIndex(PerspexObject o, int value)
{
var control = o as SelectingItemsControl;
@ -293,7 +293,7 @@ namespace Perspex.Controls.Primitives
/// <param name="o">The object on which the property has changed.</param>
/// <param name="value">The proposed value.</param>
/// <returns>The coerced value.</returns>
private static object CoerceSelectedItem(PerspexObject o, object value)
private static object ValidateSelectedItem(PerspexObject o, object value)
{
var control = o as SelectingItemsControl;

8
Perspex.Controls/ScrollViewer.cs

@ -17,7 +17,7 @@ namespace Perspex.Controls
PerspexProperty.Register<ScrollViewer, Size>("Extent");
public static readonly PerspexProperty<Vector> OffsetProperty =
PerspexProperty.Register<ScrollViewer, Vector>("Offset", coerce: CoerceOffset);
PerspexProperty.Register<ScrollViewer, Vector>("Offset", validate: ValidateOffset);
public static readonly PerspexProperty<Size> ViewportProperty =
PerspexProperty.Register<ScrollViewer, Size>("Viewport");
@ -51,8 +51,8 @@ namespace Perspex.Controls
static ScrollViewer()
{
PerspexObject.AffectsCoercion(ExtentProperty, OffsetProperty);
PerspexObject.AffectsCoercion(ViewportProperty, OffsetProperty);
PerspexObject.AffectsValidation(ExtentProperty, OffsetProperty);
PerspexObject.AffectsValidation(ViewportProperty, OffsetProperty);
}
public ScrollViewer()
@ -143,7 +143,7 @@ namespace Perspex.Controls
return double.IsNaN(result) ? 0 : Math.Round(result);
}
private static Vector CoerceOffset(PerspexObject o, Vector value)
private static Vector ValidateOffset(PerspexObject o, Vector value)
{
ScrollViewer scrollViewer = o as ScrollViewer;

8
Perspex.Controls/TextBox.cs

@ -27,13 +27,13 @@ namespace Perspex.Controls
PerspexProperty.Register<TextBox, bool>("AcceptsTab");
public static readonly PerspexProperty<int> CaretIndexProperty =
PerspexProperty.Register<TextBox, int>("CaretIndex", coerce: CoerceCaretIndex);
PerspexProperty.Register<TextBox, int>("CaretIndex", validate: ValidateCaretIndex);
public static readonly PerspexProperty<int> SelectionStartProperty =
PerspexProperty.Register<TextBox, int>("SelectionStart", coerce: CoerceCaretIndex);
PerspexProperty.Register<TextBox, int>("SelectionStart", validate: ValidateCaretIndex);
public static readonly PerspexProperty<int> SelectionEndProperty =
PerspexProperty.Register<TextBox, int>("SelectionEnd", coerce: CoerceCaretIndex);
PerspexProperty.Register<TextBox, int>("SelectionEnd", validate: ValidateCaretIndex);
public static readonly PerspexProperty<string> TextProperty =
TextBlock.TextProperty.AddOwner<TextBox>();
@ -297,7 +297,7 @@ namespace Perspex.Controls
}
}
private static int CoerceCaretIndex(PerspexObject o, int value)
private static int ValidateCaretIndex(PerspexObject o, int value)
{
var text = o.GetValue(TextProperty);
var length = (text != null) ? text.Length : 0;

4
Perspex.Controls/TreeView.cs

@ -17,7 +17,7 @@ namespace Perspex.Controls
public class TreeView : ItemsControl
{
public static readonly PerspexProperty<object> SelectedItemProperty =
PerspexProperty.Register<TreeView, object>("SelectedItem", coerce: CoerceSelectedItem);
PerspexProperty.Register<TreeView, object>("SelectedItem", validate: ValidateSelectedItem);
static TreeView()
{
@ -158,7 +158,7 @@ namespace Perspex.Controls
e.Handled = true;
}
private static object CoerceSelectedItem(PerspexObject o, object value)
private static object ValidateSelectedItem(PerspexObject o, object value)
{
//var control = o as SelectingItemsControl;

2
Tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj

@ -80,7 +80,7 @@
<Compile Include="Collections\PerspexListTests.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="PerspexObjectTests_GetObservable.cs" />
<Compile Include="PerspexObjectTests_Coercion.cs" />
<Compile Include="PerspexObjectTests_Validation.cs" />
<Compile Include="PerspexObjectTests_Binding.cs" />
<Compile Include="PerspexObjectTests_Inheritance.cs" />
<Compile Include="PerspexObjectTests_SetValue.cs" />

37
Tests/Perspex.Base.UnitTests/PerspexObjectTests_Coercion.cs → Tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs

@ -1,5 +1,5 @@
// -----------------------------------------------------------------------
// <copyright file="PerspexObjectTests_Coercion.cs" company="Steven Kirk">
// <copyright file="PerspexObjectTests_Validation.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
@ -7,22 +7,18 @@
namespace Perspex.Base.UnitTests
{
using System;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Xunit;
public class PerspexObjectTests_Coercion
public class PerspexObjectTests_Validation
{
[Fact]
public void CoerceValue_Causes_Recoercion()
public void SetValue_Causes_Validation()
{
Class1 target = new Class1();
target.SetValue(Class1.QuxProperty, 7);
Assert.Equal(7, target.GetValue(Class1.QuxProperty));
target.MaxQux = 5;
target.CoerceValue(Class1.QuxProperty);
target.SetValue(Class1.QuxProperty, 5);
Assert.Throws<ArgumentOutOfRangeException>(() => target.SetValue(Class1.QuxProperty, 25));
Assert.Equal(5, target.GetValue(Class1.QuxProperty));
}
[Fact]
@ -38,20 +34,39 @@ namespace Perspex.Base.UnitTests
Assert.Equal(10, target.GetValue(Class1.QuxProperty));
}
[Fact]
public void Revalidate_Causes_Recoercion()
{
Class1 target = new Class1();
target.SetValue(Class1.QuxProperty, 7);
Assert.Equal(7, target.GetValue(Class1.QuxProperty));
target.MaxQux = 5;
target.Revalidate(Class1.QuxProperty);
}
private class Class1 : PerspexObject
{
public static readonly PerspexProperty<int> QuxProperty =
PerspexProperty.Register<Class1, int>("Qux", coerce: Coerce);
PerspexProperty.Register<Class1, int>("Qux", validate: Coerce);
public Class1()
{
this.MaxQux = 10;
this.ErrorQux = 20;
}
public int MaxQux { get; set; }
public int ErrorQux { get; set; }
private static int Coerce(PerspexObject instance, int value)
{
if (value > ((Class1)instance).ErrorQux)
{
throw new ArgumentOutOfRangeException();
}
return Math.Min(Math.Max(value, 0), ((Class1)instance).MaxQux);
}
}

4
Tests/Perspex.Base.UnitTests/PriorityValueTests.cs

@ -290,7 +290,7 @@ namespace Perspex.Base.UnitTests
}
[Fact]
public void Coerce_Should_ReCoerce_Value()
public void Revalidate_Should_ReCoerce_Value()
{
var max = 10;
var target = new PriorityValue("Test", typeof(int), x => Math.Min((int)x, max));
@ -302,7 +302,7 @@ namespace Perspex.Base.UnitTests
source.OnNext(15);
Assert.Equal(10, target.Value);
max = 12;
target.Coerce();
target.Revalidate();
Assert.Equal(12, target.Value);
}

Loading…
Cancel
Save