Udostępnij za pośrednictwem


Omówienie języka XAML (WPF .NET)

W tym artykule opisano funkcje języka XAML i pokazano, jak można użyć języka XAML do pisania aplikacji platformy Windows Presentation Foundation (WPF). W tym artykule opisano w szczególności sposób implementacji języka XAML na platformie WPF. Sama koncepcja języka XAML wykracza poza platformę WPF.

Co to jest XAML

XAML to deklaratywny język znaczników. Zgodnie z zastosowaniem do modelu programowania .NET język XAML upraszcza tworzenie interfejsu użytkownika dla aplikacji platformy .NET. Można utworzyć widoczne elementy interfejsu użytkownika przy użyciu deklaratywnych znaczników XAML, a następnie oddzielić definicję interfejsu użytkownika od logiki czasu wykonywania za pomocą plików codebehind dołączonych do znaczników za pośrednictwem definicji klas częściowych. W języku XAML bezpośrednio reprezentowane jest tworzenie wystąpień obiektów w określonym zbiorze typów zapasowych zdefiniowanych w zestawach. To wyróżnia go spośród większości innych języków znaczników, które stanowią zazwyczaj języki interpretowane bez takiego bezpośredniego powiązania z systemem typów zapasowych. Język XAML umożliwia utworzenie przepływu pracy, w którym różne osoby mogą oddzielnie pracować nad interfejsem użytkownika i logiką aplikacji, potencjalnie przy użyciu różnych narzędzi.

W postaci tekstowej pliki języka XAML są plikami XML, na ogół mającymi rozszerzenie .xaml. Te pliki mogą być kodowane przy użyciu dowolnego kodowania XML, ale typowe jest kodowanie UTF-8.

W poniższym przykładzie pokazano, jak można utworzyć przycisk w ramach interfejsu użytkownika. Ten przykład ma na celu przedstawienie sposobu, w jaki typowe metafory programowania interfejsu użytkownika są reprezentowane w języku XAML (nie jest to kompletny kod przykładowy).

<StackPanel>
    <Button Content="Click Me"/>
</StackPanel>

Składnia języka XAML w skrócie

W poniższych sekcjach opisano podstawowe formy składni języka XAML i pokazano krótki przykład użycia znaczników. Ich celem nie jest przekazanie pełnych informacji na temat poszczególnych form składni, na przykład tego, jak są one reprezentowane w systemie typów zapasowych. Aby uzyskać więcej informacji na temat specyfiki składni języka XAML, zobacz Szczegółowe informacje o składni języka XAML.

Jeśli masz już doświadczenie z językiem XML, znaczna część materiału w kilku kolejnych sekcjach wyda Ci się znajoma. Wynika to z jednej z podstawowych zasad projektu języka XAML. Język XAML definiuje własne pojęcia, ale działają one w ramach języka XML i formatu znaczników.

Elementy obiektów XAML

Element obiektu zazwyczaj deklaruje wystąpienie typu. Ten typ jest definiowany w zestawach, do których odwołuje się technologia wykorzystująca XAML jako język.

Składnia elementu obiektu zawsze zaczyna się od otwierającego nawiasu ostrego (<). Następuje po nim nazwa typu, którego wystąpienie chcesz utworzyć. Nazwa może zawierać prefiks (ta koncepcja zostanie wyjaśniona później). Następnie można opcjonalnie zadeklarować atrybuty elementu obiektu. Tag elementu obiektu kończy się zamykającym nawiasem ostrym (>). Zamiast tego można też użyć formatu samozamykającego bez zawartości, kończąc tag ukośnikiem i zamykającym nawiasem ostrym (/>). Przyjrzyj się jeszcze raz przykładowemu fragmentowi kodu powyżej.

<StackPanel>
    <Button Content="Click Me"/>
</StackPanel>

Określono w nim dwa elementy obiektów: <StackPanel> (z zawartością i tagiem zamykającym w dalszej części) oraz <Button .../> (w formacie samozamykającym, z kilkoma atrybutami). Elementy obiektów StackPanel i Button są mapowane na nazwę klasy zdefiniowaną przez platformę WPF i należącą do zestawów platformy WPF. Określając tag elementu obiektu, tworzysz instrukcję przetwarzania języka XAML w celu utworzenia nowego wystąpienia typu bazowego. Każde wystąpienie tworzone jest przez wywołanie konstruktora bez parametrów dla danego typu podczas analizowania i ładowania kodu XAML.

Składnia atrybutów (właściwości)

Właściwości obiektu można często wyrazić jako atrybuty elementu obiektu. Składnia atrybutów obejmuje nazwę ustawianej właściwości obiektu i operator przypisania (=). Wartość atrybutu zawsze określa się jako ciąg zawarty w cudzysłowie.

Składnia atrybutów to najbardziej zoptymalizowana składnia ustawiania właściwości, a także najbardziej intuicyjna składnia dla deweloperów, którzy wcześniej używali już języków znaczników. Na przykład poniższy kod tworzy przycisk z czerwonym tekstem na niebieskim tle, na którym jest wyświetlany tekst z właściwości Content.

<Button Background="Blue" Foreground="Red" Content="This is a button"/>

Składnia elementów właściwości

