I controlli Windows Forms sono organizzati secondo una ben precisa architettura all'interno della quale la classe Control assume un ruolo determinante, non a caso in questa classe che sono racchiuse le proprietà tipiche di ogni controllo, proprietà che di conseguenza troviamo in tutte le classi che ereditano da essa, anche se, a volte, totalmente prive di significato.
Ad esempio, la classe ListView espone anch'essa una proprietà Text che, in questo caso, non serve assolutamente a nulla e, per evitare che venga usata, nel framework è decorata con [EditorBrowsable.Never] in modo che non appaia nell'intellisense.
Applicare lo stesso criterio in WPF sarebbe stato impraticabile per una serie di motivi:

  • I controlli sono molto più complessi rispetto a quelli WinForms, di conseguenza avremmo avuto parecchio spazio allocato per i fields interni sebbene inutilizzati e inizializzati ad un valore di default. 
  • Parecchie proprietà hanno un senso solo se il controllo è in un ben determinato contesto, ad esempio un ipotetica proprietà Location avrebbe senso solo se il controllo è racchiuso in un container che supporta il posizionamento assoluto. (es: Canvas)
  • Il codice applicativo non è l'unico che interagisce con le proprietà dei controlli, la stessa architettura WPF via DataBinding, Animazioni, Stili e Property Inheritance accede e modifica/inizializza le  proprieta dei vari controlli/FrameworkElements.

Per supportare questi requesiti in WPF è stato introdotto il concetto di Depencency Property (e Attached Property).
In WPF la maggior parte degli elementi che interagiscono con la UI ereditano indirettamente dalla classe DependencyObject la quale può essere vista come un contenitore di proprietà, in particolar modo di oggetti DependencyProperty.
DependecyObject 
ha, tra gli altri, due metodi: SetValue() e GetValue() attraverso i quali possiamo scrivere e modificare i valori di una  proprietà associata ad un specifica istanza.

Volendo quindi creare una proprietà e far sì che questa possa essere supporti features quali Styling, Databinding e Animations, la proprietà non può essere dichiarata con la classica copia di get{} e set{} ma deve essere una proprietà registrata nel Dependency Property System di Windows Presentation Foundation.
Immaginiamo di creare una classe Foo e di aggiungere a questa una dependency property FooCounter,quello che dobbiamo scrivere è:

public class Foo:DependencyObject {

 public
static readonly DependencyProperty FooCounterProperty =
  DependencyProperty.Register("FooCounter", typeof(int), typeof(Foo), new UIPropertyMetadata(42));

}

In pratica andiamo ad aggiungere un field statico di tipo DependencyProperty che viene registrato nel dependency property system e inizializzato al valore 42 alla crezione della prima istanza di Foo.
A questo punto, volendo possiamo, grazie al fatto che Foo eredita da DependencyObject, interagire con la proprietà scrivendo:

Foo
f = new Foo();
f.SetValue(Foo.FooCounterProperty, 56);
int ret = (int) f.GetValue(Foo
.FooCounterProperty); //ret=56

Parecchio codice e sopratutto un utilizzo macchinoso, ecco perchè solitamente le dependency property sono wrappate dalla proprietà "classica" la quale internamente usa SetValue() e GetValue().

Ecco l'implementazione completa di Foo.

public class Foo:DependencyObject {
public static readonly DependencyProperty FooCounterProperty =
DependencyProperty.Register("FooCounter", typeof(int), typeof(Foo), new UIPropertyMetadata(42));

public int FooCounter
{
 
get { return (int)GetValue(FooCounterProperty); }
  
set { SetValue(FooCounterProperty, value); }
}
}

Il codice da scrivere rimane parecchio, fortunatamente le estensioni di WinFX in VS2005 aggiungono uno snippet (propdp in C#) che genera tutto automaticamente rispettando anche la convenzione per la quale il field publico deve chiamarsi [NomeProprietà]Property.
Oltre al meccanismo di "registrazione" è interessante notare che la proprietà viene effettivamente associata all'istanza attraverso SetValue() solo quando il suo valore cambia rispetto al default minimizzando quindi il memory footprint di ogni singola istanza di Foo.

Chiaramente, avendo WPF conoscenza dell'esistenza della proprietà FooCounter può agire su di essa usando GetValue() e SetValue() via DependencyObject, questo però sta a significare che se Foo ha necessità di essere informato quando il valore di FooCounter viene modificato non può agire all'interno di set{} ma deve necessariamente usare una tecnica diversa che consiste nel passare una PropertyChangedCallback nel parametro UIMetadata associato alla dependency property.

Il codice per la gestione completa della dependency property diviene perciò il seguente:

public
class Foo:DependencyObject {

static PropertyChangedCallback cbk = new PropertyChangedCallback(FooCounterChanged);
static UIPropertyMetadata data=new UIPropertyMetadata(42,cbk);
public static readonly DependencyProperty FooCounterProperty =
  DependencyProperty.Register("FooCounter", typeof(int), typeof(Foo), data);
 public int FooCounter {...}

 static void FooCounterChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
 {
   Console.WriteLine("NewValue: {0} OldValue: {1}"
,e.NewValue,e.OldValue);
 }
}

In questo caso, quando la proprietà FooCounter viene modificata dal nostro codice o dall'engine di WPF, la FooCounterChanged viene invocata.

Le Attached Properties le rimando ad un prossimo post...