2009-02-28 5 views
4

Definieren von WPF-Eigenschaften zu lang ist:für DependencyProperty.Register Suchen Verknüpfung

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

Ich habe eine Hilfsmethode, es ist ein bisschen kürzer zu machen:

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

Gibt es bessere Helfer?

Antwort

6

Hier ist ein Code dafür. Dieser Code ist böse, aber ich wollte zeigen, wie dies zu tun, ohne Cecil, oder ein Projekt Source erstellen zu müssen :-)

, es zu benutzen, rufen:

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

Und der Code ist:

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

Eine Sache in Ihrem Beispiel verpassten ist die Deklaration Foo Eigenschaft Wrapper, die Art, um herauszufinden, erforderlich. Aber das ist großartig! :-) – alex2k8

+0

Als Randnotiz sollte ich darauf hinweisen, dass dieser Code nur funktioniert, weil statische Konstruktoren nicht inline sein können. Versuchen Sie es nicht für andere Sachen im Produktionscode ohne ein gutes Verständnis der CLR. –

4

Ich stimme zu, dass es ein Schmerz ist, dass Dependency Properties so lang sind. Ich verwende keinen Helfer, aber Visual Studio hat ein großes eingebautes Snippet, das Sie verwenden können, indem Sie wpfdp eingeben.

So fülle ich eine Reihe von Abhängigkeitseigenschaften schnell aus.

0

Für diejenigen mit ReSharper verwende ich die folgende Vorlage

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

ich habe es nur zeigen wher e es ist möglich, Mitglied zu deklarieren Ich habe auch $ SelfType $ auf den übergeordneten Typ zu erweitern, der hier ist der Klassenname

5

Hier ist mein Versuch. Es ist sicherer als die IL-Lese-Methode von Alun Harford.

Dieser Helfer hat folgende Eigenschaften:

  • Unterstützung für Abhängigkeitseigenschaften und angefügten Eigenschaften
  • Bereitstellung von Eigenschaftsnamen in einer typsichere Weise, w/o (Magie) Strings (expression Bäume mit)
  • Typ-safe Rückruf-Unterstützung, mit generischen Argumenten

Zuerst werde ich die Verwendung zeigen:

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

Und hier ist die Umsetzung:

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; } } 
} 
Verwandte Themen