W przypadku niektórych właściwości elementu obiektu nie można zastosować składni atrybutów, ponieważ obiektów lub informacji wymaganych do określenia wartości właściwości nie można odpowiednio wyrazić z zachowaniem ograniczeń dotyczących ciągów i cudzysłowów mających zastosowanie w składni atrybutów. W takich przypadkach można użyć innej składni, nazywanej składnią elementów właściwości.

Składnia tagu początkowego elementu właściwości to <TypeName.PropertyName>. Zawartość takiego tagu to na ogół element obiektu o typie, który dana właściwość przyjmuje jako wartość. Po określeniu zawartości należy zamknąć element właściwości tagiem końcowym. Składnia tagu końcowego to </TypeName.PropertyName>.

Jeśli zastosowanie składni atrybutów jest możliwe, jest to zazwyczaj wygodniejsze i umożliwia pisanie bardziej kompaktowego kodu, ale często jest to tylko kwestia stylu, a nie ograniczenie techniczne. W poniższym przykładzie pokazano te same właściwości, które zostały ustawione w poprzednim przykładzie składni atrybutów, ale tym razem dla wszystkich właściwości obiektu Button zastosowano składnię elementów właściwości.

<Button>
    <Button.Background>
        <SolidColorBrush Color="Blue"/>
    </Button.Background>
    <Button.Foreground>
        <SolidColorBrush Color="Red"/>
    </Button.Foreground>
    <Button.Content>
        This is a button
    </Button.Content>
</Button>

Składnia kolekcji

Język XAML zawiera pewne optymalizacje, umożliwiające generowanie znaczników łatwiejszych do odczytania przez człowieka. Jedna z takich optymalizacji polega na tym, że jeśli określona właściwość przyjmuje typ kolekcji, elementy zadeklarowane w kodzie jako elementy podrzędne tej właściwości stają się częścią kolekcji. W takim przypadku wartością ustawioną dla właściwości kolekcji jest kolekcja elementów obiektów podrzędnych.

W poniższym przykładzie pokazano składnię kolekcji używaną do ustawienia wartości właściwości GradientStops.

<LinearGradientBrush>
    <LinearGradientBrush.GradientStops>
        <!-- no explicit new GradientStopCollection, parser knows how to find or create -->
        <GradientStop Offset="0.0" Color="Red" />
        <GradientStop Offset="1.0" Color="Blue" />
    </LinearGradientBrush.GradientStops>
</LinearGradientBrush>

Właściwości zawartości XAML

Język XAML ma funkcję, dzięki której klasa może określać dokładnie jedną ze swoich właściwości jako właściwość zawartości języka XAML. Elementy podrzędne tego elementu obiektu służą do ustawiania wartości, jaką ma ta właściwość zawartości. Innymi słowy, wyłącznie w przypadku właściwości zawartości można pominąć element właściwości podczas ustawiania tej właściwości w kodzie XAML, aby metafora obiektu nadrzędnego i podrzędnego w kodzie była bardziej widoczna.

Na przykład klasa Border jako właściwość zawartości określa właściwość Child. Dwa następujące elementy Border są traktowane identycznie. Pierwszy z nich korzysta ze składni właściwości zawartości i pomija element właściwości Border.Child. W drugim jawnie pokazano właściwość Border.Child.

<Border>
    <TextBox Width="300"/>
</Border>
<!--explicit equivalent-->
<Border>
    <Border.Child>
        <TextBox Width="300"/>
    </Border.Child>
</Border>

Zgodnie z regułami języka XAML wartość właściwości zawartości XAML należy podać w całości przed wszystkimi innymi elementami właściwości w tym elemencie obiektu lub w całości po nich. Na przykład następujący kod nie da się skompilować.

<Button>I am a
  <Button.Background>Blue</Button.Background>
  blue button</Button>

Aby uzyskać więcej informacji na temat specyfiki składni języka XAML, zobacz Szczegółowe informacje o składni języka XAML.

Zawartość tekstowa

W kilku elementach XAML można bezpośrednio przetwarzać tekst jako zawartość. Aby było to możliwe, musi być spełniony jeden z następujących warunków:

  • Klasa musi deklarować właściwość zawartości o typie, który można przypisać do ciągu (może to być typ Object). Na przykład dowolny element ContentControl używa właściwości zawartości Content, której typ to Object, co umożliwia następujące zastosowanie w przypadku elementu ContentControl takiego jak Button: <Button>Hello</Button>.

  • Ten typ musi deklarować konwerter typów, a w takim przypadku zawartość tekstowa jest używana jako tekst inicjowania dla tego konwertera typów. Na przykład kod <Brush>Blue</Brush> konwertuje wartość zawartości Blue na pędzel. Takie przypadki są mniej powszechne w praktyce.

  • Typ musi być znanym typem pierwotnym języka XAML.

Połączenie składni kolekcji i właściwości zawartości

Rozważ następujący przykład.

<StackPanel>
    <Button>First Button</Button>
    <Button>Second Button</Button>
</StackPanel>

