Zakresy nazw XAML WPF
Zakresy nazw XAML to pojęcie identyfikujące obiekty zdefiniowane w języku XAML. Nazwy w zakresie nazw XAML mogą służyć do ustanawiania relacji między nazwami obiektów zdefiniowanymi przez XAML a ich odpowiednikami wystąpień w drzewie obiektów. Zazwyczaj przestrzenie nazw XAML w kodzie zarządzanym WPF są tworzone, gdy ładowane są poszczególne korzenie stron XAML w aplikacji XAML. Nazwy XAML jako obiekty programowania są definiowane przez interfejs INameScope i implementowane również przez konkretną klasę NameScope.
Zakresy nazw w załadowanych aplikacjach XAML
W szerszym kontekście programowania lub informatyki koncepcje programowania często obejmują zasadę unikatowego identyfikatora lub nazwy, która może służyć do uzyskiwania dostępu do obiektu. W przypadku systemów korzystających z identyfikatorów lub nazw zakres nazw definiuje granice, w których jest wyszukiwany proces lub technika, jeśli żądany jest obiekt tej nazwy lub granice, w których wymuszana jest unikatowość nazw identyfikujących. Te ogólne zasady są prawdziwe dla zakresów nazw XAML. W WPF przestrzenie nazw XAML są tworzone na elemencie głównym strony XAML podczas ładowania strony. Każda nazwa określona na stronie XAML, rozpoczynając od elementu głównego strony, jest dodawana do odpowiedniego zakresu nazw XAML.
W języku WPF XAML elementy, które są typowymi elementami głównymi (takimi jak Pagei Window) zawsze kontrolują zakres nazw XAML. Jeśli element taki jak FrameworkElement lub FrameworkContentElement jest elementem głównym strony w znacznikach, procesor XAML dodaje element Page niejawnie, aby Page mógł zapewnić działający zakres nazw XAML.
Notatka
Akcje kompilacji WPF tworzą zakres nazw XAML dla produkcji XAML, nawet jeśli na żadnym z elementów w znacznikach XAML nie są zdefiniowane atrybuty Name
lub x:Name
.
Jeśli spróbujesz użyć tej samej nazwy dwa razy w dowolnymscope nazw XAML, zostanie zgłoszony wyjątek. W przypadku WPF XAML, który zawiera kod zaplecza i jest częścią skompilowanej aplikacji, wyjątek jest zgłaszany w czasie kompilacji przez działania kompilacji WPF podczas tworzenia wygenerowanej klasy dla strony w trakcie początkowego etapu kompilacji znaczników. W przypadku języka XAML, który nie jest kompilowany przez żadną akcję kompilacji, wyjątki związane z problemami z zakresem nazw XAML mogą być zgłaszane po załadowaniu kodu XAML. Projektanci XAML mogą także przewidywać problemy z zakresem nazw XAML podczas projektowania.
Dodawanie obiektów do drzew obiektów środowiska uruchomieniowego
Moment, w którym kod XAML jest analizowany, oznacza stworzenie i zdefiniowanie zakresu nazw WPF XAML. Jeśli dodasz obiekt do drzewa obiektów w pewnym momencie po tym, jak XAML utworzyło to drzewo, wartość Name
lub x:Name
nowego obiektu nie zaktualizuje automatycznie informacji w przestrzeni nazw XAML. Aby dodać nazwę obiektu do zakresu nazw XAML WPF po załadowaniu kodu XAML, należy wywołać odpowiednią implementację RegisterName na obiekcie, który definiuje zakres nazw XAML, który jest zazwyczaj katalogiem głównym strony XAML. Jeśli nazwa nie jest zarejestrowana, dodany obiekt nie może być przywoływany przez nazwę za pomocą metod, takich jak FindName, i nie można użyć tej nazwy do określania celu animacji.
Najbardziej typowym scenariuszem dla deweloperów aplikacji jest użycie RegisterName do rejestrowania nazw w przestrzeni nazw XAML na bieżącym korzeniu strony. RegisterName jest częścią ważnego scenariusza dla storybordów, które są przeznaczone do animacji obiektów. Aby uzyskać więcej informacji, zobacz Storyboards Overview.
Jeśli wywołasz RegisterName na obiekcie innym niż obiekt, który definiuje zakres nazw XAML, nazwa jest nadal zarejestrowana w zakresie nazw XAML, w którym znajduje się obiekt wywołujący, tak jak wywołana RegisterName w obiekcie definiującym nazwę XAML.
Zakresy nazw XAML w kodzie
Możesz utworzyć, a następnie użyć zakresów nazw XAML w kodzie. API i pojęcia związane z tworzeniem przestrzeni nazw XAML są takie same nawet w przypadku wyłącznego użycia kodu, ponieważ procesor XAML w WPF używa tych API i pojęć podczas przetwarzania XAML. Koncepcje i interfejs API istnieją głównie na potrzeby znajdowania obiektów według nazwy w drzewie obiektów, który jest zwykle definiowany częściowo lub całkowicie w języku XAML.
W przypadku aplikacji, które są tworzone programowo, a nie z załadowanego kodu XAML, obiekt definiujący zakres nazw XAML musi implementować INameScopelub być klasą pochodną FrameworkElement lub FrameworkContentElement, aby obsługiwać tworzenie zakresu nazw XAML na jego instancjach.
Ponadto w przypadku każdego elementu, który nie jest ładowany i przetwarzany przez procesor XAML, zakres nazw XAML dla obiektu nie jest tworzony ani inicjowany domyślnie. Należy jawnie utworzyć nowy zakres nazw XAML dla każdego obiektu, do którego zamierzasz zarejestrować nazwy później. Aby utworzyć zakres nazw XAML, należy wywołać metodę statyczną SetNameScope. Określ obiekt, który będzie jego właścicielem jako parametr dependencyObject
, oraz nowe wywołanie konstruktora NameScope jako parametr value
.
Jeśli obiekt podany jako dependencyObject
dla SetNameScope nie jest implementacją INameScope, FrameworkElement lub FrameworkContentElement, wywoływanie RegisterName dla żadnych elementów podrzędnych nie będzie miało żadnego wpływu. Jeśli nie uda się jawnie utworzyć nowego zakresu nazw XAML, wywołania RegisterName zgłoszą wyjątek.
Aby zapoznać się z przykładem używania interfejsów API zakresu nazw XAML w kodzie, zobacz
Zakresy nazw XAML w stylach i szablonach
Style i szablony w WPF zapewniają możliwość ponownego użycia i ponownego zastosowania zawartości w prosty sposób. Jednak style i szablony mogą również zawierać elementy z nazwami XAML zdefiniowanymi na poziomie szablonu. Ten sam szablon może być używany wiele razy na stronie. Z tego powodu style i szablony definiują własne zakresy nazw XAML niezależnie od lokalizacji w drzewie obiektów, w której zastosowano styl lub szablon.
Rozważmy następujący przykład:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Page.Resources>
<ControlTemplate x:Key="MyButtonTemplate" TargetType="{x:Type Button}">
<Border BorderBrush="Red" Name="TheBorder" BorderThickness="2">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Page.Resources>
<StackPanel>
<Button Template="{StaticResource MyButtonTemplate}">My first button</Button>
<Button Template="{StaticResource MyButtonTemplate}">My second button</Button>
</StackPanel>
</Page>
W tym miejscu ten sam szablon jest stosowany do dwóch różnych przycisków. Jeśli szablony nie miały dyskretnych przestrzeni nazw XAML, nazwa TheBorder
używana w szablonie spowoduje kolizję nazw w przestrzeni nazw XAML. Każde wystąpienie szablonu ma własny zakres nazw XAML, więc w tym przykładzie każdy zakres nazw XAML będzie zawierać dokładnie jedną nazwę.
Style definiują również własny zakres nazw XAML, głównie tak, aby części scenorysów mogły mieć przypisane określone nazwy. Te nazwy umożliwiają kontrolowanie określonych zachowań, które będą dotyczyć elementów tej nazwy, nawet jeśli szablon został ponownie zdefiniowany w ramach dostosowywania kontrolek.
Znalezienie nazwanych elementów w szablonie jest trudniejsze ze względu na oddzielne zakresy nazw XAML, niż znalezienie niezszablonowanego elementu nazwanego na stronie. Najpierw należy określić zastosowany szablon, uzyskując wartość właściwości Template kontrolki, w której jest stosowany szablon. Następnie wywołasz wersję szablonu FindName, przekazując kontrolkę, w której szablon został zastosowany jako drugi parametr.
Jeśli jesteś autorem kontrolki i generujesz konwencję, w której określony nazwany element w zastosowanym szablonie jest elementem docelowym zachowania zdefiniowanego przez samą kontrolkę, możesz użyć metody GetTemplateChild z kodu implementacji kontrolki. Metoda GetTemplateChild jest chroniona, więc tylko autor kontroli ma do niej dostęp.
Jeśli pracujesz z poziomu szablonu i musisz przejść do zakresu nazw XAML, w którym jest stosowany szablon, pobierz wartość TemplatedParent, a następnie wywołaj FindName. Przykładem pracy w szablonie byłoby napisanie implementacji programu obsługi zdarzeń, w której zdarzenie zostanie zgłoszone z elementu w zastosowanym szablonie.
Zakresy nazw XAML i interfejsy API związane z nazwami
FrameworkElement ma metody FindName, RegisterName i UnregisterName. Jeśli obiekt, na którym wywołujesz te metody, posiada zakres nazw XAML, metody odwołują się do metod odpowiedniego zakresu nazw XAML. W przeciwnym razie element nadrzędny jest sprawdzany, czy jest właścicielem zakresu nazw XAML, a ten proces jest powtarzany rekursywnie, dopóki nie zostanie znaleziony zakres nazw XAML (ze względu na zachowanie procesora XAML, gwarantowane jest, że istnieje zakres nazw XAML w korzeniu). FrameworkContentElement ma analogiczne zachowania, z wyjątkiem, że żaden FrameworkContentElement nie będzie właścicielem przestrzeni nazw XAML. Metody istnieją w FrameworkContentElement, aby wywołania mogły być ostatecznie przekazane do elementu nadrzędnego FrameworkElement.
SetNameScope służy do mapowania nowego zakresu nazw XAML na istniejący obiekt. Możesz wywołać SetNameScope więcej niż raz, aby zresetować lub wyczyścić zakres nazw XAML, ale nie jest to typowe użycie. Ponadto GetNameScope nie jest zwykle używane w kodzie.
Implementacje zakresu nazw XAML
Następujące klasy implementują INameScope bezpośrednio:
ResourceDictionary nie używa nazw XAML ani zakresów nazw; zamiast tego używa kluczy, ponieważ jest to implementacja słownika. Jedynym powodem, dla którego ResourceDictionary implementuje INameScope, jest możliwość zgłaszania wyjątków do kodu użytkownika, które pomagają wyjaśnić rozróżnienie między prawdziwym zakresem nazw XAML i sposobem obsługi kluczy przez ResourceDictionary, a także w celu zapewnienia, że zakresy nazw XAML nie są stosowane do ResourceDictionary przez elementy nadrzędne.
FrameworkTemplate i Style implementują INameScope za pomocą jawnych definicji interfejsu. Jawne implementacje umożliwiają tym zakresom nazw XAML zachowanie się w konwencjonalny sposób, gdy są one dostępne za pośrednictwem interfejsu INameScope, co jest metodą komunikacji zakresów nazw XAML przez wewnętrzne procesy WPF. Jednak jawne definicje interfejsu nie są częścią konwencjonalnej powierzchni API FrameworkTemplate i Style, ponieważ rzadko zachodzi potrzeba bezpośredniego wywoływania metod INameScope na FrameworkTemplate i Style, a lepiej używać innego API, takiego jak GetTemplateChild.
Następujące klasy definiują własny zakres nazw XAML za pomocą klasy pomocniczej System.Windows.NameScope i łączą się z implementacją zakresu nazw XAML poprzez właściwość dołączoną NameScope.NameScope:
Zobacz też
.NET Desktop feedback