寻找 DependencyProperty.Register 的快捷方式

4

定义WPF属性太长了:

public static readonly DependencyProperty FooProperty = 
  DependencyProperty.Register("Foo", typeof(string), typeof(FooClass), new PropertyMetadata("Foooooo"));

我有一个帮助方法,现在想要让它更短:

public static readonly DependencyProperty FooProperty =
  WpfUtils.Property<string, FooControl>("Foo", "Foooooo");

代码:

public partial class WpfUtils 
{
    public static DependencyProperty Property<T, TClass>(string name) 
    {
        return Property<T, TClass>(name, default(T));
    }

    public static DependencyProperty Property<T, TClass>(string name, T defaultValue) 
    {
        return DependencyProperty.Register(name, typeof(T), typeof(TClass), new PropertyMetadata(defaultValue));
    }
}

有比你更好的助手吗?
4个回答

6
这里是相关代码。这段代码有些邪恶,但我想展示如何在不使用Cecil或创建sourceforge项目的情况下实现这个功能 :-)
要使用它,请调用:
public static readonly DependencyProperty FooProperty = D.P();

代码如下:

public class D
{
    [MethodImpl(MethodImplOptions.NoInlining)]
    public static DependencyProperty P()
    {
        StackTrace stackTrace = new StackTrace();
        StackFrame oneUp = stackTrace.GetFrame(1);
        MethodBase callingMethod = oneUp.GetMethod();
        if (!(callingMethod is ConstructorInfo))
        {
            throw new InvalidOperationException("This method must be called from a static constructor/initializer");
        }
        byte[] staticConstructorCode = callingMethod.GetMethodBody().GetILAsByteArray();
        int offsetAfterThisCall = oneUp.GetILOffset() + 5;

        while (staticConstructorCode[offsetAfterThisCall] == OpCodes.Nop.Value)
        {
            offsetAfterThisCall++;
        }

        if (staticConstructorCode[offsetAfterThisCall] != OpCodes.Stsfld.Value)
        {
            throw new InvalidOperationException("Unexpected IL");
        }

        int token = BitConverter.ToInt32(staticConstructorCode, offsetAfterThisCall + 1);

        FieldInfo field = callingMethod.Module.ResolveField(token);

        if (!field.Name.EndsWith("Property") || field.FieldType != typeof(DependencyProperty))
        {
            throw new NotSupportedException("The field the return value of this method will be stored in must be named xxxProperty and be of type DependencyProperty");
        }

        string name = field.Name.Substring(0, field.Name.Length - "Property".Length);
        return DependencyProperty.Register(name, callingMethod.DeclaringType.GetProperty(name).PropertyType, callingMethod.DeclaringType);
    }
}

你的示例中漏掉了Foo属性包装器声明,需要确定类型。但这很棒! :-) - alex2k8
值得一提的是,我应该指出这段代码之所以能工作,是因为静态构造函数无法内联。如果没有对CLR有很好的理解,不要在生产代码中尝试使用它进行其他操作。 - Alun Harford

5

这是我的尝试。它比Alun Harford的IL读取方法更安全。

这个助手具有以下功能:

  • 支持依赖属性和附加属性
  • 以类型安全的方式提供属性名称,无需(神奇)字符串(使用表达式树)
  • 具有通用参数的类型安全回调支持

首先,我将展示用法:

public class Tester : DependencyObject
{
    public int Foo
    {
        get { return (int)GetValue(FooProperty); }
        set { SetValue(FooProperty, value); }
    }
    public static readonly DependencyProperty FooProperty = 
        For<Tester>.Register(o => o.Foo, 0, onFooChanged);

    private static void onFooChanged(Tester obj, DependencyPropertyChangedEventArgs<int> e)
    {
    }

    public string Attached
    {
        get { return (string)GetValue(AttachedProperty); }
        set { SetValue(AttachedProperty, value); }
    }
    public static readonly DependencyProperty AttachedProperty = 
        For<Tester>.RegisterAttached(o => o.Attached, "default", onAttachedChanged);

    private static void onAttachedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs<string> e)
    {
    }
}

以下是实现代码:

public static class For<TOwner>
    where TOwner : DependencyObject
{
    public static DependencyProperty Register<TProperty>(
        Expression<Func<TOwner,TProperty>> property, 
        TProperty defaultValue, 
        Action<TOwner, DependencyPropertyChangedEventArgs<TProperty>> callback)
    {
        return DependencyProperty.Register(
            getName(property),
            typeof(TProperty),
            typeof(TOwner),
            new FrameworkPropertyMetadata(
                defaultValue,
                (o, args) => callback((TOwner)o, new DependencyPropertyChangedEventArgs<TProperty>(args))));
    }

    public static DependencyProperty RegisterAttached<TProperty>(
        Expression<Func<TOwner,TProperty>> property, 
        TProperty defaultValue,
        Action<DependencyObject, DependencyPropertyChangedEventArgs<TProperty>> callback)
    {
        return DependencyProperty.RegisterAttached(
            getName(property),
            typeof(TProperty),
            typeof(TOwner),
            new FrameworkPropertyMetadata(
                defaultValue,
                (o, args) => callback(o, new DependencyPropertyChangedEventArgs<TProperty>(args))));
    }

    private static string getName<T>(Expression<Func<TOwner,T>> property)
    {
        var name = ((MemberExpression)property.Body).Member.Name;
        return name;
    }
}

public struct DependencyPropertyChangedEventArgs<T>
{
    public DependencyPropertyChangedEventArgs(DependencyPropertyChangedEventArgs source)
    {
        m_property = source.Property;
        m_oldValue = (T)source.OldValue;
        m_newValue = (T)source.NewValue;
    }

    private readonly DependencyProperty m_property;
    public DependencyProperty Property { get { return m_property; } }

    private readonly T m_oldValue;
    public T OldValue { get { return m_oldValue; } }

    private readonly T m_newValue;
    public T NewValue { get { return m_newValue; } }
}

4

我同意依赖属性名称太长确实很麻烦。我不使用帮助程序,但是Visual Studio内置了一个很好的代码片段,您可以通过键入wpfdp来使用。

这就是我快速填写一堆依赖属性的方法。


0

对于使用resharper的人,我使用以下模板

//DependencyProperty $PropertyName$
public static readonly DependencyProperty $PropertyName$Property =
      DependencyProperty.Register("$PropertyName$", typeof($PropertyType$), typeof($SelfType$),
      new FrameworkPropertyMetadata($DefaultValue$, $PropertyName$ChangedCallback, $PropertyName$CoerceValue));

public $PropertyType$ $PropertyName${
       set { SetValue($PropertyName$Property, value); }
       get { return ($PropertyType$)GetValue($PropertyName$Property); }
      }

private static void $PropertyName$ChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e){

      $SelfType$ owner = d as $SelfType$;
      if(owner!=null){}

      }

 private static object $PropertyName$CoerceValue(DependencyObject d, object value)   {

            $PropertyType$ val = ($PropertyType$)value;
        return value;
      }

我只在可能声明成员的地方显示它。 我还将 $SelfType$ 设置为扩展到父类型,这里是类名。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接