Tutaj każdy element Button jest elementem podrzędnym elementu StackPanel. Jest to zoptymalizowany i intuicyjny kod, w którym pominięto dwa tagi z dwóch różnych przyczyn.

  • Pominięto element właściwości StackPanel.Children:StackPanel pochodzi z elementu Panel. Element Panel definiuje właściwość Panel.Children jako swoją właściwość zawartości XAML.

  • Pominięto element obiektu UIElementCollection: właściwość Panel.Children przyjmuje typ UIElementCollection, który implementuje kolekcję IList. Tag elementu kolekcji można pominąć ze względu na reguły przetwarzania kolekcji XAML, takich jak IList. (W tym przypadku nie można tak naprawdę utworzyć wystąpienia kolekcji UIElementCollection, ponieważ nie uwidacznia ona konstruktora bez parametrów, dlatego element obiektu UIElementCollection jest w tym przykładzie oznaczony jako komentarz).

<StackPanel>
    <StackPanel.Children>
        <!--<UIElementCollection>-->
        <Button>First Button</Button>
        <Button>Second Button</Button>
        <!--</UIElementCollection>-->
    </StackPanel.Children>
</StackPanel>

Składnia atrybutów (zdarzenia)

Składni atrybutów można również używać w przypadku składowych, które są zdarzeniami, a nie właściwościami. W takim przypadku nazwa atrybutu jest nazwą zdarzenia. W implementacji zdarzeń XAML na platformie WPF wartość atrybutu jest nazwą procedury obsługi, która implementuje delegata tego zdarzenia. Na przykład w poniższym kodzie przypisano procedurę obsługi zdarzenia Click do obiektu Button utworzonego przy użyciu znacznika:

<Button Click="Button_Click" >Click Me!</Button>

Zdarzenia i kod XAML na platformie WPF to zagadnienie znacznie szersze niż ten przykład składni atrybutu. Być może zastanawiasz się na przykład, co reprezentuje i jak jest definiowany element ClickHandler wymieniony tutaj. Zostanie to wyjaśnione w sekcji Zdarzenia i pliki codebehind języka XAML w dalszej części tego artykułu.

Wielkość liter i białe znaki w języku XAML

Język XAML z zasady uwzględnia wielkość liter. Na potrzeby rozpoznawania typów zapasowych w języku XAML na platformie WPF wielkość liter jest uwzględniana na tych samych zasadach, co w środowisku uruchomieniowym CLR. Elementy obiektów, elementy właściwości i nazwy atrybutów należy określać z uwzględnieniem wielkości liter, gdy ich nazwy są porównywane z bazowym typem w zestawie lub ze składową typu. Wielkość liter jest też uwzględniana w słowach kluczowych i typach pierwotnych języka XAML. W wartościach nie zawsze jest uwzględniana wielkość liter. Uwzględnianie wielkości liter w wartościach zależy od działania konwertera typów skojarzonego z właściwością przyjmującą daną wartość lub z typem wartości tej właściwości. Na przykład wartości przyjmujące typ Boolean mogą przyjmować wartości true i True jako równoważne, ale tylko dlatego, że natywna konwersja typów analizatora języka XAML platformy WPF, z ciągu na typ Boolean, dopuszcza je jako równoważne.

Procesory i serializatory języka XAML dla platformy WPF będą ignorować lub usuwać wszystkie nieistotne białe znaki i normalizować wszelkie istotne białe znaki. Jest to zgodne z zaleceniami dotyczącymi domyślnego działania białych znaków w specyfikacji języka XAML. To zachowanie ma znaczenie tylko w przypadku określania ciągów we właściwościach zawartości XAML. Mówiąc najprościej, język XAML konwertuje znaki spacji, nowego wiersza i tabulatora na spacje, a następnie zachowuje jedną spację, jeśli zostanie znaleziona na początku lub na końcu nieprzerwanego ciągu. Ten artykuł nie zawiera pełnego wyjaśnienia obsługi białych znaków w języku XAML. Aby uzyskać więcej informacji, zobacz Przetwarzanie białych znaków w języku XAML.

Rozszerzenia struktury znaczników

Rozszerzenia struktury znaczników to koncepcja języka XAML. Użycie nawiasów klamrowych ({ i }) podczas podawania wartości w składni atrybutów wskazuje na zastosowanie rozszerzenia struktury znaczników. To użycie powoduje, że przetwarzanie kodu XAML ucieka od ogólnego traktowania wartości atrybutów albo jako ciągów literału albo jako wartości konwertowalnych na ciąg.

Najczęściej używane rozszerzenia struktury znaczników w przypadku tworzenia aplikacji WPF to Binding, używane w wyrażeniach powiązań danych, oraz odwołania do zasobów StaticResource i DynamicResource. Korzystając z rozszerzeń struktury znaczników, można wprowadzać wartości właściwości przy użyciu składni atrybutów, nawet jeśli dana właściwość generalnie nie obsługuje składni atrybutów. Rozszerzenia struktury znaczników często korzystają z typów wyrażeń pośrednich do włączania funkcji takich jak opóźnianie wartości lub odwoływanie się do innych obiektów, które są obecne tylko w czasie wykonywania.

Na przykład następujący kod ustawia wartość właściwości Style przy użyciu składni atrybutów. Właściwość Style przyjmuje wystąpienie klasy Style, którego domyślnie nie można utworzyć przy użyciu ciągu składni atrybutów. Jednak w tym przypadku ten atrybut odwołuje się do określonego rozszerzenia struktury znaczników, StaticResource. Gdy to rozszerzenie struktury znaczników jest przetwarzane, zwraca odwołanie do stylu, którego wystąpienie utworzono wcześniej jako zasób z kluczem w słowniku zasobów.

