MarkupExtensions, czyli jak rozbudować XAML #2
Wysłany dnia 24 listopada 2010 o godzinie 21:43. Komentarzy: 1, kategorie: .NET, Programowanie, tagi: .

Ostatnio pokazywałem, jak napisać proste rozszerzenie XAML – opisałem, jak stworzyć parametrowe i bezparametrowe rozszerzenie. Dziś skupię się na metodzie ProvideValue, a dokładniej na dostawcy usług, który przekazywany jest nam w parametrze.

Pierwszym i jedynym parametrem metody ProvideValue jest coś, co się nazywa IServiceProvider. Jest to swego rodzaju fabryka, która udostępnia nam żądane usługi. A jest ich kilka. Oto ich lista:

Każdą z usług pobiera się jednakowo:

var service = serviceProvider.GetService(typeof(InterfejsUsługi)) as InterfejsUsługi;

MSDN zaleca, by zawsze sprawdzać, czy GetService nie zwraca nam nulla. Nie wiem, kiedy taka sytuacja może nastąpić, lecz wolę nie ryzykować i zawsze trzymam się tego ;)

Teraz pokrótce postaram się opisać każdą z nich.

IProvideValueTarget

Ta usługa służy, jak sama nazwa wskazuje, do pobierania właściwości, w której dane rozszerzenie zostało użyte. Udostępnia ona dwie informacje: TargetObject – obiekt, w którym zostało dane rozszerzenie wywołane oraz TargetProperty – właściwość, do które dane rozszerzenie jest przypisane. Mogą one wskazywać na właściwość elementu jak również na właściwość innego rozszerzenia(gdyż markup extensions można zagnieżdżać*)! Jedno jest pewne – obiekt przechowywany w TargetProperty jest typu PropertyInfo(a przynajmniej ja nie wyśledziłem ani na MSDN, ani przy debugowaniu mojego kodu, by było inaczej) i dzięki temu nie musimy się przejmować typem obiektu ;)

Jednak jest pewien haczyk. Obie(na raz, lub pojedynczo) mogą być puste(mogą być nullami). MSDN twierdzi, że dzieje się tak, gdy zagnieździmy nasze rozszerzenie w innym. Całe szczęście tyczy się to tylko WPF i, gdy wszystkie rozszerzenia piszemy sami, nie musimy się tym przejmować.

IXamlTypeResolver

To rozszerzenie jest bardzo proste i w sumie nie niesie zbyt wielu informacji. Udostępnia metodę Resolve, która to, po podaniu jej nazwy typu XAML(czyli nazwy elementu), zwraca nam odpowiadający jej typ CLR.

IXamlSchemaContextProvider

To rozszerzenie jest nawet prostsze od poprzedniego ;) i służy do pobierania kontekstu mapującego typy XAML do typów CLR(ale zawile to napisałem… niestety, trudno wymyśleć sensowne tłumaczenie dla XamlSchemaContext ;) ).

IUriContext

Wg MSDN ta usługa daje nam możliwość do „use application context to resolve a provided relative URI to an absolute URI„. Przyznam, że nie wiem, jak to sensownie przetłumaczyć, ani nie wiem, do czego to wykorzystać. W WPF np. we właściwości BaseUri dostajemy „pack://application:,,,/WpfApplication1;component/mainwindow.xaml”, co jest linkiem do pliku, w którym, dane rozszerzenie zostało użyte. Jeśli ktoś wie, do czego można to wykorzystać – chętnie wysłucham.

IAmbientProvider

Tutaj też niestety muszę się przyznać do niedouczenia – nie wiem dokładnie, do czego służą ambient properties. Nie potrzebowałem(chyba) czegoś takiego, więc pominąłem to. Z tego, co zdążyłem „przy okazji” na ten temat wyczytać, służą one do wpływania na przebieg parsowania kodu. Domyślnie parser robi to w kolejności napotkania atrybutów. Ta właściwość wymusza, by były one przetwarzane jako pierwsze. Jeśli dowiem się czegoś więcej – na pewno o tym napiszę ;)

