Browse Source

Supply UnsetValue for DirectProperty.

When a direct property is set to UnsetValue, previously it used
default(TValue). Allow that value to be overridden and make it be -1 for
SelectingItemsControl.SelectedIndex.
pull/467/head
Steven Kirk 11 years ago
parent
commit
d0dc103c5e
  1. 52
      src/Perspex.Base/DirectPropertyMetadata`1.cs
  2. 16
      src/Perspex.Base/IDirectPropertyMetadata.cs
  3. 2
      src/Perspex.Base/Perspex.Base.csproj
  4. 37
      src/Perspex.Base/PerspexObject.cs
  5. 9
      src/Perspex.Base/PerspexProperty.cs
  6. 6
      src/Perspex.Controls/Primitives/SelectingItemsControl.cs
  7. 3
      src/Perspex.Controls/Primitives/ToggleButton.cs
  8. 29
      tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs

52
src/Perspex.Base/DirectPropertyMetadata`1.cs

@ -0,0 +1,52 @@
// 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 Perspex.Data;
namespace Perspex
{
/// <summary>
/// Metadata for direct perspex properties.
/// </summary>
public class DirectPropertyMetadata<TValue> : PropertyMetadata, IDirectPropertyMetadata
{
/// <summary>
/// Initializes a new instance of the <see cref="StyledPropertyMetadata{TValue}"/> class.
/// </summary>
/// <param name="unsetValue">
/// The value to use when the property is set to <see cref="PerspexProperty.UnsetValue"/>
/// </param>
/// <param name="defaultBindingMode">The default binding mode.</param>
public DirectPropertyMetadata(
TValue unsetValue = default(TValue),
BindingMode defaultBindingMode = BindingMode.Default)
: base(defaultBindingMode)
{
UnsetValue = unsetValue;
}
/// <summary>
/// Gets the to use when the property is set to <see cref="PerspexProperty.UnsetValue"/>.
/// </summary>
public TValue UnsetValue { get; private set; }
/// <inheritdoc/>
object IDirectPropertyMetadata.UnsetValue => UnsetValue;
/// <inheritdoc/>
public override void Merge(PropertyMetadata baseMetadata, PerspexProperty property)
{
base.Merge(baseMetadata, property);
var src = baseMetadata as DirectPropertyMetadata<TValue>;
if (src != null)
{
if (UnsetValue == null)
{
UnsetValue = src.UnsetValue;
}
}
}
}
}

16
src/Perspex.Base/IDirectPropertyMetadata.cs

@ -0,0 +1,16 @@
// 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.
namespace Perspex
{
/// <summary>
/// Untyped interface to <see cref="DirectPropertyMetadata{TValue}"/>
/// </summary>
public interface IDirectPropertyMetadata
{
/// <summary>
/// Gets the to use when the property is set to <see cref="PerspexProperty.UnsetValue"/>.
/// </summary>
object UnsetValue { get; }
}
}

2
src/Perspex.Base/Perspex.Base.csproj

@ -56,6 +56,7 @@
<Compile Include="IDirectPropertyAccessor.cs" /> <Compile Include="IDirectPropertyAccessor.cs" />
<Compile Include="DirectProperty.cs" /> <Compile Include="DirectProperty.cs" />
<Compile Include="IPerspexObject.cs" /> <Compile Include="IPerspexObject.cs" />
<Compile Include="IDirectPropertyMetadata.cs" />
<Compile Include="IStyledPropertyMetadata.cs" /> <Compile Include="IStyledPropertyMetadata.cs" />
<Compile Include="ISupportInitialize.cs" /> <Compile Include="ISupportInitialize.cs" />
<Compile Include="Metadata\DependsOnAttribute.cs" /> <Compile Include="Metadata\DependsOnAttribute.cs" />
@ -92,6 +93,7 @@
<Compile Include="Reactive\AnonymousSubject`1.cs" /> <Compile Include="Reactive\AnonymousSubject`1.cs" />
<Compile Include="Reactive\AnonymousSubject`2.cs" /> <Compile Include="Reactive\AnonymousSubject`2.cs" />
<Compile Include="Reactive\PerspexObservable.cs" /> <Compile Include="Reactive\PerspexObservable.cs" />
<Compile Include="DirectPropertyMetadata`1.cs" />
<Compile Include="StyledPropertyMetadata`1.cs" /> <Compile Include="StyledPropertyMetadata`1.cs" />
<Compile Include="Threading\Dispatcher.cs" /> <Compile Include="Threading\Dispatcher.cs" />
<Compile Include="Threading\DispatcherPriority.cs" /> <Compile Include="Threading\DispatcherPriority.cs" />

37
src/Perspex.Base/PerspexObject.cs

