niedziela, 27 listopada 2011

MVVM cz.4 - ElementName, RelativeSource, Source, Converters

Co w momencie, kiedy bindingi z ViewModelu nam nie wystarczą? Nie chcemy podpinać się do obiektu z DataContextu tylko do właściwości innej kontrolki, statycznego obiektu lub obiektu którego w ogóle nie widzimy w danym xamlu... Z takimi sytuacjami też możemy sobie poradzić. Binding posiada 3 właściwości, które nam w tym pomogą: ElementName, Source i RelativeSource. W danym bindingu można ustawić tylko jedną z tych trzech właściwości.

Poprzez właściwość Source możemy odwoływać się do obiektów statycznych i obiektów znajdujących się w resourcach. Np.:
<TextBlock Text="{Binding ., Source={x:Static System:DateTime.Now}}"/>

<TextBlock Text="{Binding XPath=Hulk, Source={StaticResource Hogan}}"/>


XPath jest alternatywą dla Path, dzięki czemu możemy bindować się do wezłów w xml’u. Wówczas w Resourcach powinniśmy mieć wpis podobny do poniższego:
<XmlDataProvider x:Key="Hogan">
<x:XData>
<Hulk>me</Hulk>
</x:XData>
</XmlDataProvider>


Właściwość ElementName wskazuje na obiekt o podanej nazwie.

<Border>
<TextBlock Text="{Binding Text, ElementName=NestedElement}"/>
</Border>

<Border>
<TextBlock Text="Big ben" Name="NestedElement"/>
</Border>


W efekcie takiego bindingu otrzymamy 2 elementy typu TextBlock z właściwością Text ustawioną na “Big ben”. W ramach danego widoku możemy odwoływać się do dowolnego elementu, który ma przypisaną nazwę. Ponadto z poziomu resourców, styli i control templatów też możemy podpinać się za pomocą nazwy elementu. Jedyne ograniczenie dla tego rodzaju bindingu jest wtedy, gdy z wnętrza widoku jednej klasy próbujemy się odwołać do elementu widoku drugiej klasy.

Ostatnią właściwością jest RelativeSource. Obiekt tego typu występuje w 4 trybach, które można ustawić za pomocą właściwości Mode:
  • Self - w tym trybie źródłem i celem bindingu jest ten sam obiekt
  • TemplatedParent - stosuje się przy tworzeniu kontrolek za pomocą ControlTemplate
  • FindAncestor - odszukiwanie obiektu źródłowego w drzewie wizualnym
  • PreviousData - ten tryb stosowany jest w różnego rodzaju listach, jeżeli potrzebujemy podpiąć się pod porzedni element.

Tryb FindAncestor jest powiązany z dwoma innymi właściwościami klasy RelativeSource: AncestorType i AncestorLevel. AncestorType wskazuje typ źródła natomiast AncestorLevel poziom na którym jest źródło. Jeżeli ustawimy te właściwości nie musimy wówczas bezpośrednio definiować trybu na FindAncestor, gdyż jest on wybierany domyślnie.

<TextBlock Text="{Binding Path=Name, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TextBlock}}" />


Klasa RelativeSource posiada 3 statyczne właściwości typu RelativeSource, które zwracają obiekty o różnych trybach RelativeSourceMode. Dzięki temu możemy zastosować zapis:
<Component Property="{Binding Property2, RelativeSource={x:Static RelativeSource.TemplatedParent}}">

<Component Property="{Binding Property2, RelativeSource={x:Static RelativeSource.PreviousData}}">

<Component Property="{Binding Property2, RelativeSource={x:Static RelativeSource.TemplatedParent}}">


Converters

Często okazuje się, że potrzebujemy przekonwertować bindowaną wartość na jakaś inną lub zmienić jej typ. Wówczas z pomocą przychodzą nam konwertery. Są to specjalne klasy, które dziedziczą po interfejsie IValueConverter, który zawiera dwie metody:

object Convert(object value, Type targetType, object parameter, CultureInfo culture);
object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);


Metoda Convert przyjmuje jako pierwszy parametr wartość właściwości źródłowej i zamienia ją na wartość zgodną z naszą DependencyProperty takiego samego typu jak targetType. Metoda Convert odpala się po zmianie właściwości źródłowej (ViewModelProperty). Metoda ConvertBack działa analogicznie i robi konwersję w drugim kierunku. Uruchamia się zazwyczaj po interakcji użytkownika czyli wtedy gdy zmiana pochodzi od DependencyProperty.
<TextBlock Text="{Binding ShowText, Converter={StaticResource BooleanToVisibilityConverter}}"/>


Oczywiście każdy konwerter, którego używamy musi być zdefiniowany w Resources naszego widoku:
<SomeView.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</SomeView.Resources>


.Net dostarcza nam całą gamę gotowych konwerterów jak chociażby powyższy BooleanToVisibilityConverter, dzięki czemu nie musimy zawsze definiować własnych konwerterów.
Każdy konwerter ma właściwość ConverterParameter typu object. Za jej pomocą możemy sobie przekazać dowolną wartość do funkcji konwertujących. Jest to jednak zwykła właściwość dlatego nie możemy się do niej zbindować.
Warto zwrócić uwagę na statyczną wartość Binding.DoNothing którą może zwracać konweter.
Jeżeli ją zwrócimy, wówczas nie zajdą żadne zmiany we właściwości do której konwerter przekazuje wartość.
Istnieje jeszcze możliwość stworzenia konwertera wielowartościowego, który musi implementować interfejs IMultiValueConverte. Zawiera on dwie metody:
object Convert(object[] values, Type targetType, object parameter, CultureInfo culture);
object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture);


Tak jak widać jedyną różnicą jest to, że zamiast pojedynczej wartości mamy do czynienia w tablicą wartości.

0 komentarze:

Prześlij komentarz

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