Wracając jednak do tej usługi: służy ona do pobierania wszystkich ambient properties i ambient types jakie do tej pory zostały sparsowane.

IDestinationTypeProvider

Kolejna(tym razem już ostatnia) usługa, której działania nie jestem pewien. Metoda GetDestinationType tejże usługi zwraca nam typ, który jest pobrany z aktualnej pozycji w strumieniu XAML. Często zwraca po prostu odpowiadający typ CLR. Niestety, nie udało mi się tego ani razu użyć.

IRootObjectProvider

Usługa ta daje nam dostęp do głównego obiektu(RootObject) dokumentu. Przez „główny obiekt” rozumiemy ten obiekt, który występuje jako pierwszy i jest kontenerem na inne. Np.:

<RootObject property="5" xmlns="...">
    <SubObject1 />
    <SubObject2 />
</RootObject >

Przy wykorzystaniu go należy jednak pamiętać, że nie jest skonstruowany do końca i, jeśli dane rozszerzenie(trochę Was okłamuje – to nie musi być rozszerzenie, równie dobrze może być TypeConverter lub ValueSerializer, ale nie one są tematem moich rozważań) nie było wykorzystane w ostatniej właściwości ostatniego elementu-dziecka, to to, co jest za nim w pliku, nie jest jeszcze dostępne!

IXamlNameResolver

Ta usługa zaś daje nam dostęp do wszystkich obiektów. Identyfikuje się je po właściwości x:Name(lub każdej innej, wystarczy, że dodamy do klasy odpowiedni atrybut – RuntimeNamePropertyAttribute).

Istnieje jednak pewien haczyk – jeśli element, do którego się chcemy odnieść nie został już całkowicie przetworzony, tylko „czeka w kolejce”(występuje za użyciem danego rozszerzenia) – dostaniemy nulla. Istnieje jednak proste rozwiązanie tego. Wystarczy, że z metody ProvideValue zwrócimy to, co zwróci nam GetFixupToken i możemy być pewni, że parser wywoła tą metodę jeszcze raz, wtedy, gdy wszystkie żądane elementy będą już gotowe do użytku.

var resolver = serviceProvider.GetService(typeof(IXamlNameResolver)) as IXamlNameResolver;
object instance = resolver.Resolve("SomeName");
if (instance == null)
{
    return resolver.GetFixupToken(new string[] { "SomeName" });
}

IXamlNamespaceResolver

Dzięki niej dostajemy dostęp do wszystkich przestrzeni nazw(xmlns), które są dostępne w danym miejscu. Możemy pobrać tylko jedną interesującą nas przestrzeń za pomocą jej prefiksu używając do tego metody GetNamespace, jak również możemy wylistować wszystkie za pomocą GetNamespacePrefixes. Niech nie zwiedzie Was nazwa – metoda ta zwraca listę NamespaceDeclaration, która to zawiera i prefiks i samą przestrzeń nazw.

To by było na tyle. Nie są to może bardzo rozbudowane opisy, ale powinny rozjaśnić, co nieco a może i zmusić do dalszych poszukiwań „na własną rękę”. W trakcie pisania tego postu, całkiem przypadkowo, dowiedziałem się kilku ciekawych rzeczy, które ułatwią mi dalsze boje z wprowadzaniem XAML do silnika jak i samej gry(precz XML! :P ). W następnym odcinku postaram się pokazać jak wykorzystać część usług w jednym, ciut bardziej ambitnym niż Random, rozszerzeniu.

  • Trackback: dotnetomaniak.pl
    Wysłany dnia 25 listopada 2010 o godzinie 08:16

    MarkupExtensions, czyli jak rozbudować XAML #2 | Fiołek – blog…

    Dziękujemy za publikację – Trackback z dotnetomaniak.pl…

Dodaj komentarz

XHTML: Możesz użyć: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>