<Window x:Class="index.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="100" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
        <Style TargetType="Border" x:Key="PageBackground">
            <Setter Property="BorderBrush" Value="Blue"/>
            <Setter Property="BorderThickness" Value="5" />
        </Style>
    </Window.Resources>
    <Border Style="{StaticResource PageBackground}">
        <StackPanel>
            <TextBlock Text="Hello" />
        </StackPanel>
    </Border>
</Window>

Aby zapoznać się z listą referencyjną wszystkich rozszerzeń struktury znaczników dla języka XAML zaimplementowanych na platformie WPF, zobacz Rozszerzenia języka XAML na platformie WPF. Aby zapoznać się z listą referencyjną rozszerzeń znaczników zdefiniowanych przez system.Xaml i są szerzej dostępne dla implementacji języka XAML platformy .NET, zobacz Funkcje języka XAML (x:). Aby uzyskać więcej informacji na temat pojęć dotyczących rozszerzeń struktury znaczników, zobacz Rozszerzenia struktury znaczników a język XAML na platformie WPF.

Konwertery typów

W sekcji Składnia języka XAML w skrócie podano, że wartość atrybutu musi być możliwa do ustawienia przy użyciu ciągu. Podstawowy, natywny sposób obsługi konwersji ciągów na inne typy obiektów lub wartości pierwotne opiera się na samym typie String oraz na natywnym przetwarzaniu pewnych typów takich jak DateTime i Uri. Jednak wiele typów platformy WPF lub ich składowych rozszerza podstawowe działanie przetwarzania atrybutów w postaci ciągu, dzięki czemu wystąpienia bardziej złożonych typów obiektów również można określić jako ciągi i atrybuty.

Struktura Thickness to przykład typu z konwersją typów na potrzeby użycia w kodzie XAML. Struktura Thickness wskazuje wymiary w zagnieżdżonym prostokącie i jest używana jako wartość właściwości takich jak Margin. Dzięki umieszczeniu konwertera typu w strukturze Thickness wszystkie właściwości używające wartości Thickness są łatwiejsze do określenia w języku XAML, ponieważ można je określić jako atrybuty. W poniższym przykładzie użyto konwersji typu i składni atrybutu, aby podać wartość właściwości Margin:

<Button Margin="10,20,10,30" Content="Click me"/>

Powyższy przykład składni atrybutu jest odpowiednikiem poniższej, bardziej rozbudowanej składni przykładowej, gdzie właściwość Margin jest zamiast tego ustawiana za pomocą składni elementu właściwości zawierającej element obiektu Thickness. Cztery kluczowe właściwości obiektu Thickness są ustawione jako atrybuty nowego wystąpienia:

<Button Content="Click me">
    <Button.Margin>
        <Thickness Left="10" Top="20" Right="10" Bottom="30"/>
    </Button.Margin>
</Button>

Uwaga

Istnieje również ograniczona liczba obiektów, w których konwersja typu jest jedynym publicznym sposobem ustawienia właściwości na dany typ bez użycia podklasy, ponieważ sam typ nie ma konstruktora bez parametrów. Może to być na przykład Cursor.

Aby uzyskać więcej informacji na temat konwersji typów, zobacz Konwertery typów i język XAML.

Elementy główne i przestrzenie nazw

Plik XAML musi mieć tylko jeden element główny, aby był zarówno poprawnie sformułowanym plikiem XML, jak i prawidłowym plikiem XAML. W typowych scenariuszach użycia platformy WPF używa się elementu głównego mającego wyraźne znaczenie w modelu aplikacji WPF (na przykład Window lub Page w przypadku strony, ResourceDictionary w przypadku słownika zewnętrznego lub Application w przypadku definicji aplikacji). Poniższy przykład przedstawia element główny typowego pliku XAML dla strony WPF z elementem głównym Page.

<Page x:Class="index.Page1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Page1">

</Page>

Element główny zawiera również atrybuty xmlns i xmlns:x. Te atrybuty wskazują procesorowi języka XAML, które przestrzenie nazw XAML zawierają definicje typów dla typów zapasowych, do których kod będzie się odwoływać jako do elementów. Atrybut xmlns wskazuje konkretnie domyślną przestrzeń nazw XAML. W domyślnej przestrzeni nazw XAML elementy obiektów w kodzie można określać bez prefiksu. W większości scenariuszy dotyczących aplikacji WPF i w prawie wszystkich przykładach podanych w sekcjach zestawu SDK dla platformy WPF domyślna przestrzeń nazw XAML jest mapowana na przestrzeń nazw http://schemas.microsoft.com/winfx/2006/xaml/presentation platformy WPF. Atrybut xmlns:x wskazuje dodatkową przestrzeń nazw XAML, która mapuje przestrzeń nazw http://schemas.microsoft.com/winfx/2006/xamljęzyka XAML.

To użycie atrybutu xmlns w celu zdefiniowania użycia i mapowania zakresu nazw jest zgodne ze specyfikacją języka XML w wersji 1.0. Zakresy nazw XAML różnią się od zakresów nazw XML tylko tym, że zakres nazw XAML ma też znaczenie dla sposobu, w jaki jego elementy powiązane są z typami podczas rozpoznawania typów i analizowania kodu XAML.