@ -268,14 +268,7 @@ namespace Perspex
{ {
Contract.Requires<ArgumentNullException>(property != null); Contract.Requires<ArgumentNullException>(property != null);
if (property.IsDirect) return (T)GetValue((PerspexProperty)property);
{
return (T)((IDirectPropertyAccessor)GetRegistered(property)).GetValue(this);
}
else
{
return (T)GetValue((PerspexProperty)property);
}
} }
/// <summary> /// <summary>
@ -315,7 +308,7 @@ namespace Perspex
{ {
var accessor = (IDirectPropertyAccessor)GetRegistered(property); var accessor = (IDirectPropertyAccessor)GetRegistered(property);
LogPropertySet(property, value, priority); LogPropertySet(property, value, priority);
accessor.SetValue(this, UnsetToDefault(value, property)); accessor.SetValue(this, DirectUnsetToDefault(value, property));
} }
else else
{ {
@ -567,19 +560,6 @@ namespace Perspex
} }
} }
/// <summary>
/// Converts an unset value to the default value for a property type.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="property">The property.</param>
/// <returns>The value.</returns>
private static object UnsetToDefault(object value, PerspexProperty property)
{
return value == PerspexProperty.UnsetValue ?
TypeUtilities.Default(property.PropertyType) :
value;
}
/// <summary> /// <summary>
/// Creates a <see cref="PriorityValue"/> for a <see cref="PerspexProperty"/>. /// Creates a <see cref="PriorityValue"/> for a <see cref="PerspexProperty"/>.
/// </summary> /// </summary>
@ -626,6 +606,19 @@ namespace Perspex
return result; return result;
} }
/// <summary>
/// Converts an unset value to the default value for a direct property.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="property">The property.</param>
/// <returns>The value.</returns>
private object DirectUnsetToDefault(object value, PerspexProperty property)
{
return value == PerspexProperty.UnsetValue ?
((IDirectPropertyMetadata)property.GetMetadata(GetType())).UnsetValue :
value;
}
/// <summary> /// <summary>
/// Gets the default value for a property. /// Gets the default value for a property.
/// </summary> /// </summary>

9
src/Perspex.Base/PerspexProperty.cs

