зеркало из https://github.com/DeGsoft/maui-linux.git
349 строки
21 KiB
C#
349 строки
21 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Linq.Expressions;
|
|
using System.Reflection;
|
|
using Xamarin.Forms.Internals;
|
|
using Xamarin.Forms.Xaml;
|
|
|
|
namespace Xamarin.Forms
|
|
{
|
|
[DebuggerDisplay("{PropertyName}")]
|
|
[TypeConverter(typeof(BindablePropertyConverter))]
|
|
public sealed class BindableProperty
|
|
{
|
|
public delegate void BindingPropertyChangedDelegate(BindableObject bindable, object oldValue, object newValue);
|
|
|
|
public delegate void BindingPropertyChangedDelegate<in TPropertyType>(BindableObject bindable, TPropertyType oldValue, TPropertyType newValue);
|
|
|
|
public delegate void BindingPropertyChangingDelegate(BindableObject bindable, object oldValue, object newValue);
|
|
|
|
public delegate void BindingPropertyChangingDelegate<in TPropertyType>(BindableObject bindable, TPropertyType oldValue, TPropertyType newValue);
|
|
|
|
public delegate object CoerceValueDelegate(BindableObject bindable, object value);
|
|
|
|
public delegate TPropertyType CoerceValueDelegate<TPropertyType>(BindableObject bindable, TPropertyType value);
|
|
|
|
public delegate object CreateDefaultValueDelegate(BindableObject bindable);
|
|
|
|
public delegate TPropertyType CreateDefaultValueDelegate<in TDeclarer, out TPropertyType>(TDeclarer bindable);
|
|
|
|
public delegate bool ValidateValueDelegate(BindableObject bindable, object value);
|
|
|
|
public delegate bool ValidateValueDelegate<in TPropertyType>(BindableObject bindable, TPropertyType value);
|
|
|
|
static readonly Dictionary<Type, TypeConverter> WellKnownConvertTypes = new Dictionary<Type,TypeConverter>
|
|
{
|
|
{ typeof(Uri), new UriTypeConverter() },
|
|
{ typeof(Color), new ColorTypeConverter() },
|
|
};
|
|
|
|
// more or less the encoding of this, without the need to reflect
|
|
// http://msdn.microsoft.com/en-us/library/y5b434w4.aspx
|
|
static readonly Dictionary<Type, Type[]> SimpleConvertTypes = new Dictionary<Type, Type[]>
|
|
{
|
|
{ typeof(sbyte), new[] { typeof(string), typeof(short), typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) } },
|
|
{ typeof(byte), new[] { typeof(string), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
|
|
{ typeof(short), new[] { typeof(string), typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) } },
|
|
{ typeof(ushort), new[] { typeof(string), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
|
|
{ typeof(int), new[] { typeof(string), typeof(long), typeof(float), typeof(double), typeof(decimal) } },
|
|
{ typeof(uint), new[] { typeof(string), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
|
|
{ typeof(long), new[] { typeof(string), typeof(float), typeof(double), typeof(decimal) } },
|
|
{ typeof(char), new[] { typeof(string), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
|
|
{ typeof(float), new[] { typeof(string), typeof(double) } },
|
|
{ typeof(ulong), new[] { typeof(string), typeof(float), typeof(double), typeof(decimal) } }
|
|
};
|
|
|
|
BindableProperty(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
|
|
ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
|
|
CoerceValueDelegate coerceValue = null, BindablePropertyBindingChanging bindingChanging = null, bool isReadOnly = false, CreateDefaultValueDelegate defaultValueCreator = null)
|
|
{
|
|
if (propertyName == null)
|
|
throw new ArgumentNullException("propertyName");
|
|
if (ReferenceEquals(returnType, null))
|
|
throw new ArgumentNullException("returnType");
|
|
if (ReferenceEquals(declaringType, null))
|
|
throw new ArgumentNullException("declaringType");
|
|
|
|
// don't use Enum.IsDefined as its redonkulously expensive for what it does
|
|
if (defaultBindingMode != BindingMode.Default && defaultBindingMode != BindingMode.OneWay && defaultBindingMode != BindingMode.OneWayToSource && defaultBindingMode != BindingMode.TwoWay && defaultBindingMode != BindingMode.OneTime)
|
|
throw new ArgumentException($"Not a valid type of BindingMode. Property: {returnType} {declaringType.Name}.{propertyName}. Default binding mode: {defaultBindingMode}", "defaultBindingMode");
|
|
|
|
if (defaultValue == null && Nullable.GetUnderlyingType(returnType) == null && returnType.GetTypeInfo().IsValueType)
|
|
defaultValue = Activator.CreateInstance(returnType);
|
|
|
|
if (defaultValue != null && !returnType.IsInstanceOfType(defaultValue))
|
|
throw new ArgumentException($"Default value did not match return type. Property: {returnType} {declaringType.Name}.{propertyName} Default value type: {defaultValue.GetType().Name}, ", "defaultValue");
|
|
|
|
if (defaultBindingMode == BindingMode.Default)
|
|
defaultBindingMode = BindingMode.OneWay;
|
|
|
|
PropertyName = propertyName;
|
|
ReturnType = returnType;
|
|
ReturnTypeInfo = returnType.GetTypeInfo();
|
|
DeclaringType = declaringType;
|
|
DefaultValue = defaultValue;
|
|
DefaultBindingMode = defaultBindingMode;
|
|
PropertyChanged = propertyChanged;
|
|
PropertyChanging = propertyChanging;
|
|
ValidateValue = validateValue;
|
|
CoerceValue = coerceValue;
|
|
BindingChanging = bindingChanging;
|
|
IsReadOnly = isReadOnly;
|
|
DefaultValueCreator = defaultValueCreator;
|
|
}
|
|
|
|
public Type DeclaringType { get; private set; }
|
|
|
|
public BindingMode DefaultBindingMode { get; private set; }
|
|
|
|
public object DefaultValue { get; }
|
|
|
|
public bool IsReadOnly { get; private set; }
|
|
|
|
public string PropertyName { get; }
|
|
|
|
public Type ReturnType { get; }
|
|
|
|
internal BindablePropertyBindingChanging BindingChanging { get; private set; }
|
|
|
|
internal CoerceValueDelegate CoerceValue { get; private set; }
|
|
|
|
internal CreateDefaultValueDelegate DefaultValueCreator { get; }
|
|
|
|
internal BindingPropertyChangedDelegate PropertyChanged { get; private set; }
|
|
|
|
internal BindingPropertyChangingDelegate PropertyChanging { get; private set; }
|
|
|
|
internal TypeInfo ReturnTypeInfo { get; }
|
|
|
|
internal ValidateValueDelegate ValidateValue { get; private set; }
|
|
|
|
[Obsolete("Create<> (generic) is obsolete as of version 2.1.0 and is no longer supported.")]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public static BindableProperty Create<TDeclarer, TPropertyType>(Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
|
|
ValidateValueDelegate<TPropertyType> validateValue = null, BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null,
|
|
BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, CoerceValueDelegate<TPropertyType> coerceValue = null,
|
|
CreateDefaultValueDelegate<TDeclarer, TPropertyType> defaultValueCreator = null) where TDeclarer : BindableObject
|
|
{
|
|
return Create(getter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, defaultValueCreator: defaultValueCreator);
|
|
}
|
|
|
|
public static BindableProperty Create(string propertyName, Type returnType, Type declaringType, object defaultValue = null, BindingMode defaultBindingMode = BindingMode.OneWay,
|
|
ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
|
|
CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
|
|
{
|
|
return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue,
|
|
defaultValueCreator: defaultValueCreator);
|
|
}
|
|
|
|
[Obsolete("CreateAttached<> (generic) is obsolete as of version 2.1.0 and is no longer supported.")]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public static BindableProperty CreateAttached<TDeclarer, TPropertyType>(Expression<Func<BindableObject, TPropertyType>> staticgetter, TPropertyType defaultValue,
|
|
BindingMode defaultBindingMode = BindingMode.OneWay, ValidateValueDelegate<TPropertyType> validateValue = null, BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null,
|
|
BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, CoerceValueDelegate<TPropertyType> coerceValue = null,
|
|
CreateDefaultValueDelegate<BindableObject, TPropertyType> defaultValueCreator = null)
|
|
{
|
|
return CreateAttached<TDeclarer, TPropertyType>(staticgetter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null,
|
|
defaultValueCreator: defaultValueCreator);
|
|
}
|
|
|
|
public static BindableProperty CreateAttached(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
|
|
ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
|
|
CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
|
|
{
|
|
return CreateAttached(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, false, defaultValueCreator);
|
|
}
|
|
|
|
[Obsolete("CreateAttachedReadOnly<> (generic) is obsolete as of version 2.1.0 and is no longer supported.")]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public static BindablePropertyKey CreateAttachedReadOnly<TDeclarer, TPropertyType>(Expression<Func<BindableObject, TPropertyType>> staticgetter, TPropertyType defaultValue,
|
|
BindingMode defaultBindingMode = BindingMode.OneWayToSource, ValidateValueDelegate<TPropertyType> validateValue = null,
|
|
BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null,
|
|
CoerceValueDelegate<TPropertyType> coerceValue = null, CreateDefaultValueDelegate<BindableObject, TPropertyType> defaultValueCreator = null)
|
|
|
|
{
|
|
return
|
|
new BindablePropertyKey(CreateAttached<TDeclarer, TPropertyType>(staticgetter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true,
|
|
defaultValueCreator));
|
|
}
|
|
|
|
public static BindablePropertyKey CreateAttachedReadOnly(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource,
|
|
ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
|
|
CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
|
|
{
|
|
return
|
|
new BindablePropertyKey(CreateAttached(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true,
|
|
defaultValueCreator));
|
|
}
|
|
|
|
[Obsolete("CreateReadOnly<> (generic) is obsolete as of version 2.1.0 and is no longer supported.")]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public static BindablePropertyKey CreateReadOnly<TDeclarer, TPropertyType>(Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue,
|
|
BindingMode defaultBindingMode = BindingMode.OneWayToSource, ValidateValueDelegate<TPropertyType> validateValue = null,
|
|
BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null,
|
|
CoerceValueDelegate<TPropertyType> coerceValue = null, CreateDefaultValueDelegate<TDeclarer, TPropertyType> defaultValueCreator = null) where TDeclarer : BindableObject
|
|
{
|
|
return new BindablePropertyKey(Create(getter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true, defaultValueCreator));
|
|
}
|
|
|
|
public static BindablePropertyKey CreateReadOnly(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource,
|
|
ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
|
|
CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
|
|
{
|
|
return
|
|
new BindablePropertyKey(new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue,
|
|
isReadOnly: true, defaultValueCreator: defaultValueCreator));
|
|
}
|
|
|
|
[Obsolete("Create<> (generic) is obsolete as of version 2.1.0 and is no longer supported.")]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
internal static BindableProperty Create<TDeclarer, TPropertyType>(Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue, BindingMode defaultBindingMode,
|
|
ValidateValueDelegate<TPropertyType> validateValue, BindingPropertyChangedDelegate<TPropertyType> propertyChanged, BindingPropertyChangingDelegate<TPropertyType> propertyChanging,
|
|
CoerceValueDelegate<TPropertyType> coerceValue, BindablePropertyBindingChanging bindingChanging, bool isReadOnly = false,
|
|
CreateDefaultValueDelegate<TDeclarer, TPropertyType> defaultValueCreator = null) where TDeclarer : BindableObject
|
|
{
|
|
if (getter == null)
|
|
throw new ArgumentNullException("getter");
|
|
|
|
Expression expr = getter.Body;
|
|
|
|
var unary = expr as UnaryExpression;
|
|
if (unary != null)
|
|
expr = unary.Operand;
|
|
|
|
var member = expr as MemberExpression;
|
|
if (member == null)
|
|
throw new ArgumentException("getter must be a MemberExpression", "getter");
|
|
|
|
var property = (PropertyInfo)member.Member;
|
|
|
|
ValidateValueDelegate untypedValidateValue = null;
|
|
BindingPropertyChangedDelegate untypedBindingPropertyChanged = null;
|
|
BindingPropertyChangingDelegate untypedBindingPropertyChanging = null;
|
|
CoerceValueDelegate untypedCoerceValue = null;
|
|
CreateDefaultValueDelegate untypedDefaultValueCreator = null;
|
|
if (validateValue != null)
|
|
untypedValidateValue = (bindable, value) => validateValue(bindable, (TPropertyType)value);
|
|
if (propertyChanged != null)
|
|
untypedBindingPropertyChanged = (bindable, oldValue, newValue) => propertyChanged(bindable, (TPropertyType)oldValue, (TPropertyType)newValue);
|
|
if (propertyChanging != null)
|
|
untypedBindingPropertyChanging = (bindable, oldValue, newValue) => propertyChanging(bindable, (TPropertyType)oldValue, (TPropertyType)newValue);
|
|
if (coerceValue != null)
|
|
untypedCoerceValue = (bindable, value) => coerceValue(bindable, (TPropertyType)value);
|
|
if (defaultValueCreator != null)
|
|
untypedDefaultValueCreator = o => defaultValueCreator((TDeclarer)o);
|
|
|
|
return new BindableProperty(property.Name, property.PropertyType, typeof(TDeclarer), defaultValue, defaultBindingMode, untypedValidateValue, untypedBindingPropertyChanged,
|
|
untypedBindingPropertyChanging, untypedCoerceValue, bindingChanging, isReadOnly, untypedDefaultValueCreator);
|
|
}
|
|
|
|
internal static BindableProperty Create(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
|
|
BindingPropertyChangedDelegate propertyChanged, BindingPropertyChangingDelegate propertyChanging, CoerceValueDelegate coerceValue, BindablePropertyBindingChanging bindingChanging,
|
|
CreateDefaultValueDelegate defaultValueCreator = null)
|
|
{
|
|
return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging,
|
|
defaultValueCreator: defaultValueCreator);
|
|
}
|
|
|
|
[Obsolete("CreateAttached<> (generic) is obsolete as of version 2.1.0 and is no longer supported.")]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
internal static BindableProperty CreateAttached<TDeclarer, TPropertyType>(Expression<Func<BindableObject, TPropertyType>> staticgetter, TPropertyType defaultValue, BindingMode defaultBindingMode,
|
|
ValidateValueDelegate<TPropertyType> validateValue, BindingPropertyChangedDelegate<TPropertyType> propertyChanged, BindingPropertyChangingDelegate<TPropertyType> propertyChanging,
|
|
CoerceValueDelegate<TPropertyType> coerceValue, BindablePropertyBindingChanging bindingChanging, bool isReadOnly = false,
|
|
CreateDefaultValueDelegate<BindableObject, TPropertyType> defaultValueCreator = null)
|
|
{
|
|
if (staticgetter == null)
|
|
throw new ArgumentNullException("staticgetter");
|
|
|
|
Expression expr = staticgetter.Body;
|
|
|
|
var unary = expr as UnaryExpression;
|
|
if (unary != null)
|
|
expr = unary.Operand;
|
|
|
|
var methodcall = expr as MethodCallExpression;
|
|
if (methodcall == null)
|
|
throw new ArgumentException("staticgetter must be a MethodCallExpression", "staticgetter");
|
|
|
|
MethodInfo method = methodcall.Method;
|
|
if (!method.Name.StartsWith("Get", StringComparison.Ordinal))
|
|
throw new ArgumentException("staticgetter name must start with Get", "staticgetter");
|
|
|
|
string propertyname = method.Name.Substring(3);
|
|
|
|
ValidateValueDelegate untypedValidateValue = null;
|
|
BindingPropertyChangedDelegate untypedBindingPropertyChanged = null;
|
|
BindingPropertyChangingDelegate untypedBindingPropertyChanging = null;
|
|
CoerceValueDelegate untypedCoerceValue = null;
|
|
CreateDefaultValueDelegate untypedDefaultValueCreator = null;
|
|
if (validateValue != null)
|
|
untypedValidateValue = (bindable, value) => validateValue(bindable, (TPropertyType)value);
|
|
if (propertyChanged != null)
|
|
untypedBindingPropertyChanged = (bindable, oldValue, newValue) => propertyChanged(bindable, (TPropertyType)oldValue, (TPropertyType)newValue);
|
|
if (propertyChanging != null)
|
|
untypedBindingPropertyChanging = (bindable, oldValue, newValue) => propertyChanging(bindable, (TPropertyType)oldValue, (TPropertyType)newValue);
|
|
if (coerceValue != null)
|
|
untypedCoerceValue = (bindable, value) => coerceValue(bindable, (TPropertyType)value);
|
|
if (defaultValueCreator != null)
|
|
untypedDefaultValueCreator = o => defaultValueCreator(o);
|
|
|
|
return new BindableProperty(propertyname, method.ReturnType, typeof(TDeclarer), defaultValue, defaultBindingMode, untypedValidateValue, untypedBindingPropertyChanged, untypedBindingPropertyChanging,
|
|
untypedCoerceValue, bindingChanging, isReadOnly, untypedDefaultValueCreator);
|
|
}
|
|
|
|
internal static BindableProperty CreateAttached(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
|
|
BindingPropertyChangedDelegate propertyChanged, BindingPropertyChangingDelegate propertyChanging, CoerceValueDelegate coerceValue, BindablePropertyBindingChanging bindingChanging,
|
|
bool isReadOnly, CreateDefaultValueDelegate defaultValueCreator = null)
|
|
{
|
|
return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging, isReadOnly,
|
|
defaultValueCreator);
|
|
}
|
|
|
|
internal object GetDefaultValue(BindableObject bindable)
|
|
{
|
|
if (DefaultValueCreator != null)
|
|
return DefaultValueCreator(bindable);
|
|
|
|
return DefaultValue;
|
|
}
|
|
|
|
internal bool TryConvert(ref object value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
return !ReturnTypeInfo.IsValueType || ReturnTypeInfo.IsGenericType && ReturnTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>);
|
|
}
|
|
|
|
Type valueType = value.GetType();
|
|
Type type = ReturnType;
|
|
|
|
// Dont support arbitrary IConvertible by limiting which types can use this
|
|
Type[] convertableTo;
|
|
TypeConverter typeConverterTo;
|
|
if (SimpleConvertTypes.TryGetValue(valueType, out convertableTo) && Array.IndexOf(convertableTo, type) != -1)
|
|
{
|
|
value = Convert.ChangeType(value, type);
|
|
}
|
|
else if (WellKnownConvertTypes.TryGetValue(type, out typeConverterTo) && typeConverterTo.CanConvertFrom(valueType))
|
|
{
|
|
value = typeConverterTo.ConvertFromInvariantString(value.ToString());
|
|
}
|
|
else if (!ReturnTypeInfo.IsAssignableFrom(valueType.GetTypeInfo()))
|
|
{
|
|
var cast = type.GetImplicitConversionOperator(fromType: valueType, toType: type)
|
|
?? valueType.GetImplicitConversionOperator(fromType: valueType, toType: type);
|
|
|
|
if (cast == null)
|
|
return false;
|
|
|
|
value = cast.Invoke(null, new[] { value });
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
internal delegate void BindablePropertyBindingChanging(BindableObject bindable, BindingBase oldValue, BindingBase newValue);
|
|
}
|
|
} |