При работе с WPF/Silverlight периодически приходится создавать собственный DependencyProperty, в основном при создании элементов управления.
Стандартный подход к рекламе и работе с ней не идеален и имеет недостатки, о которых речь пойдет ниже.
Соответственно, возникла идея упростить запись регистрации и работу с DependencyProperty. Для начала приведем стандартный код объявления DependencyProperty:
Недостатки такого подхода:public class SomeDependecyObject : DependencyObject { public static readonly DependencyProperty IntValueProperty = DependencyProperty.Register("IntValue", typeof(int), typeof(SomeDependecyObject), new UIPropertyMetadata(1, OnIntValuePropertyChanged)); public int IntValue { get { return (int)GetValue(IntValueProperty); } set { SetValue(IntValueProperty, value); } } private static void OnIntValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { int newPropertyValue = (int)e.NewValue; SomeDependecyObject instance = (SomeDependecyObject)d; // Perform callback action. } }
- Указывает имя свойства в виде строки.
При переименовании свойства необходимо не забыть переименовать строковое значение.
- Статический обратный вызов.
Чтобы получить доступ к членам класса, параметр d должен быть приведен к типу класса.
Новые и старые значения свойств также не приводятся к типу свойства.
- Проверка типа свойства и значения по умолчанию на уровне компиляции не выполняется.
Параметр propertyType метода Register может принимать любой тип.
Параметр defaultValue является объектом.
public class SomeDependecyObject : DependencyObject
{
public static readonly DependencyProperty IntValueProperty =
DependencyProperty<SomeDependecyObject>.
Register(x => x.IntValue, 1, x => x.OnIntValuePropertyChanged);
public int IntValue
{
get { return (int)GetValue(IntValueProperty); }
set { SetValue(IntValueProperty, value); }
}
private void OnIntValuePropertyChanged(DependencyPropertyChangedEventArgs<int> e)
{
}
}
В этом варианте метод Register принимает свойство как выражение, значение по умолчанию определенного типа, а не статический обратный вызов.
Метод обратного вызова принимает экземпляр универсального класса DependencyPropertyChangedEventArgs, где старые и новые значения свойств приводятся к типу свойства.
Сама запись также была упрощена.
Ниже приведен код классов, которые позволяют использовать такую запись.
Код для пользовательского универсального класса DependecyProperty: public static class DependencyProperty<T> where T : DependencyObject
{
public static DependencyProperty Register<TProperty>(Expression<Func<T, TProperty>> propertyExpression)
{
return Register<TProperty>(propertyExpression, default(TProperty), null);
}
public static DependencyProperty Register<TProperty>(Expression<Func<T, TProperty>> propertyExpression, TProperty defaultValue)
{
return Register<TProperty>(propertyExpression, defaultValue, null);
}
public static DependencyProperty Register<TProperty>(Expression<Func<T, TProperty>> propertyExpression, Func<T, PropertyChangedCallback<TProperty>> propertyChangedCallbackFunc)
{
return Register<TProperty>(propertyExpression, default(TProperty), propertyChangedCallbackFunc);
}
public static DependencyProperty Register<TProperty>(Expression<Func<T, TProperty>> propertyExpression, TProperty defaultValue, Func<T, PropertyChangedCallback<TProperty>> propertyChangedCallbackFunc)
{
string propertyName = propertyExpression.RetrieveMemberName();
PropertyChangedCallback callback = ConvertCallback(propertyChangedCallbackFunc);
return DependencyProperty.Register(propertyName, typeof(TProperty), typeof(T), new PropertyMetadata(defaultValue, callback));
}
private static PropertyChangedCallback ConvertCallback<TProperty>(Func<T, PropertyChangedCallback<TProperty>> propertyChangedCallbackFunc)
{
if (propertyChangedCallbackFunc == null)
return null;
return new PropertyChangedCallback((d, e) =>
{
PropertyChangedCallback<TProperty> callback = propertyChangedCallbackFunc((T)d);
if (callback != null)
callback(new DependencyPropertyChangedEventArgs<TProperty>(e));
});
}
}
public delegate void PropertyChangedCallback<TProperty>(DependencyPropertyChangedEventArgs<TProperty> e);
Этот класс принимает тип DependencyObject в качестве универсального параметра и содержит несколько перегруженных методов Register. Метод Register извлекает свое имя из выражения свойства, преобразует обратный вызов и создает DependencyProperty с помощью стандартного метода.
Код класса DependecyPropertyChangedEventArgs: public class DependencyPropertyChangedEventArgs<T> : EventArgs
{
public DependencyPropertyChangedEventArgs(DependencyPropertyChangedEventArgs e)
{
NewValue = (T)e.NewValue;
OldValue = (T)e.OldValue;
Property = e.Property;
}
public T NewValue { get; private set; }
public T OldValue { get; private set; }
public DependencyProperty Property { get; private set; }
}
Код дополнительного класса ExpressionExtensions, который используется для получения имени свойства по выражению: public static class ExpressionExtensions
{
public static string RetrieveMemberName<TArg, TRes>(this Expression<Func<TArg, TRes>> propertyExpression)
{
MemberExpression memberExpression = propertyExpression.Body as MemberExpression;
if (memberExpression == null)
{
UnaryExpression unaryExpression = propertyExpression.Body as UnaryExpression;
if (unaryExpression != null)
memberExpression = unaryExpression.Operand as MemberExpression;
}
if (memberExpression != null)
{
ParameterExpression parameterExpression = memberExpression.Expression as ParameterExpression;
if (parameterExpression != null && parameterExpression.Name == propertyExpression.Parameters[0].
Name)
return memberExpression.Member.Name;
}
throw new ArgumentException("Invalid expression.", "propertyExpression");
}
}
Теги: #.
NET #C++ #wpf #silverlight #silverlight #.
NET #C++
-
Как Настроить Dynamics Gp Great Plains
19 Oct, 24 -
Я Люблю Красивые Диаграммы
19 Oct, 24