Упростите Регистрацию И Работу С Dependencyproperty

При работе с 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++

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.