niedziela, 27 listopada 2011

MVVM cz. 5 - DependencyProperty

DependencyProperty jest to właściwość z dodatkowymi możliwościami. Definiujemy ją w klasie dziedziczącej po DependencyObject, ponieważ potrzebujemy dostępu do metod tej klasy GetValue i SetValue. Definicja DependencyProperty wygląda mniej więcej tak:
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(bool),
typeof(MyType), new PropertyMetadata(OnMyPropertyChanged));

private static void OnMyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is MyType)
{
// do something
}
}

public bool MyProperty
{
get { return (bool)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}

Pierwszy argument w statycznej metodzie Register to nazwa naszej właściwości i musi być taka sama jak prawdziwa nazwa MyProperty. Następnie rejestrujemy typ właściwości oraz typ naszej klasy. Dwa ostatnie parametry są opcjonalne.
Jeżeli chodzi o czwarty argument metody Register, to posiada on najwięcej możliwości. Może byc to klasa typu PropertyMetadata, UIPropertyMetadata lub FrameworkPropertyMetadata.
Jest to klasa zawierająca dodatkowe funkcjonalności dla DependencyProperty. W przypadku piątego argumentu jest to metoda która będzie walidować dane. Przyjmuje ona jeden paramter typu object w którym przekazuje nową wartość DependencyProperty.

private static bool Validate(object value)
{
// Validate...
return false;
}

Metoda zwraca bool i w przypadku wartości false otrzymujemy wyjątek System.ArgumentException.

W przypadku, gdy działamy mocno w warstwie UI i tworzymy dużo DependencyProperty możemy skorzystać ze snippeta propdp.

Warto pamiętać, że w przypadku wszystkich DependencyProperty dozwolona jest wartość DependencyProperty.UnsetValue, która wskazuje, że dana właściwośc nie ma przypisanej wartości w danym momencie.

Istotną rzeczą jest również różnica pomiędzy metodami SetValue oraz SetCurrentValue. Jeżeli używamy metody SetValue, a wcześniej ustawiliśmy binding do danej DependencyProperty (lub trigger), wówczas dany binding zostanie zniszczony. Sytuacji tej możemy uniknąć stosując metodę SetCurrentValue, która ustala wartość bez jakichkolwiek konsewkencji dla naszych bindingów i triggerów.

PropertyMetadata


Jak widać na rysunku powyżej UIPropertyMetadata dziedziczy po PropertyMetadata, a FrameworkPropertyMetadata dziedziczy po UIPropertyMetadata. Za pomocą PropertyMetadata możemy zdefiniować wartość domyślną DependencyProperty oraz dwa callbacki: PropertyChangedCallback i CoerceValueCallback.
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(MyType),
typeof(MyClass), new PropertyMetadata(null, OnMyPropertyChanged, OnCoerceValue));

private static object OnCoerceValue(DependencyObject d, object basevalue)
{
return null;
}

private static void OnMyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is MyClass) { }
}

public MyType MyProperty
{
get { return (MyType)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}

PropertyChangedCallback uruchamia się tylko i wyłącznie po zmianie wartości DependencyProperty, natomiast CoerceValueCallback uruchamiany jest przy każdej próbie przypisania nowej wartości (przed zmianą wartości DependencyProperty). Jako parametr metoda typu CoerceValueCallback przyjmuje nową wartość. Jeżeli jednak metoda ta zwróci wartość starą, to w ogóle nie dojdzie do odpalenia PropertyChangedCallback.
UIPropertyMetadata posiada dodatkowy feature - właściwość IsAnimationProhibited. Jeżeli jest ustawiona na true, wówczas nasza DependencyProperty nie może być użyta w animacjach.
Ostatnia klasa FrameworkPropertyMetadata oprócz powyższych posiada w konstruktorach jeszcze 2 parametry: defaultUpdateSourceTrigger (ustawia domyślną wartość dla UpdateSourceTrigger dla bindingu) oraz flagi typu FrameworkPropertyMetadataOptions. Jest to typ wyliczeniowy, którego naważniejsze wartości wymienione są poniżej:
  • AffectsMeasure -Zmiana wartości DependencyProperty powoduje wywołanie metody Measure.

  • AffectsArrange -Zmiana wartości DependencyProperty powoduje wywołanie metody Arrange.

  • AffectsParentMeasure - Zmiana wartości DependencyProperty powoduje wywołanie metody Measure dla obiektu Parent.

  • AffectsParentArrange - Zmiana wartości DependencyProperty powoduje wywołanie metody Arrange dla obiektu Parent.

  • AffectsRender - Zmiana wartości DependencyProperty powoduje odrysowanie kontrolki.

  • Inherits - Oznacza, że dana DependencyProperty jest dziedziczona - tzn. jeżeli wewnątrz kontrolki umieścimy drugą kontrolkę z tą samą, ale nie ustawioną DependencyProperty, to wówczas właściwość będzie odziedziczona z kontrolki wyższej. Bardzo dobrym przykładem jest właściwość DataContext, która jest dziedziczona w kontrolkach zagnieżdżonych.

  • NotDataBindable - Oznacza, że do danej DependencyProperty nie można się podpiąć (zbindować).

  • BindsTwoWayByDefault - Ustawia jako domyślny tryb bindingu TwoWay.

  • Journal - Oznacza, że dana DependencyProperty jest używana do nawigacji w aplikacji.


AttachedProperty

Jak sama nazwa wskazuje jest to właściwośc doczepiana. Doskonalym przykladem jest Grid.Row:

<Grid>
<TextBlock Text="Text w trzecim wierszu grida" Grid.Row="2"/>
<Label Grid.Row="3">Text w czwartym wierszy grida</Label>
</Grid>


Możemy odwołać się do property Grida z poziomu TextBlocka i przypisać wartość dla danego TextBlocka. Właściwośc ta ma osobną wartość dla każdego elementu. Definiuje się ją w nieco inny sposób niż tradycyjną właściwość. Na przykładzie grida wygląda to tak:

RowProperty = DependencyProperty.RegisterAttached("Row", typeof(int),
typeof(Grid), metadata, new ValidateValueCallback(Grid.IsIntValueNotNegative));

public static int GetRow(UIElement element)
{
// Some content
return (int)element.GetValue(Grid.RowProperty);
}
public static void SetRow(UIElement element, int value)
{
// Some content
element.SetValue(Grid.RowProperty, value);
}

Tak jak widać, nie stosujemy w tym przypadku zwykłego propertasa tylko dwie metody GetRow i SetRow, których nazwy muszą być utrzymane w takiej konwencji
GetProperty SetProperty i posiadać takie same sygnatury jak powyższe metody.

1 komentarze:

Tomek pisze...

Jeżeli chodzi o DependencyProperty tonie mam uwag:D Jeżeli chodzi natomiast o Attached Dependecy Property myślę, że dobrze byłoby napisać jakiś kawałek kodu pokazujący ich możliwości. Myślę, że dobrym przykładem byłoby napisanie własnej attached dependecy property, która symulowałaby właściwość UpdateSourceTrigger=PropertyChanged. Taka właściwość jest w WPF-ie ale nie ma jej w Silverlighcie. Wykorzystując jednak Attached Dependecy Property można sprawić, że zbindowane propertisy będą odświeżały się zaraz po zmianie wartości w kontrolce - a nie jak jest do tej pory (w Silverlighcie) po stracie focusa

Prześlij komentarz

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Online Project management