Atrybuty xmlns są ściśle niezbędne tylko w przypadku elementu głównego każdego pliku XAML. Definicje atrybutów xmlns będą stosowane do wszystkich elementów podrzędnych elementu głównego (to działanie również jest zgodne ze specyfikacją języka XML w wersji 1.0 dla atrybutuxmlns). Atrybuty xmlns są też dozwolone w przypadku innych elementów znajdujących się poniżej elementu głównego i mają zastosowanie do wszelkich elementów podrzędnych definiującego elementu. Jednak częste definiowanie i ponowne definiowanie przestrzeni nazw XAML może spowodować, że styl kodu XAML będzie mało czytelny.

Implementacja procesora XAML na platformie WPF obejmuje infrastrukturę uwzględniającą podstawowe zestawy platformy WPF. Podstawowe zestawy platformy WPF zawierają typy obsługujące mapowania platformy WPF na domyślną przestrzeń nazw XAML. Jest to możliwe dzięki konfiguracji stanowiącej część pliku kompilacji projektu oraz systemów kompilacji i projektów platformy WPF. Zatem zadeklarowanie domyślnej przestrzeni nazw XAML jako domyślnej wartości atrybutu xmlns wystarczy do odwoływania się do elementów języka XAML pochodzących z zestawów platformy WPF.

Prefiks x:

W poprzednim przykładzie elementu głównego użyto prefiksu x: do mapowania przestrzeni nazw XAML http://schemas.microsoft.com/winfx/2006/xaml, czyli dedykowanej przestrzeni nazw XAML obsługującej konstrukcje języka XAML. Ten prefiks x: służy do mapowania tej przestrzeni nazw XAML w szablonach projektów, w przykładach i w dokumentacji w całym tym zestawie SDK. Przestrzeń nazw XAML dla języka XAML zawiera kilka konstrukcji programowania, z których będziesz często korzystać we własnym kodzie XAML. Poniżej znajduje się lista najczęściej używanych konstrukcji programowania z prefiksem x::

  • x:Key: ustawia unikatowy klucz dla każdego zasobu w elemencie ResourceDictionary (lub w podobnych koncepcjach słownika w innych strukturach). Konstrukcja x:Key prawdopodobnie będzie odpowiadać za 90% użycia prefiksu x: w kodzie typowej aplikacji WPF.

  • x:Class: określa przestrzeń nazw środowiska uruchomieniowego CLR i nazwę klasy udostępniającej plik codebehind strony XAML. Zgodnie z modelem programowania platformy WPF wymagana jest klasa obsługująca plik codebehind, dlatego niemal zawsze prefiks x: będzie mapowany, nawet jeśli nie ma zasobów.

  • x:Name: określa nazwę obiektu czasu wykonywania dla wystąpienia istniejącego w kodzie czasu wykonywania po przetworzeniu elementu obiektu. Na ogół będziesz często używać zdefiniowanej na platformie WPF właściwości równoważnej wobec konstrukcji x:Name. Takie właściwości są mapowane konkretnie na właściwość zapasową środowiska uruchomieniowego CLR i dlatego są wygodniejsze w przypadku programowania aplikacji, gdy często używa się kodu czasu wykonywania do znalezienia nazwanych elementów z zainicjowanego kodu XAML. Najbardziej typową właściwością tego rodzaju jest właściwość FrameworkElement.Name. Nadal można używać konstrukcji x:Name, gdy równoważna właściwość WPF Name na poziomie struktury nie jest obsługiwana w określonym typie. Jest tak w niektórych scenariuszach animacji.

  • x:Static: umożliwia użycie odwołania zwracającego wartość statyczną, która w innym przypadku nie jest właściwością zgodną z językiem XAML.

  • x:Type: tworzy odwołanie Type na podstawie nazwy typu. Służy do określania atrybutów przyjmujących wartość Type, takich jak Style.TargetType, chociaż często właściwości mają natywną konwersję z ciągu na wartość Type, w związku z czym zastosowanie rozszerzenia struktury znaczników x:Type jest opcjonalne.

Istnieją dodatkowe konstrukcje programowania korzystające z prefiksu x:/przestrzeni nazw XAML, które nie są tak powszechne. Aby uzyskać szczegółowe informacje, zobacz Przestrzeń nazw XAML (x:) — funkcje języka.

Prefiksy i typy niestandardowe

W przypadku własnych zestawów niestandardowych lub zestawów innych niż podstawowe zestawy PresentationCore, PresentationFramework i WindowsBase platformy WPF można określić zestaw w ramach niestandardowego mapowania xmlns. Następnie można odwoływać się do typów z tego zestawu we własnym kodzie XAML, o ile dany typ jest poprawnie zaimplementowany na potrzeby obsługi pożądanych zastosowań języka XAML.

Poniżej przedstawiono podstawowy przykład działania niestandardowych prefiksów w kodzie XAML. Prefiks custom jest zdefiniowany w tagu elementu głównego i zamapowany na określony zestaw, który jest spakowany z aplikacją i dostępny dla niej. Ten zestaw zawiera typ NumericUpDown, zaimplementowany w celu obsługi ogólnego użycia języka XAML, z użyciem dziedziczenia klasy, które umożliwia wstawienie go w tym konkretnym punkcie w modelu zawartości XAML na platformie WPF. Wystąpienie tej kontrolki NumericUpDown jest deklarowane jako element obiektu przy użyciu prefiksu, tak aby analizator XAML wiedział, która przestrzeń nazw XAML zawiera odpowiedni typ i gdzie znajduje się zestaw zapasowy zawierający definicję typu.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:NumericUpDownCustomControl;assembly=CustomLibrary"
    >
  <StackPanel Name="LayoutRoot">
    <custom:NumericUpDown Name="numericCtrl1" Width="100" Height="60"/>