@ -342,18 +342,25 @@ namespace Perspex
/// <param name="name">The name of the property.</param> /// <param name="name">The name of the property.</param>
/// <param name="getter">Gets the current value of the property.</param> /// <param name="getter">Gets the current value of the property.</param>
/// <param name="setter">Sets the value of the property.</param> /// <param name="setter">Sets the value of the property.</param>
/// <param name="unsetValue">
/// The value to use when the property is set to <see cref="PerspexProperty.UnsetValue"/>
/// </param>
/// <param name="defaultBindingMode">The default binding mode for the property.</param> /// <param name="defaultBindingMode">The default binding mode for the property.</param>
/// <returns>A <see cref="PerspexProperty{TValue}"/></returns> /// <returns>A <see cref="PerspexProperty{TValue}"/></returns>
public static DirectProperty<TOwner, TValue> RegisterDirect<TOwner, TValue>( public static DirectProperty<TOwner, TValue> RegisterDirect<TOwner, TValue>(
string name, string name,
Func<TOwner, TValue> getter, Func<TOwner, TValue> getter,
Action<TOwner, TValue> setter = null, Action<TOwner, TValue> setter = null,
TValue unsetValue = default(TValue),
BindingMode defaultBindingMode = BindingMode.OneWay) BindingMode defaultBindingMode = BindingMode.OneWay)
where TOwner : IPerspexObject where TOwner : IPerspexObject
{ {
Contract.Requires<ArgumentNullException>(name != null); Contract.Requires<ArgumentNullException>(name != null);
var metadata = new PropertyMetadata(defaultBindingMode); var metadata = new DirectPropertyMetadata<TValue>(
unsetValue: unsetValue,
defaultBindingMode: defaultBindingMode);
var result = new DirectProperty<TOwner, TValue>(name, getter, setter, metadata); var result = new DirectProperty<TOwner, TValue>(name, getter, setter, metadata);
PerspexPropertyRegistry.Instance.Register(typeof(TOwner), result); PerspexPropertyRegistry.Instance.Register(typeof(TOwner), result);
return result; return result;

6
src/Perspex.Controls/Primitives/SelectingItemsControl.cs

@ -53,7 +53,8 @@ namespace Perspex.Controls.Primitives
PerspexProperty.RegisterDirect<SelectingItemsControl, int>( PerspexProperty.RegisterDirect<SelectingItemsControl, int>(
nameof(SelectedIndex), nameof(SelectedIndex),
o => o.SelectedIndex, o => o.SelectedIndex,
(o, v) => o.SelectedIndex = v); (o, v) => o.SelectedIndex = v,
unsetValue: -1);
/// <summary> /// <summary>
/// Defines the <see cref="SelectedItem"/> property. /// Defines the <see cref="SelectedItem"/> property.
@ -62,7 +63,8 @@ namespace Perspex.Controls.Primitives
PerspexProperty.RegisterDirect<SelectingItemsControl, object>( PerspexProperty.RegisterDirect<SelectingItemsControl, object>(
nameof(SelectedItem), nameof(SelectedItem),
o => o.SelectedItem, o => o.SelectedItem,
(o, v) => o.SelectedItem = v, BindingMode.TwoWay); (o, v) => o.SelectedItem = v,
defaultBindingMode: BindingMode.TwoWay);
/// <summary> /// <summary>
/// Defines the <see cref="SelectedItems"/> property. /// Defines the <see cref="SelectedItems"/> property.

3
src/Perspex.Controls/Primitives/ToggleButton.cs

@ -13,7 +13,8 @@ namespace Perspex.Controls.Primitives
PerspexProperty.RegisterDirect<ToggleButton, bool>( PerspexProperty.RegisterDirect<ToggleButton, bool>(
"IsChecked", "IsChecked",
o => o.IsChecked, o => o.IsChecked,
(o,v) => o.IsChecked = v, BindingMode.TwoWay); (o,v) => o.IsChecked = v,
defaultBindingMode: BindingMode.TwoWay);
private bool _isChecked; private bool _isChecked;

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

@ -62,7 +62,7 @@ namespace Perspex.Base.UnitTests
target.SetValue((PerspexProperty)Class1.BazProperty, PerspexProperty.UnsetValue); target.SetValue((PerspexProperty)Class1.BazProperty, PerspexProperty.UnsetValue);
Assert.Equal(0, target.Baz); Assert.Equal(-1, target.Baz);
} }
[Fact] [Fact]
@ -160,7 +160,7 @@ namespace Perspex.Base.UnitTests
} }
[Fact] [Fact]
public void Bind_NonGeneric_Coerces_UnsetValue() public void Bind_NonGeneric_Uses_UnsetValue()
{ {
var target = new Class1(); var target = new Class1();
var source = new Subject<object>(); var source = new Subject<object>();
@ -171,7 +171,7 @@ namespace Perspex.Base.UnitTests
source.OnNext(6); source.OnNext(6);
Assert.Equal(6, target.Baz); Assert.Equal(6, target.Baz);
source.OnNext(PerspexProperty.UnsetValue); source.OnNext(PerspexProperty.UnsetValue);
Assert.Equal(0, target.Baz); Assert.Equal(-1, target.Baz);
} }
[Fact] [Fact]
@ -282,6 +282,17 @@ namespace Perspex.Base.UnitTests
Assert.Equal("newvalue", target.Foo); Assert.Equal("newvalue", target.Foo);
} }
[Fact]
public void UnsetValue_Is_Used_On_AddOwnered_Property()
{
var target = new Class2();
target.SetValue((PerspexProperty)Class1.FooProperty, PerspexProperty.UnsetValue);
Assert.Equal("unset", target.Foo);
}
[Fact] [Fact]
public void Bind_Binds_AddOwnered_Property_Value() public void Bind_Binds_AddOwnered_Property_Value()
{ {
@ -342,13 +353,21 @@ namespace Perspex.Base.UnitTests
private class Class1 : PerspexObject private class Class1 : PerspexObject
{ {
public static readonly DirectProperty<Class1, string> FooProperty = public static readonly DirectProperty<Class1, string> FooProperty =
PerspexProperty.RegisterDirect<Class1, string>("Foo", o => o.Foo, (o, v) => o.Foo = v); PerspexProperty.RegisterDirect<Class1, string>(
"Foo",
o => o.Foo,
(o, v) => o.Foo = v,
unsetValue: "unset");
public static readonly DirectProperty<Class1, string> BarProperty = public static readonly DirectProperty<Class1, string> BarProperty =
PerspexProperty.RegisterDirect<Class1, string>("Bar", o => o.Bar); PerspexProperty.RegisterDirect<Class1, string>("Bar", o => o.Bar);
public static readonly DirectProperty<Class1, int> BazProperty = public static readonly DirectProperty<Class1, int> BazProperty =
PerspexProperty.RegisterDirect<Class1, int>("Bar", o => o.Baz, (o,v) => o.Baz = v); PerspexProperty.RegisterDirect<Class1, int>(
"Bar",
o => o.Baz,
(o,v) => o.Baz = v,
unsetValue: -1);
private string _foo = "initial"; private string _foo = "initial";
private readonly string _bar = "bar"; private readonly string _bar = "bar";

Loading…
Cancel
Save