diff --git a/src/Perspex.Base/AttachedProperty.cs b/src/Perspex.Base/AttachedProperty.cs
new file mode 100644
index 0000000000..b713f61a07
--- /dev/null
+++ b/src/Perspex.Base/AttachedProperty.cs
@@ -0,0 +1,41 @@
+// 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 System;
+using Perspex.Data;
+
+namespace Perspex
+{
+ ///
+ /// An attached perspex property.
+ ///
+ /// The type of the property's value.
+ public class AttachedProperty : StyledPropertyBase
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the property.
+ /// The class that is registering the property.
+ /// The default value of the property.
+ /// Whether the property inherits its value.
+ /// The default binding mode for the property.
+ /// A validation function.
+ public AttachedProperty(
+ string name,
+ Type ownerType,
+ TValue defaultValue = default(TValue),
+ bool inherits = false,
+ BindingMode defaultBindingMode = BindingMode.Default,
+ Func validate = null)
+ : base(name, ownerType, defaultValue, inherits, defaultBindingMode, validate)
+ {
+ }
+
+ ///
+ public override string FullName => OwnerType + "." + Name;
+
+ ///
+ public override bool IsAttached => true;
+ }
+}
diff --git a/src/Perspex.Base/DirectProperty.cs b/src/Perspex.Base/DirectProperty.cs
new file mode 100644
index 0000000000..d36eb675cf
--- /dev/null
+++ b/src/Perspex.Base/DirectProperty.cs
@@ -0,0 +1,109 @@
+// 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 System;
+
+namespace Perspex
+{
+ ///
+ /// A direct perspex property.
+ ///
+ /// The class that registered the property.
+ /// The type of the property's value.
+ ///
+ /// Direct perspex properties are backed by a field on the object, but exposed via the
+ /// system. They hold a getter and an optional setter which
+ /// allows the perspex property system to read and write the current value.
+ ///
+ public class DirectProperty : PerspexProperty, IDirectPropertyAccessor
+ where TOwner : IPerspexObject
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the property.
+ /// Gets the current value of the property.
+ /// Sets the value of the property. May be null.
+ public DirectProperty(
+ string name,
+ Func getter,
+ Action setter = null)
+ : base(name, typeof(TOwner))
+ {
+ Contract.Requires(getter != null);
+
+ Getter = getter;
+ Setter = setter;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The property to copy.
+ /// Gets the current value of the property.
+ /// Sets the value of the property. May be null.
+ private DirectProperty(
+ PerspexProperty source,
+ Func getter,
+ Action setter)
+ : base(source, typeof(TOwner))
+ {
+ Contract.Requires(getter != null);
+
+ Getter = getter;
+ Setter = setter;
+ }
+
+ ///
+ public override bool IsDirect => true;
+
+ ///
+ public override bool IsReadOnly => Setter == null;
+
+ ///
+ /// Gets the getter function.
+ ///
+ public Func Getter { get; }
+
+ ///
+ /// Gets the setter function.
+ ///
+ public Action Setter { get; }
+
+ ///
+ /// Registers the direct property on another type.
+ ///
+ /// The type of the additional owner.
+ /// The property.
+ public DirectProperty AddOwner(
+ Func getter,
+ Action setter = null)
+ where TNewOwner : PerspexObject
+ {
+ var result = new DirectProperty(
+ this,
+ getter,
+ setter);
+
+ PerspexPropertyRegistry.Instance.Register(typeof(TOwner), result);
+ return result;
+ }
+
+ ///
+ object IDirectPropertyAccessor.GetValue(IPerspexObject instance)
+ {
+ return Getter((TOwner)instance);
+ }
+
+ ///
+ void IDirectPropertyAccessor.SetValue(IPerspexObject instance, object value)
+ {
+ if (Setter == null)
+ {
+ throw new ArgumentException($"The property {Name} is readonly.");
+ }
+
+ Setter((TOwner)instance, (TValue)value);
+ }
+ }
+}
diff --git a/src/Perspex.Base/IDirectPropertyAccessor.cs b/src/Perspex.Base/IDirectPropertyAccessor.cs
new file mode 100644
index 0000000000..497be4f122
--- /dev/null
+++ b/src/Perspex.Base/IDirectPropertyAccessor.cs
@@ -0,0 +1,31 @@
+// 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
+{
+ ///
+ /// Provides a runtime interface for getting and setting
+ /// values.
+ ///
+ internal interface IDirectPropertyAccessor
+ {
+ ///
+ /// Gets a value indicating whether the property is read-only.
+ ///
+ bool IsReadOnly { get; }
+
+ ///
+ /// Gets the value of the property on the instance.
+ ///
+ /// The instance.
+ /// The property value.
+ object GetValue(IPerspexObject instance);
+
+ ///
+ /// Sets the value of the property on the instance.
+ ///
+ /// The instance.
+ /// The value.
+ void SetValue(IPerspexObject instance, object value);
+ }
+}
diff --git a/src/Perspex.Base/IStyledPropertyAccessor.cs b/src/Perspex.Base/IStyledPropertyAccessor.cs
new file mode 100644
index 0000000000..c8207332a4
--- /dev/null
+++ b/src/Perspex.Base/IStyledPropertyAccessor.cs
@@ -0,0 +1,31 @@
+// 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 System;
+
+namespace Perspex
+{
+ ///
+ /// Provides a runtime interface for interfacing with .
+ ///
+ internal interface IStyledPropertyAccessor
+ {
+ ///
+ /// Gets the default value for the property for the specified type.
+ ///
+ /// The type.
+ ///
+ /// The default value.
+ ///
+ object GetDefaultValue(Type type);
+
+ ///
+ /// Gets a validation function for the property on the specified type.
+ ///
+ /// The type.
+ ///
+ /// The validation function, or null if no validation function exists.
+ ///
+ Func GetValidationFunc(Type type);
+ }
+}
diff --git a/src/Perspex.Base/Perspex.Base.csproj b/src/Perspex.Base/Perspex.Base.csproj
index 3a61fbc996..8b44506c15 100644
--- a/src/Perspex.Base/Perspex.Base.csproj
+++ b/src/Perspex.Base/Perspex.Base.csproj
@@ -48,12 +48,18 @@
+
+
+
+
+
+
diff --git a/src/Perspex.Base/PerspexObject.cs b/src/Perspex.Base/PerspexObject.cs
index 5310651514..e550c96302 100644
--- a/src/Perspex.Base/PerspexObject.cs
+++ b/src/Perspex.Base/PerspexObject.cs
@@ -57,21 +57,21 @@ namespace Perspex
new PropertyEnricher("Id", GetHashCode()),
});
- foreach (var property in PerspexPropertyRegistry.Instance.GetRegistered(this))
- {
- object value = property.IsDirect ?
- property.Getter(this) :
- property.GetDefaultValue(GetType());
-
- var e = new PerspexPropertyChangedEventArgs(
- this,
- property,
- PerspexProperty.UnsetValue,
- value,
- BindingPriority.Unset);
-
- property.NotifyInitialized(e);
- }
+ ////foreach (var property in PerspexPropertyRegistry.Instance.GetRegistered(this))
+ ////{
+ //// object value = property.IsDirect ?
+ //// ((IDirectPropertyAccessor)property).GetValue(this) :
+ //// property.GetDefaultValue(GetType());
+
+ //// var e = new PerspexPropertyChangedEventArgs(
+ //// this,
+ //// property,
+ //// PerspexProperty.UnsetValue,
+ //// value,
+ //// BindingPriority.Unset);
+
+ //// property.NotifyInitialized(e);
+ ////}
}
///
@@ -229,7 +229,7 @@ namespace Perspex
if (property.IsDirect)
{
- return GetRegistered(property).Getter(this);
+ return ((IDirectPropertyAccessor)GetRegistered(property)).GetValue(this);
}
else
{
@@ -267,7 +267,7 @@ namespace Perspex
if (property.IsDirect)
{
- return ((PerspexProperty)GetRegistered(property)).Getter(this);
+ return (T)((IDirectPropertyAccessor)GetRegistered(property)).GetValue(this);
}
else
{
@@ -310,15 +310,9 @@ namespace Perspex
if (property.IsDirect)
{
- property = GetRegistered(property);
-
- if (property.Setter == null)
- {
- throw new ArgumentException($"The property {property.Name} is readonly.");
- }
-
+ var accessor = (IDirectPropertyAccessor)GetRegistered(property);
LogPropertySet(property, value, priority);
- property.Setter(this, UnsetToDefault(value, property));
+ accessor.SetValue(this, UnsetToDefault(value, property));
}
else
{
@@ -368,23 +362,8 @@ namespace Perspex
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires(property != null);
- VerifyAccess();
- if (property.IsDirect)
- {
- property = (PerspexProperty)GetRegistered(property);
-
- if (property.Setter == null)
- {
- throw new ArgumentException($"The property {property.Name} is readonly.");
- }
- LogPropertySet(property, value, priority);
- property.Setter(this, value);
- }
- else
- {
- SetValue((PerspexProperty)property, value, priority);
- }
+ SetValue((PerspexProperty)property, value, priority);
}
///
@@ -406,9 +385,7 @@ namespace Perspex
if (property.IsDirect)
{
- property = GetRegistered(property);
-
- if (property.Setter == null)
+ if (property.IsReadOnly)
{
throw new ArgumentException($"The property {property.Name} is readonly.");
}
@@ -463,22 +440,8 @@ namespace Perspex
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires(property != null);
- VerifyAccess();
- if (property.IsDirect)
- {
- property = (PerspexProperty)GetRegistered(property);
- if (property.Setter == null)
- {
- throw new ArgumentException($"The property {property.Name} is readonly.");
- }
-
- return source.Subscribe(x => SetValue(property, x));
- }
- else
- {
- return Bind((PerspexProperty)property, source.Select(x => (object)x), priority);
- }
+ return Bind((PerspexProperty)property, source.Select(x => (object)x), priority);
}
///
@@ -621,7 +584,7 @@ namespace Perspex
/// The .
private PriorityValue CreatePriorityValue(PerspexProperty property)
{
- Func validate = property.GetValidationFunc(GetType());
+ var validate = ((IStyledPropertyAccessor)property).GetValidationFunc(GetType());
Func
private readonly Subject _changed;
- ///
- /// The validation functions for the property, by type.
- ///
- private readonly Dictionary> _validation;
-
///
/// Gets the ID of the property.
///
@@ -65,26 +44,18 @@ namespace Perspex
/// The name of the property.
/// The type of the property's value.
/// The type of the class that registers the property.
- /// The default value of the property.
- /// Whether the property inherits its value.
/// The default binding mode for the property.
- /// A validation function.
///
/// A method that gets called before and after the property starts being notified on an
/// object; the bool argument will be true before and false afterwards. This callback is
/// intended to support IsDataContextChanging.
///
- /// Whether the property is an attached property.
- public PerspexProperty(
+ protected PerspexProperty(
string name,
Type valueType,
Type ownerType,
- object defaultValue,
- bool inherits = false,
BindingMode defaultBindingMode = BindingMode.Default,
- Func validate = null,
- Action notifying = null,
- bool isAttached = false)
+ Action notifying = null)
{
Contract.Requires(name != null);
Contract.Requires(valueType != null);
@@ -95,64 +66,15 @@ namespace Perspex
throw new ArgumentException("'name' may not contain periods.");
}
- _defaultValues = new Dictionary();
_initialized = new Subject();
_changed = new Subject();
- _validation = new Dictionary>();
Name = name;
PropertyType = valueType;
OwnerType = ownerType;
- _defaultValue = defaultValue;
- Inherits = inherits;
DefaultBindingMode = defaultBindingMode;
- IsAttached = isAttached;
Notifying = notifying;
_id = s_nextId++;
-
- if (validate != null)
- {
- _validation.Add(ownerType, validate);
- }
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The name of the property.
- /// The type of the property's value.
- /// The type of the class that registers the property.
- /// Gets the current value of the property.
- /// Sets the value of the property.
- public PerspexProperty(
- string name,
- Type valueType,
- Type ownerType,
- Func getter,
- Action setter)
- {
- Contract.Requires(name != null);
- Contract.Requires(valueType != null);
- Contract.Requires(ownerType != null);
- Contract.Requires(getter != null);
-
- if (name.Contains("."))
- {
- throw new ArgumentException("'name' may not contain periods.");
- }
-
- _defaultValues = new Dictionary();
- _initialized = new Subject();
- _changed = new Subject();
- _validation = new Dictionary>();
-
- Name = name;
- PropertyType = valueType;
- OwnerType = ownerType;
- Getter = getter;
- Setter = setter;
- IsDirect = true;
- _id = s_nextId++;
}
///
@@ -171,117 +93,62 @@ namespace Perspex
"This method cannot be called on direct PerspexProperties.");
}
- _defaultValues = source._defaultValues;
_initialized = source._initialized;
_changed = source._changed;
- _validation = source._validation;
Name = source.Name;
PropertyType = source.PropertyType;
OwnerType = ownerType;
- _defaultValue = source._defaultValue;
- Inherits = source.Inherits;
DefaultBindingMode = source.DefaultBindingMode;
- IsAttached = false;
Notifying = Notifying;
- _validation = source._validation;
_id = source._id;
}
///
- /// Initializes a new instance of the class.
+ /// Gets the name of the property.
///
- /// The direct property to copy.
- /// The new owner type.
- /// A new getter.
- /// A new setter.
- protected PerspexProperty(
- PerspexProperty source,
- Type ownerType,
- Func getter,
- Action setter)
- {
- Contract.Requires(source != null);
- Contract.Requires(ownerType != null);
- Contract.Requires(getter != null);
-
- if (!source.IsDirect)
- {
- throw new InvalidOperationException(
- "This method can only be called on direct PerspexProperties.");
- }
-
- _defaultValues = source._defaultValues;
- _initialized = source._initialized;
- _changed = source._changed;
- _validation = source._validation;
-
- Name = source.Name;
- PropertyType = source.PropertyType;
- OwnerType = ownerType;
- Getter = getter;
- Setter = setter;
- IsDirect = true;
- _id = source._id;
- }
+ public string Name { get; }
///
- /// Gets the name of the property.
+ /// Gets the full name of the property, wich includes the owner type in the case of
+ /// attached properties.
///
- ///
- /// The name of the property.
- ///
- public string Name { get; }
+ public virtual string FullName => Name;
///
/// Gets the type of the property's value.
///
- ///
- /// The type of the property's value.
- ///
public Type PropertyType { get; }
///
- /// Gets the type of the class that registers the property.
+ /// Gets the type of the class that registered the property.
///
- ///
- /// The type of the class that registers the property.
- ///
public Type OwnerType { get; }
///
/// Gets a value indicating whether the property inherits its value.
///
- ///
- /// A value indicating whether the property inherits its value.
- ///
- public bool Inherits { get; }
+ public virtual bool Inherits => false;
///
/// Gets the default binding mode for the property.
///
- ///
- /// The default binding mode for the property.
- ///
public BindingMode DefaultBindingMode { get; }
///
/// Gets a value indicating whether this is an attached property.
///
- ///
- /// A value indicating whether this is an attached property.
- ///
- public bool IsAttached { get; }
+ public virtual bool IsAttached => false;
///
/// Gets a value indicating whether this is a direct property.
///
- public bool IsDirect { get; }
+ public virtual bool IsDirect => false;
///
/// Gets a value indicating whether this is a readonly property.
///
- public bool IsReadOnly => IsDirect && Setter == null;
+ public virtual bool IsReadOnly => false;
///
/// Gets an observable that is fired when this property is initialized on a
@@ -349,16 +216,6 @@ namespace Perspex
};
}
- ///
- /// Gets the getter function for direct properties.
- ///
- internal Func Getter { get; }
-
- ///
- /// Gets the etter function for direct properties.
- ///
- internal Action Setter { get; }
-
///
/// Tests two s for equality.
///
@@ -407,55 +264,26 @@ namespace Perspex
/// object; the bool argument will be true before and false afterwards. This callback is
/// intended to support IsDataContextChanging.
///
- /// A
- public static PerspexProperty Register(
+ /// A
+ public static StyledProperty Register(
string name,
TValue defaultValue = default(TValue),
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay,
Func validate = null,
- Action notifying = null)
- where TOwner : PerspexObject
+ Action notifying = null)
+ where TOwner : IPerspexObject
{
Contract.Requires(name != null);
- PerspexProperty result = new PerspexProperty(
+ var result = new StyledProperty(
name,
typeof(TOwner),
defaultValue,
inherits,
defaultBindingMode,
Cast(validate),
- notifying,
- false);
-
- PerspexPropertyRegistry.Instance.Register(typeof(TOwner), result);
-
- return result;
- }
-
- ///
- /// Registers a direct .
- ///
- /// The type of the class that is registering the property.
- /// The type of the property's value.
- /// The name of the property.
- /// Gets the current value of the property.
- /// Sets the value of the property.
- /// A
- public static PerspexProperty RegisterDirect(
- string name,
- Func getter,
- Action setter = null)
- where TOwner : PerspexObject
- {
- Contract.Requires(name != null);
-
- PerspexProperty result = new PerspexProperty(
- name,
- typeof(TOwner),
- Cast(getter),
- Cast(setter));
+ notifying);
PerspexPropertyRegistry.Instance.Register(typeof(TOwner), result);
@@ -474,24 +302,23 @@ namespace Perspex
/// The default binding mode for the property.
/// A validation function.
/// A
- public static PerspexProperty RegisterAttached(
+ public static AttachedProperty RegisterAttached(
string name,
TValue defaultValue = default(TValue),
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay,
- Func validate = null)
+ Func validate = null)
+ where THost : IPerspexObject
{
Contract.Requires(name != null);
- PerspexProperty result = new PerspexProperty(
+ var result = new AttachedProperty(
name,
typeof(TOwner),
defaultValue,
inherits,
defaultBindingMode,
- validate,
- null,
- true);
+ Cast(validate));
PerspexPropertyRegistry.Instance.Register(typeof(THost), result);
@@ -516,25 +343,46 @@ namespace Perspex
TValue defaultValue = default(TValue),
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay,
- Func validate = null)
+ Func validate = null)
+ where THost : IPerspexObject
{
Contract.Requires(name != null);
- PerspexProperty result = new PerspexProperty(
+ var result = new AttachedProperty(
name,
ownerType,
defaultValue,
inherits,
defaultBindingMode,
- validate,
- null,
- true);
+ Cast(validate));
PerspexPropertyRegistry.Instance.Register(typeof(THost), result);
return result;
}
+ ///
+ /// Registers a direct .
+ ///
+ /// The type of the class that is registering the property.
+ /// The type of the property's value.
+ /// The name of the property.
+ /// Gets the current value of the property.
+ /// Sets the value of the property.
+ /// A
+ public static DirectProperty RegisterDirect(
+ string name,
+ Func getter,
+ Action setter = null)
+ where TOwner : IPerspexObject
+ {
+ Contract.Requires(name != null);
+
+ var result = new DirectProperty(name, getter, setter);
+ PerspexPropertyRegistry.Instance.Register(typeof(TOwner), result);
+ return result;
+ }
+
///
public override bool Equals(object obj)
{
@@ -570,56 +418,6 @@ namespace Perspex
};
}
- ///
- /// Gets the default value for the property on the specified type.
- ///
- /// The type.
- /// The default value.
- public object GetDefaultValue(Type type)
- {
- Contract.Requires(type != null);
-
- while (type != null)
- {
- object result;
-
- if (_defaultValues.TryGetValue(type, out result))
- {
- return result;
- }
-
- type = type.GetTypeInfo().BaseType;
- }
-
- return _defaultValue;
- }
-
- ///
- /// Gets the validation function for the property on the specified type.
- ///
- /// The type.
- ///
- /// The validation function, or null if no validation function registered for this type.
- ///
- public Func GetValidationFunc(Type type)
- {
- Contract.Requires(type != null);
-
- while (type != null)
- {
- Func result;
-
- if (_validation.TryGetValue(type, out result))
- {
- return result;
- }
-
- type = type.GetTypeInfo().BaseType;
- }
-
- return null;
- }
-
///
/// Checks whether the is valid for the property.
///
@@ -630,59 +428,6 @@ namespace Perspex
return TypeUtilities.TryCast(PropertyType, value, out value);
}
- ///
- /// Overrides the default value for the property on the specified type.
- ///
- /// The type.
- /// The default value.
- public void OverrideDefaultValue(object defaultValue)
- {
- OverrideDefaultValue(typeof(T), defaultValue);
- }
-
- ///
- /// Overrides the default value for the property on the specified type.
- ///
- /// The type.
- /// The default value.
- public void OverrideDefaultValue(Type type, object defaultValue)
- {
- Contract.Requires(type != null);
-
- if (!TypeUtilities.TryCast(PropertyType, defaultValue, out defaultValue))
- {
- throw new InvalidOperationException(string.Format(
- "Invalid value for Property '{0}': {1} ({2})",
- Name,
- defaultValue,
- defaultValue.GetType().FullName));
- }
-
- if (_defaultValues.ContainsKey(type))
- {
- throw new InvalidOperationException("Default value is already set for this property.");
- }
-
- _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 (_validation.ContainsKey(type))
- {
- throw new InvalidOperationException("Validation is already set for this property.");
- }
-
- _validation.Add(type, validation);
- }
-
///
/// Gets the string representation of the property.
///
@@ -711,45 +456,24 @@ namespace Perspex
}
///
- /// Casts a getter function accepting a typed owner to one accepting a
- /// .
+ /// Casts a validation function accepting a typed owner to one accepting an
+ /// .
///
/// The owner type.
/// The property value type.
/// The typed function.
/// The untyped function.
- private static Func Cast(Func f)
- where TOwner : PerspexObject
+ protected static Func Cast(Func f)
+ where TOwner : IPerspexObject
{
- return (f != null) ? o => f((TOwner)o) : (Func)null;
- }
-
- ///
- /// Casts a setter action accepting a typed owner to one accepting a
- /// .
- ///
- /// The owner type.
- /// The property value type.
- /// The typed action.
- /// The untyped action.
- private static Action Cast(Action f)
- where TOwner : PerspexObject
- {
- return f != null ? (o, v) => f((TOwner)o, v) : (Action)null;
- }
-
- ///
- /// 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)null;
+ if (f == null)
+ {
+ return null;
+ }
+ else
+ {
+ return (o, v) => f((TOwner)o, v);
+ }
}
///
@@ -761,10 +485,7 @@ namespace Perspex
/// Returns the string representation of the .
///
/// The string "(unset)".
- public override string ToString()
- {
- return "(unset)";
- }
+ public override string ToString() => "(unset)";
}
}
}
diff --git a/src/Perspex.Base/PerspexProperty`1.cs b/src/Perspex.Base/PerspexProperty`1.cs
index dae476da31..81b11b4397 100644
--- a/src/Perspex.Base/PerspexProperty`1.cs
+++ b/src/Perspex.Base/PerspexProperty`1.cs
@@ -17,217 +17,34 @@ namespace Perspex
///
/// The name of the property.
/// The type of the class that registers the property.
- /// The default value of the property.
- /// Whether the property inherits its value.
/// The default binding mode for the property.
- /// A validation function.
///
/// A method that gets called before and after the property starts being notified on an
/// object; the bool argument will be true before and false afterwards. This callback is
/// intended to support IsDataContextChanging.
///
- /// Whether the property is an attached property.
- public PerspexProperty(
+ protected PerspexProperty(
string name,
Type ownerType,
- TValue defaultValue = default(TValue),
- bool inherits = false,
BindingMode defaultBindingMode = BindingMode.Default,
- Func validate = null,
- Action notifying = null,
- bool isAttached = false)
+ Action notifying = null)
: base(
name,
typeof(TValue),
ownerType,
- defaultValue,
- inherits,
defaultBindingMode,
- Cast(validate),
- notifying,
- isAttached)
+ notifying)
{
}
- ///
- /// Initializes a new instance of the class.
- ///
- /// The name of the property.
- /// The type of the class that registers the property.
- /// Gets the current value of the property.
- /// Sets the value of the property.
- public PerspexProperty(
- string name,
- Type ownerType,
- Func getter,
- Action setter)
- : base(name, typeof(TValue), ownerType, CastParamReturn(getter), CastParams(setter))
- {
- Getter = getter;
- Setter = setter;
- }
-
///
/// Initializes a new instance of the class.
///
/// The property to copy.
/// The new owner type.
- private PerspexProperty(PerspexProperty source, Type ownerType)
+ protected PerspexProperty(PerspexProperty source, Type ownerType)
: base(source, ownerType)
{
}
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The direct property to copy.
- /// The new owner type.
- /// A new getter.
- /// A new setter.
- private PerspexProperty(
- PerspexProperty source,
- Type ownerType,
- Func getter,
- Action setter)
- : base(source, ownerType, CastParamReturn(getter), CastParams(setter))
- {
- Getter = getter;
- Setter = setter;
- }
-
- ///
- /// Gets the getter function for direct properties.
- ///
- internal new Func Getter { get; }
-
- ///
- /// Gets the etter function for direct properties.
- ///
- internal new Action Setter { get; }
-
- ///
- /// Registers the property on another type.
- ///
- /// The type of the additional owner.
- /// The property.
- public PerspexProperty AddOwner() where TOwner : PerspexObject
- {
- if (IsDirect)
- {
- throw new InvalidOperationException(
- "You must provide a new getter and setter when calling AddOwner on a direct PerspexProperty.");
- }
-
- var result = new PerspexProperty(this, typeof(TOwner));
- PerspexPropertyRegistry.Instance.Register(typeof(TOwner), result);
- return result;
- }
-
- ///
- /// Registers the direct property on another type.
- ///
- /// The type of the additional owner.
- /// The property.
- public PerspexProperty AddOwner(
- Func getter,
- Action setter = null)
- where TOwner : PerspexObject
- {
- if (!IsDirect)
- {
- throw new InvalidOperationException(
- "This overload of AddOwner is for direct PerspexProperties.");
- }
-
- var result = new PerspexProperty(
- this,
- typeof(TOwner),
- CastReturn(getter),
- CastParam1(setter));
-
- PerspexPropertyRegistry.Instance.Register(typeof(TOwner), result);
- return result;
- }
-
- ///
- /// Gets the default value for the property on the specified type.
- ///
- /// The type.
- /// The default value.
- public TValue GetDefaultValue()
- {
- return (TValue)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;
- OverrideValidation(typeof(T), f);
- }
-
- ///
- /// Casts a typed getter function to an untyped.
- ///
- /// The owner type.
- /// The typed function.
- /// The untyped function.
- private static Func CastParamReturn(Func f)
- where TOwner : PerspexObject
- {
- return (f != null) ? o => f((TOwner)o) : (Func)null;
- }
-
- ///
- /// Casts a typed getter function to an untyped.
- ///
- /// The owner type.
- /// The typed function.
- /// The untyped function.
- private static Func CastReturn(Func f)
- where TOwner : PerspexObject
- {
- return (f != null) ? o => f((TOwner)o) : (Func)null;
- }
-
- ///
- /// Casts a typed setter function to an untyped.
- ///
- /// The owner type.
- /// The typed function.
- /// The untyped function.
- private static Action CastParams(Action f)
- where TOwner : PerspexObject
- {
- return (f != null) ? (o, v) => f((TOwner)o, (TValue)v) : (Action)null;
- }
-
- ///
- /// Casts a typed setter function to an untyped.
- ///
- /// The owner type.
- /// The typed function.
- /// The untyped function.
- private static Action CastParam1(Action f)
- where TOwner : PerspexObject
- {
- return (f != null) ? (o, v) => f((TOwner)o, v) : (Action)null;
- }
-
- ///
- /// Casts a typed validation function to an untyped.
- ///
- /// The typed validation function.
- /// The untyped validation function.
- private static Func Cast(Func f)
- {
- return f != null ? (o, v) => f(o, (TValue)v) : (Func)null;
- }
}
}
diff --git a/src/Perspex.Base/StyledProperty.cs b/src/Perspex.Base/StyledProperty.cs
new file mode 100644
index 0000000000..2d78b5c872
--- /dev/null
+++ b/src/Perspex.Base/StyledProperty.cs
@@ -0,0 +1,40 @@
+// 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 System;
+using Perspex.Data;
+
+namespace Perspex
+{
+ ///
+ /// A styled perspex property.
+ ///
+ public class StyledProperty : StyledPropertyBase
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the property.
+ /// The type of the class that registers the property.
+ /// The default value of the property.
+ /// Whether the property inherits its value.
+ /// The default binding mode for the property.
+ /// A validation function.
+ ///
+ /// A method that gets called before and after the property starts being notified on an
+ /// object; the bool argument will be true before and false afterwards. This callback is
+ /// intended to support IsDataContextChanging.
+ ///
+ public StyledProperty(
+ string name,
+ Type ownerType,
+ TValue defaultValue,
+ bool inherits = false,
+ BindingMode defaultBindingMode = BindingMode.Default,
+ Func validate = null,
+ Action notifying = null)
+ : base(name, ownerType, defaultValue, inherits, defaultBindingMode, validate, notifying)
+ {
+ }
+ }
+}
diff --git a/src/Perspex.Base/StyledPropertyBase.cs b/src/Perspex.Base/StyledPropertyBase.cs
new file mode 100644
index 0000000000..90723fdf58
--- /dev/null
+++ b/src/Perspex.Base/StyledPropertyBase.cs
@@ -0,0 +1,204 @@
+// 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 System;
+using System.Collections.Generic;
+using System.Reflection;
+using Perspex.Data;
+
+namespace Perspex
+{
+ ///
+ /// Base class for styled properties.
+ ///
+ public class StyledPropertyBase : PerspexProperty, IStyledPropertyAccessor
+ {
+ private readonly TValue _defaultValue;
+ private readonly Dictionary _defaultValues;
+ private bool _inherits;
+ private readonly Dictionary> _validation;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the property.
+ /// The type of the class that registers the property.
+ /// The default value of the property.
+ /// Whether the property inherits its value.
+ /// The default binding mode for the property.
+ /// A validation function.
+ ///
+ /// A method that gets called before and after the property starts being notified on an
+ /// object; the bool argument will be true before and false afterwards. This callback is
+ /// intended to support IsDataContextChanging.
+ ///
+ protected StyledPropertyBase(
+ string name,
+ Type ownerType,
+ TValue defaultValue,
+ bool inherits = false,
+ BindingMode defaultBindingMode = BindingMode.Default,
+ Func validate = null,
+ Action notifying = null)
+ : base(name, ownerType, defaultBindingMode, notifying)
+ {
+ Contract.Requires(name != null);
+ Contract.Requires(ownerType != null);
+
+ if (name.Contains("."))
+ {
+ throw new ArgumentException("'name' may not contain periods.");
+ }
+
+ _defaultValues = new Dictionary();
+ _validation = new Dictionary>();
+
+ _defaultValue = defaultValue;
+ _inherits = inherits;
+
+ if (validate != null)
+ {
+ _validation.Add(ownerType, validate);
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the property inherits its value.
+ ///
+ ///
+ /// A value indicating whether the property inherits its value.
+ ///
+ public override bool Inherits => _inherits;
+
+ ///
+ /// Gets the default value for the property on the specified type.
+ ///
+ /// The type.
+ /// The default value.
+ public TValue GetDefaultValue(Type type)
+ {
+ Contract.Requires(type != null);
+
+ while (type != null)
+ {
+ TValue result;
+
+ if (_defaultValues.TryGetValue(type, out result))
+ {
+ return result;
+ }
+
+ type = type.GetTypeInfo().BaseType;
+ }
+
+ return _defaultValue;
+ }
+
+ ///
+ /// Gets the validation function for the property on the specified type.
+ ///
+ /// The type.
+ ///
+ /// The validation function, or null if no validation function registered for this type.
+ ///
+ public Func GetValidationFunc(Type type)
+ {
+ Contract.Requires(type != null);
+
+ while (type != null)
+ {
+ Func result;
+
+ if (_validation.TryGetValue(type, out result))
+ {
+ return result;
+ }
+
+ type = type.GetTypeInfo().BaseType;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Overrides the default value for the property on the specified type.
+ ///
+ /// The type.
+ /// The default value.
+ public void OverrideDefaultValue(TValue defaultValue) where T : IPerspexObject
+ {
+ OverrideDefaultValue(typeof(T), defaultValue);
+ }
+
+ ///
+ /// Overrides the default value for the property on the specified type.
+ ///
+ /// The type.
+ /// The default value.
+ public void OverrideDefaultValue(Type type, TValue defaultValue)
+ {
+ Contract.Requires(type != null);
+
+ if (_defaultValues.ContainsKey(type))
+ {
+ throw new InvalidOperationException("Default value is already set for this property.");
+ }
+
+ _defaultValues.Add(type, defaultValue);
+ }
+
+ ///
+ /// Overrides the validation function for the property on the specified type.
+ ///
+ /// The type.
+ /// The validation function.
+ public void OverrideValidation(Func validation)
+ where T : IPerspexObject
+ {
+ var type = typeof(T);
+
+ if (_validation.ContainsKey(type))
+ {
+ throw new InvalidOperationException("Validation is already set for this property.");
+ }
+
+ _validation.Add(type, Cast(validation));
+ }
+
+ ///
+ /// 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 (_validation.ContainsKey(type))
+ {
+ throw new InvalidOperationException("Validation is already set for this property.");
+ }
+
+ _validation.Add(type, validation);
+ }
+
+ ///
+ /// Gets the string representation of the property.
+ ///
+ /// The property's string representation.
+ public override string ToString()
+ {
+ return Name;
+ }
+
+ ///
+ Func IStyledPropertyAccessor.GetValidationFunc(Type type)
+ {
+ var typed = GetValidationFunc(type);
+ return (o, v) => typed(o, (TValue)v);
+ }
+
+ ///
+ object IStyledPropertyAccessor.GetDefaultValue(Type type) => GetDefaultValue(type);
+ }
+}
diff --git a/src/Perspex.Controls/Grid.cs b/src/Perspex.Controls/Grid.cs
index c2da194441..1c848f6887 100644
--- a/src/Perspex.Controls/Grid.cs
+++ b/src/Perspex.Controls/Grid.cs
@@ -17,7 +17,9 @@ namespace Perspex.Controls
/// Defines the Column attached property.
///
public static readonly PerspexProperty ColumnProperty =
- PerspexProperty.RegisterAttached("Column");
+ PerspexProperty.RegisterAttached(
+ "Column",
+ validate: ValidateColumn);
///
/// Defines the ColumnSpan attached property.
@@ -29,7 +31,9 @@ namespace Perspex.Controls
/// Defines the Row attached property.
///
public static readonly PerspexProperty RowProperty =
- PerspexProperty.RegisterAttached("Row");
+ PerspexProperty.RegisterAttached(
+ "Row",
+ validate: ValidateRow);
///
/// Defines the RowSpan attached property.
@@ -578,6 +582,26 @@ namespace Perspex.Controls
}
}
+ private static int ValidateColumn(PerspexObject o, int value)
+ {
+ if (value < 0)
+ {
+ throw new ArgumentException("Invalid Grid.Column value.");
+ }
+
+ return value;
+ }
+
+ private static int ValidateRow(PerspexObject o, int value)
+ {
+ if (value < 0)
+ {
+ throw new ArgumentException("Invalid Grid.Row value.");
+ }
+
+ return value;
+ }
+
private void CreateMatrices(int rowCount, int colCount)
{
if (_rowMatrix == null || _colMatrix == null ||
diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs
index 07a4758292..13e36f40a8 100644
--- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs
+++ b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs
@@ -341,13 +341,13 @@ namespace Perspex.Base.UnitTests
private class Class1 : PerspexObject
{
- public static readonly PerspexProperty FooProperty =
+ public static readonly DirectProperty FooProperty =
PerspexProperty.RegisterDirect("Foo", o => o.Foo, (o, v) => o.Foo = v);
- public static readonly PerspexProperty BarProperty =
+ public static readonly DirectProperty BarProperty =
PerspexProperty.RegisterDirect("Bar", o => o.Bar);
- public static readonly PerspexProperty BazProperty =
+ public static readonly DirectProperty BazProperty =
PerspexProperty.RegisterDirect("Bar", o => o.Baz, (o,v) => o.Baz = v);
private string _foo = "initial";
diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_GetValue.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_GetValue.cs
index f143f56c56..c5af5273e9 100644
--- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_GetValue.cs
+++ b/tests/Perspex.Base.UnitTests/PerspexObjectTests_GetValue.cs
@@ -55,10 +55,10 @@ namespace Perspex.Base.UnitTests
private class Class1 : PerspexObject
{
- public static readonly PerspexProperty FooProperty =
+ public static readonly StyledProperty FooProperty =
PerspexProperty.Register("Foo", "foodefault");
- public static readonly PerspexProperty BazProperty =
+ public static readonly StyledProperty BazProperty =
PerspexProperty.Register("Baz", "bazdefault", true);
}
diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Inheritance.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Inheritance.cs
index cfaf89a30a..4c4e233613 100644
--- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Inheritance.cs
+++ b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Inheritance.cs
@@ -77,10 +77,10 @@ namespace Perspex.Base.UnitTests
private class Class1 : PerspexObject
{
- public static readonly PerspexProperty FooProperty =
+ public static readonly StyledProperty FooProperty =
PerspexProperty.Register("Foo", "foodefault");
- public static readonly PerspexProperty BazProperty =
+ public static readonly StyledProperty BazProperty =
PerspexProperty.Register("Baz", "bazdefault", true);
}
diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs
index 422967f858..cfbf31a764 100644
--- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs
+++ b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Validation.cs
@@ -71,7 +71,7 @@ namespace Perspex.Base.UnitTests
private class Class1 : PerspexObject
{
- public static readonly PerspexProperty QuxProperty =
+ public static readonly StyledProperty QuxProperty =
PerspexProperty.Register("Qux", validate: Validate);
public Class1()
@@ -97,7 +97,7 @@ namespace Perspex.Base.UnitTests
private class Class2 : PerspexObject
{
- public static readonly PerspexProperty QuxProperty =
+ public static readonly StyledProperty QuxProperty =
Class1.QuxProperty.AddOwner();
static Class2()