// -----------------------------------------------------------------------
//
// Copyright 2014 MIT Licence. See licence.md for more information.
//
// -----------------------------------------------------------------------
namespace Perspex
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Subjects;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
///
/// A perspex property.
///
///
/// This class is analogous to DependencyProperty in WPF.
///
public class PerspexProperty
{
///
/// Represents an unset property value.
///
public static readonly object UnsetValue = new object();
///
/// The default values for the property, by type.
///
private Dictionary defaultValues = new Dictionary();
///
/// Observable fired when this property changes on any .
///
private Subject changed = new Subject();
///
/// 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.
/// The default value of the property.
/// Whether the property inherits its value.
public PerspexProperty(
string name,
Type valueType,
Type ownerType,
object defaultValue,
bool inherits)
{
Contract.Requires(name != null);
Contract.Requires(valueType != null);
Contract.Requires(ownerType != null);
this.Name = name;
this.PropertyType = valueType;
this.OwnerType = ownerType;
this.Inherits = inherits;
this.defaultValues.Add(ownerType, defaultValue);
}
///
/// Gets the name of the property.
///
public string Name { get; private set; }
///
/// Gets the type of the property's value.
///
public Type PropertyType { get; private set; }
///
/// Gets the type of the class that registers the property.
///
public Type OwnerType { get; private set; }
///
/// Gets a value indicating whether the property inherits its value.
///
public bool Inherits { get; private set; }
///
/// Gets an observable that is fired when this property changes on any
/// instance.
///
public IObservable Changed
{
get { return this.changed; }
}
///
/// Registers a .
///
/// The type of the class that is registering the property.
/// The type of the property's value.
/// The name of the property.
/// The default value of the property.
/// Whether the property inherits its value.
///
public static PerspexProperty Register(
string name,
TValue defaultValue = default(TValue),
bool inherits = false)
where TOwner : PerspexObject
{
Contract.Requires(name != null);
PerspexProperty result = new PerspexProperty(
name,
typeof(TOwner),
defaultValue,
inherits);
PerspexObject.Register(typeof(TOwner), result);
return result;
}
///
/// Registers an attached .
///
/// The type of the class that is registering the property.
/// The type of the class that the property is to be registered on.
/// The type of the property's value.
/// The name of the property.
/// The default value of the property.
/// Whether the property inherits its value.
///
public static PerspexProperty RegisterAttached(
string name,
TValue defaultValue = default(TValue),
bool inherits = false)
where TOwner : PerspexObject
{
Contract.Requires(name != null);
PerspexProperty result = new PerspexProperty(
name,
typeof(TOwner),
defaultValue,
inherits);
PerspexObject.Register(typeof(THost), result);
return result;
}
///
/// Provides access to a property's binding via the
/// indexer.
///
/// The property.
/// A describing the binding.
public static BindingAccessor operator!(PerspexProperty property)
{
return new BindingAccessor(property, BindingPriority.LocalValue);
}
///
/// Provides access to a property's template binding via the
/// indexer.
///
/// The property.
/// A describing the binding.
public static BindingAccessor operator ~(PerspexProperty property)
{
return new BindingAccessor(property, BindingPriority.TemplatedParent);
}
///
/// 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 (this.defaultValues.TryGetValue(type, out result))
{
return result;
}
type = type.GetTypeInfo().BaseType;
}
return this.defaultValues[this.OwnerType];
}
public bool IsValidValue(object value)
{
if (value == UnsetValue)
{
return true;
}
else if (value == null)
{
return !this.PropertyType.GetTypeInfo().IsValueType ||
Nullable.GetUnderlyingType(this.PropertyType) != null;
}
return this.PropertyType.GetTypeInfo().IsAssignableFrom(value.GetType().GetTypeInfo());
}
///
/// Gets 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);
// TODO: Ensure correct type.
if (this.defaultValues.ContainsKey(type))
{
throw new InvalidOperationException("Default value is already set for this property.");
}
this.defaultValues.Add(type, defaultValue);
}
public override string ToString()
{
return this.Name;
}
internal void NotifyChanged(PerspexPropertyChangedEventArgs e)
{
this.changed.OnNext(e);
}
public class BindingAccessor
{
public BindingAccessor(PerspexProperty property, BindingPriority priority)
{
this.Property = property;
this.Priority = priority;
}
public PerspexProperty Property
{
get;
private set;
}
public BindingPriority Priority
{
get;
private set;
}
}
}
///
/// A typed perspex property.
///
public class PerspexProperty : PerspexProperty
{
///
/// 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.
public PerspexProperty(
string name,
Type ownerType,
TValue defaultValue,
bool inherits)
: base(name, typeof(TValue), ownerType, defaultValue, inherits)
{
Contract.Requires(name != null);
Contract.Requires(ownerType != null);
}
///
/// Registers the property on another type.
///
/// The type of the additional owner.
/// The property.
public PerspexProperty AddOwner()
{
PerspexObject.Register(typeof(TOwner), this);
return this;
}
///
/// Gets the default value for the property on the specified type.
///
/// The type.
/// The default value.
public TValue GetDefaultValue()
{
return (TValue)this.GetDefaultValue(typeof(T));
}
}
}