views:

1003

answers:

4

Defining WPF properties is too long:

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

I have a helper method, making it a bit shorter:

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

Code:

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));
    }
}

Are there better helpers around?

+4  A: 

I agree that it is a pain that Dependency Properties are so long. I don't use a helper, but Visual Studio has a great built-in snippet that you can use by typing wpfdp.

That's how I fill in a bunch of Dependency Properties quickly.

timothymcgrath
+4  A: 

Here is some code for that. This code is evil, but I wanted to show how to do this without using Cecil, or having to create a sourceforge project :-)

To use it, call:

public static readonly DependencyProperty FooProperty = D.P();

And the code is:

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);
    }
}
Alun Harford
One thing missed in your example is the Foo property wrapper declaration, required to figure out the type. But this is great! :-)
alex2k8
As a side note, I should point out that this code only works because static constructors can't be inlined. Don't try it for other stuff in production code without a good understanding of the CLR.
Alun Harford
A: 

For those using resharper I use the following template

//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;
      }

I have it show only where it's possible to declare member I've also set $SelfType$ to expand to the parent type which here is the class name

JC
+2  A: 

Here is my attempt. It is safer than the IL-reading method by Alun Harford.

This helper has the following features:

  • Support for dependency properties and attached properties
  • Providing property name in a type-safe manner, w/o (magic) strings (using expression trees)
  • Type-safe callback support, with generic arguments

First, I'll show the usage:

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)
    {
    }
}

And here is the implementation:

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; } }
}
Omer Mor