...
  </StackPanel>
</Page>

Aby uzyskać więcej informacji na temat typów niestandardowych w języku XAML, zobacz XAML i klasy niestandardowe na platformie WPF.

Aby uzyskać więcej informacji na temat związku między przestrzeniami nazw XML a przestrzeniami nazw kodu w zestawach, zobacz Przestrzenie nazw XAML i mapowanie przestrzeni nazw w przypadku języka XAML na platformie WPF.

Zdarzenia i pliki codebehind języka XAML

Większość aplikacji WPF zawiera zarówno kod XAML, jak i pliki codebehind. W projekcie kod XAML jest zapisywany jako plik .xaml, a do napisania pliku codebehind używa się języka środowiska uruchomieniowego CLR, takiego jak Microsoft Visual Basic lub C#. Podczas kompilowania znaczników z pliku XAML w ramach modeli programowania i aplikacji WPF lokalizację pliku codebehind języka XAML dla pliku XAML wskazuje się przez określenie przestrzeni nazw i klasy jako atrybutu x:Class elementu głównego pliku XAML.

W przykładach podanych do tej pory pokazano kilka przycisków, ale żaden z nich nie miał jeszcze skojarzonego żadnego działania logicznego. Podstawowym mechanizmem dodawania działania do elementu obiektu na poziomie aplikacji jest użycie istniejącego zdarzenia klasy elementu i napisanie specyficznej procedury obsługi tego zdarzenia, wywoływanej w przypadku wystąpienia zdarzenia w czasie wykonywania. Nazwę zdarzenia i nazwę procedury obsługi do użycia określa się w kodzie XAML, a kod wdrażający procedurę obsługi definiuje się w pliku codebehind.

<Page x:Class="ExampleNamespace.ExamplePage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <Button Click="Button_Click">Click me</Button>
    </StackPanel>
</Page>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ExampleNamespace;

public partial class ExamplePage : Page
{
    public ExamplePage() =>
        InitializeComponent();

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        var buttonControl = (Button)e.Source;
        buttonControl.Foreground = Brushes.Red;
    }
}
Class ExamplePage
    Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
        Dim buttonControl = DirectCast(e.Source, Button)
        buttonControl.Foreground = Brushes.Red
    End Sub
End Class

Zwróć uwagę, że plik za kodem używa przestrzeni ExampleNamespace nazw CLR (przestrzeń nazw nie jest widoczna w Visual Basic) i deklaruje ExamplePage jako klasę częściową w tej przestrzeni nazw. To odpowiada atrybutu x:Class określonej jako ExampleNamespace. ExamplePage w elemencie głównym znaczników. Kompilator języka znaczników platformy WPF utworzy klasę częściową dla każdego kompilowanego pliku XAML, wyprowadzając klasę z typu elementu głównego. Gdy dostarczysz plik codebehind, który również definiuje tę samą klasę częściową, wynikowy kod jest łączony z tą samą przestrzenią nazw i klasą, co w kompilowanej aplikacji.

Ważne

W języku Visual Basic główna przestrzeń nazw jest dorozumiana zarówno dla kodu XAML, jak i kodu. Widoczne są tylko zagnieżdżone przestrzenie nazw. W tym artykule przedstawiono kod XAML projektu języka C#.

Aby uzyskać więcej informacji na temat wymagań dotyczących programowania z użyciem plików codebehind na platformie WPF, zobacz Wymagania dotyczące plików codebehind, obsługi zdarzeń i klas częściowych na platformie WPF.

Jeśli nie chcesz tworzyć oddzielnego pliku codebehind, możesz również umieścić kod śródwierszowy w pliku XAML. Jednak użycie kodu śródwierszowego to mniej wszechstronna technika, która ma znaczne ograniczenia. Aby uzyskać więcej informacji, zobacz Pliki codebehind i język XAML na platformie WPF.

Zdarzenia trasowane

Szczególną funkcją zdarzeń o podstawowym znaczeniu dla platformy WPF jest zdarzenie trasowane. Zdarzenia trasowane umożliwiają elementowi obsługę zdarzenia, które zostało zgłoszone przez inny element, o ile te elementy są połączone za pośrednictwem relacji drzewa. W przypadku określenia obsługi zdarzeń przy użyciu atrybutu XAML można nasłuchiwać zdarzenia trasowanego i obsługiwać je w dowolnym elemencie, w tym w elementach, które nie mają tego konkretnego zdarzenia w tabeli składowych klasy. W tym celu atrybut nazwy zdarzenia jest kwalifikowany przy użyciu nazwy klasy, do której należy. Na przykład element nadrzędny StackPanel w bieżącym przykładzie dotyczącym obiektów StackPanel / Button może zarejestrować procedurę obsługi dla zdarzenia Click przycisku elementu podrzędnego, określając atrybut Button.Click w elemencie obiektu StackPanel, z nazwą procedury obsługi jako wartością atrybutu. Aby uzyskać więcej informacji, zobacz Omówienie zdarzeń trasowanych.

