📜 ⬆️ ⬇️

Simplify registration and work with DependencyProperty

When working with WPF / Silverlight, periodically you have to create custom DependencyProperty, mainly when creating controls. The standard approach of advertising and working with them is not perfect and has disadvantages, which will be discussed below. Accordingly, the idea was to simplify the recording of registration and work with the DependencyProperty.

To get started, here’s the standard code for the DependencyProperty declaration:
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. } } 

Disadvantages of this approach:

Improved version code:
 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) { } } 

In this embodiment, the Register method accepts a property as an expression, a default value of a specific type, and not a static callback. The callback method accepts a generic instance of the DependencyPropertyChangedEventArgs class, where the old and new property values ​​are cast to the property type. The recording itself is also simplified. Below is the code for the classes that allow you to apply this entry.

Code generic generic 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); 

This class takes as a generic parameter the type of DependencyObject and contains several overloaded Register methods. The Register method gets its name from the property expression, converts the callback and creates a DependencyProperty by the standard method.

DependecyPropertyChangedEventArgs class code:
 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; } } 

The code of the additional class ExpressionExtensions, which is used to get the property name by expression:
 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"); } } 

')

Source: https://habr.com/ru/post/149835/


All Articles