Elementy nazwane

Domyślnie wystąpienie obiektu utworzone na wykresie obiektów w wyniku przetworzenia elementu obiektu XAML nie ma żadnego unikatowego identyfikatora ani odwołania do obiektu. Z kolei w przypadku wywołania konstruktora w kodzie niemal zawsze używa się wyniku konstruktora do ustawienia zmiennej w skonstruowanym wystąpieniu, co umożliwia odwoływanie się do tego wystąpienia w dalszej części kodu. Aby zapewnić ustandaryzowany dostęp do obiektów tworzonych za pośrednictwem definicji znacznika, język XAML definiuje atrybut x:Name. Można ustawić wartość atrybutu x:Name w dowolnym elemencie obiektu. W pliku codebehind wybrany identyfikator jest odpowiednikiem zmiennej wystąpienia odwołującej się do skonstruowanego wystąpienia. Pod każdym względem nazwane elementy działają tak, jakby były wystąpieniami obiektów (nazwa odwołuje się do danego wystąpienia), a plik codebehind może odwoływać się do nazwanych elementów w celu obsługi interakcji w czasie wykonywania w aplikacji. To powiązanie między wystąpieniami a zmiennymi zapewnia kompilator znaczników XAML platformy WPF, a mówiąc dokładniej, opiera się ono na funkcjach i wzorcach takich jak InitializeComponent, które nie będą szczegółowo omawiane w tym artykule.

Elementy XAML na poziomie struktury na platformie WPF dziedziczą właściwość Name, która stanowi odpowiednik zdefiniowanego w języku XAML atrybutu x:Name. Niektóre inne klasy również zapewniają odpowiedniki na poziomie właściwości dla atrybutu x:Name, który definiuje się też zazwyczaj jako właściwość Name. Mówiąc ogólnie, jeśli nie można znaleźć właściwości Name w tabeli składowych wybranego elementu lub typu, należy użyć zamiast niej atrybutu x:Name. Wartości atrybutu x:Name tworzą identyfikator elementu XAML, który może być używany w czasie wykonywania albo przez określone podsystemy, albo przez metody narzędzi, takie jak FindName.

W poniższym przykładzie ustawiono właściwość Name dla elementu StackPanel. Następnie procedura obsługi obiektu Button w ramach tego elementu StackPanel odwołuje się do elementu StackPanel za pośrednictwem odwołania do wystąpienia buttonContainer ustawionego za pomocą właściwości Name.

<StackPanel Name="buttonContainer">
    <Button Click="RemoveThis_Click">Click to remove this button</Button>
</StackPanel>
private void RemoveThis_Click(object sender, RoutedEventArgs e)
{
    var element = (FrameworkElement)e.Source;
    
    if (buttonContainer.Children.Contains(element))
        buttonContainer.Children.Remove(element);
}
Private Sub RemoveThis_Click(sender As Object, e As RoutedEventArgs)
    Dim element = DirectCast(e.Source, FrameworkElement)

    If buttonContainer.Children.Contains(element) Then
        buttonContainer.Children.Remove(element)
    End If
End Sub

Podobnie jak zmienne, nazwy wystąpień XAML podlegają koncepcji zakresu, a więc można wymuszać stosowanie unikatowych nazw w określonym, przewidywalnym zakresie. Podstawowe znaczniki definiujące stronę określają jeden unikatowy zakres nazw XAML, a granicą zakresu nazw XAML jest element główny tej strony. Jednak inne źródła znaczników, na przykład style lub szablony w ramach stylów, mogą wchodzić w interakcje ze stroną w czasie wykonywania, a takie źródła znaczników często mają własne zakresy nazw XAML, które niekoniecznie są zgodne z zakresem nazw XAML strony. Aby uzyskać więcej informacji na temat atrybutu x:Name i zakresów nazw XAML, zobacz Name, Dyrektywa x:Name lub Zakresy nazw XAML na platformie WPF.

Właściwości dołączone i zdarzenia dołączone

XAML określa funkcję języka, która umożliwia określenie niektórych właściwości lub zdarzeń na dowolnym elemenie, nawet jeśli właściwość lub zdarzenie nie istnieje w definicjach typu dla elementu, na który jest ustawiany. W odniesieniu do właściwości nazywa się to właściwością dołączoną, a w odniesieniu do zdarzeń — zdarzeniem dołączonym. Można wyobrazić sobie właściwości dołączone i zdarzenia dołączone jako globalne składowe, które można ustawić w dowolnym elemencie/wystąpieniu obiektu XAML. Jednak takie elementy/klasy lub ogólna infrastruktura muszą obsługiwać magazyn właściwości zapasowych dla wartości dołączonych.

Właściwości dołączone w języku XAML są zwykle stosowane przy użyciu składni atrybutów. W składni atrybutów właściwość dołączoną określa się w następującej formie: ownerType.propertyName.

Na pierwszy rzut oka przypomina to użycie elementu właściwości, ale w tym przypadku określony typ ownerType jest zawsze inny niż typ elementu obiektu, w którym ustawiana jest właściwość dołączona. Typ ownerType udostępnia metody dostępu wymagane przez procesor XAML w celu uzyskania lub ustawienia wartości właściwości dołączonej.

Najczęstszym scenariuszem użycia właściwości dołączonych jest umożliwienie elementom podrzędnym raportowania wartości właściwości do elementu nadrzędnego.

Poniższy przykład przedstawia użycie właściwości dołączonej DockPanel.Dock. Klasa DockPanel definiuje metody dostępu do właściwości dołączonej DockPanel.Dock i jest jej właścicielem. Klasa DockPanel zawiera również logikę wykonującą iterację przez jej elementy podrzędne i sprawdza każdy element pod kątem ustawionej wartości właściwości DockPanel.Dock. W przypadku znalezienia wartości jest ona używana do określenia położenia elementów podrzędnych w układzie. Użycie właściwości dołączonej DockPanel.Dock i tej funkcji określania położenia to właśnie główne uzasadnienie wprowadzenia klasy DockPanel.

<DockPanel>
    <Button DockPanel.Dock="Left" Width="100" Height="20">I am on the left</Button>
    <Button DockPanel.Dock="Right" Width="100" Height="20">I am on the right</Button>
</DockPanel>

Na platformie WPF większość właściwości dołączonych, jeśli nie wszystkie, implementuje się także jako właściwości zależności. Aby uzyskać więcej informacji, zobacz Omówienie właściwości dołączonych.

Zdarzenia dołączone wykorzystują podobną formę składni atrybutów: ownerType.eventName. Podobnie jak w przypadku zdarzeń niedołączonych wartość atrybutu zdarzenia dołączonego w kodzie XAML określa nazwę metody procedury obsługi, która jest wywoływana w celu obsługi zdarzenia w tym elemencie. Użycie zdarzeń dołączonych w aplikacjach XAML na platformie WPF jest rzadsze. Aby uzyskać więcej informacji, zobacz Omówienie zdarzeń dołączonych.

Typy podstawowe

Bazowy kod XAML na platformie WPF i jego przestrzeń nazw XAML to kolekcja typów odpowiadających obiektom środowiska uruchomieniowego CLR i elementom znaczników XAML. Jednak nie wszystkie klasy można mapować na elementy. Klasy abstrakcyjne, takie jak ButtonBase, i niektóre nieabstrakcyjne klasy bazowe są używane na potrzeby dziedziczenia w modelu obiektów środowiska uruchomieniowego CLR. Klasy bazowe, w tym abstrakcyjne, pozostają ważne podczas programowania w języku XAML, ponieważ każdy konkretny element XAML dziedziczy składowe z jakiejś klasy bazowej w hierarchii. Tymi składowymi często są właściwości, które można ustawić jako atrybuty w elemencie, lub zdarzenia, które można obsługiwać. FrameworkElement to konkretna klasa bazowa interfejsu użytkownika platformy WPF na poziomie struktury. Projektując interfejs użytkownika, będziesz korzystać z różnych klas kształtów, paneli, dekoratorów lub kontrolek, a wszystkie one pochodzą z klasy FrameworkElement. Powiązana klasa bazowa FrameworkContentElement obsługuje elementy zorientowane na dokumenty, które sprawdzają się w przypadku prezentacji układu przepływu i korzystają z interfejsów API celowo dublujących interfejsy API w klasie FrameworkElement. Kombinacja atrybutów na poziomie elementu i modelu obiektów CLR zapewnia zestaw typowych właściwości, które można ustawić w większości konkretnych elementów XAML, niezależnie od tego, jaki to element XAML i jaki jest jego typ bazowy.

Zabezpieczenia

XAML to język znaczników, w którym bezpośrednio reprezentowane jest tworzenie wystąpień obiektów i ich wykonywanie. Dlatego elementy utworzone w kodzie XAML mają taką samą możliwość interakcji z zasobami systemowymi (na przykład dostępem do sieci czy operacjami we/wy systemu plików), jaką ma kod aplikacji. Kod XAML ma również taki sam dostęp do zasobów systemowych, jak hostująca aplikacja.

Zabezpieczenia dostępu kodu (CAS) na platformie WPF

W odróżnieniu od programu .NET Framework platforma WPF dla platformy .NET nie obsługuje funkcji CAS. Aby uzyskać więcej informacji, zobacz Różnice w zabezpieczeniach dostępu kodu.

Ładowanie znaczników XAML z kodu

Za pomocą języka XAML można zdefiniować cały interfejs użytkownika, ale czasami wskazane jest zdefiniowanie w nim tylko fragmentu interfejsu użytkownika. Za pomocą tej funkcji możliwe jest:

  • Częściowe dostosowywanie.
  • Lokalne przechowywanie informacji o interfejsie użytkownika.
  • Modelowanie obiektów biznesowych.

Kluczem do tych scenariuszy jest klasa XamlReader i zawarta w niej metoda Load. Jako dane wejściowe wprowadza się plik XAML, a wynikiem jest obiekt reprezentujący całe drzewo obiektów czasu wykonywania utworzonych na podstawie znaczników. Ten obiekt można następnie wstawić jako właściwość innego obiektu, który już istnieje w aplikacji. O ile dana właściwość znajduje się w modelu zawartości i ma funkcje wyświetlania, które powiadomią aparat wykonywania o dodaniu nowej zawartości do aplikacji, można łatwo modyfikować zawartość uruchomionej aplikacji dzięki dynamicznemu ładowaniu kodu XAML.

Zobacz też