Omówienie zasobów XAML (WPF .NET)

Zasób to obiekt, który może być ponownie używany w różnych miejscach w aplikacji. Przykłady zasobów obejmują pędzle i style. W tym omówieniu opisano sposób używania zasobów w języku XAML (Extensible Application Markup Language). Zasoby można również tworzyć i uzyskiwać do ich dostępu przy użyciu kodu.

Uwaga

Zasoby XAML opisane w tym artykule różnią się od zasobów aplikacji, które są zazwyczaj plikami dodawanymi do aplikacji, takimi jak zawartość, dane lub pliki osadzone.

Ważne

Dokumentacja przewodnika dla komputerów dla platform .NET 7 i .NET 6 jest w budowie.

Korzystanie z zasobów w języku XAML

W poniższym przykładzie zdefiniowano SolidColorBrush element jako zasób w elemecie głównym strony. Następnie przykład odwołuje się do zasobu i używa go do ustawiania właściwości kilku elementów podrzędnych, w tym Ellipse, , TextBlocki Button.

<Window x:Class="resources.ResExample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ResExample" Height="400" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="#05E0E9"/>
        <Style TargetType="Border">
            <Setter Property="Background" Value="#4E1A3D" />
            <Setter Property="BorderThickness" Value="5" />
            <Setter Property="BorderBrush">
                <Setter.Value>
                    <LinearGradientBrush>
                        <GradientStop Offset="0.0" Color="#4E1A3D"/>
                        <GradientStop Offset="1.0" Color="Salmon"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="TextBlock" x:Key="TitleText">
            <Setter Property="FontSize" Value="18"/>
            <Setter Property="Foreground" Value="#4E87D4"/>
            <Setter Property="FontFamily" Value="Trebuchet MS"/>
            <Setter Property="Margin" Value="0,10,10,10"/>
        </Style>
        <Style TargetType="TextBlock" x:Key="Label">
            <Setter Property="HorizontalAlignment" Value="Right"/>
            <Setter Property="FontSize" Value="13"/>
            <Setter Property="Foreground" Value="{StaticResource MyBrush}"/>
            <Setter Property="FontFamily" Value="Arial"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Margin" Value="0,3,10,0"/>
        </Style>
    </Window.Resources>

    <Border>
        <StackPanel>
            <TextBlock Style="{StaticResource TitleText}">Title</TextBlock>
            <TextBlock Style="{StaticResource Label}">Label</TextBlock>
            <TextBlock HorizontalAlignment="Right" FontSize="36" Foreground="{StaticResource MyBrush}" Text="Text" Margin="20" />
            <Button HorizontalAlignment="Left" Height="30" Background="{StaticResource MyBrush}" Margin="40">Button</Button>
            <Ellipse HorizontalAlignment="Center" Width="100" Height="100" Fill="{StaticResource MyBrush}" Margin="10" />
        </StackPanel>
    </Border>

</Window>

Każdy element na poziomie platformy (FrameworkElement lub ) ma Resources właściwość, która jest typem zawierającym ResourceDictionary zdefiniowane FrameworkContentElementzasoby. Zasoby można zdefiniować na dowolnym elemecie, takim jak Button. Jednak zasoby są najczęściej definiowane w elemecie głównym, który znajduje się Window w przykładzie.

Każdy zasób w słowniku zasobów musi mieć unikatowy klucz. Podczas definiowania zasobów w adiustacji należy przypisać unikatowy klucz za pomocą dyrektywy x:Key. Zazwyczaj klucz jest ciągiem; Można go jednak również ustawić na inne typy obiektów przy użyciu odpowiednich rozszerzeń znaczników. Klucze inne niż ciągi dla zasobów są używane przez niektóre obszary funkcji w WPF, zwłaszcza w przypadku stylów, zasobów składników i stylów danych.

Można użyć zdefiniowanego zasobu ze składnią rozszerzenia znaczników zasobów, która określa nazwę klucza zasobu. Na przykład użyj zasobu jako wartości właściwości w innym elemecie.

<Button Background="{StaticResource MyBrush}"/>
<Ellipse Fill="{StaticResource MyBrush}"/>

W poprzednim przykładzie, gdy moduł ładujący XAML przetwarza wartość {StaticResource MyBrush}Background właściwości na Button, logika wyszukiwania zasobów najpierw sprawdza słownik zasobów dla Button elementu. Jeśli Button nie ma definicji klucza MyBrush zasobu (w tym przykładzie nie; jego kolekcja zasobów jest pusta), wyszukiwanie sprawdza następny element nadrzędny elementu Button. Jeśli zasób nie jest zdefiniowany w obiekcie nadrzędnym, nadal sprawdza drzewo logiczne obiektu w górę, dopóki nie zostanie znalezione.

Jeśli zdefiniujesz zasoby w elemecie głównym, wszystkie elementy w drzewie logicznym, takie jak lub WindowPage, będą mogły uzyskać do niego dostęp. Możesz również użyć tego samego zasobu, aby ustawić wartość dowolnej właściwości, która akceptuje ten sam typ, który reprezentuje zasób. W poprzednim przykładzie ten sam MyBrush zasób ustawia dwie różne właściwości: Button.Background i Ellipse.Fill.

Zasoby statyczne i dynamiczne

Zasób może być przywołyny jako statyczny lub dynamiczny. Odwołania są tworzone przy użyciu rozszerzenia StaticResource Markup lub rozszerzenia DynamicResource Markup. Rozszerzenie znaczników to funkcja XAML, która umożliwia określenie odwołania do obiektu przez przetworzenie ciągu atrybutu rozszerzenia znaczników i zwrócenie obiektu do modułu ładującego XAML. Aby uzyskać więcej informacji na temat zachowania rozszerzenia znaczników, zobacz Rozszerzenia znaczników i WPF XAML.

Gdy używasz rozszerzenia znaczników, zazwyczaj podajesz jeden lub więcej parametrów w postaci ciągu, które są przetwarzane przez to określone rozszerzenie znaczników. Rozszerzenie znaczników StaticResource przetwarza klucz, wyszukując wartość tego klucza we wszystkich dostępnych słownikach zasobów. Przetwarzanie odbywa się podczas ładowania, czyli wtedy, gdy proces ładowania musi przypisać wartość właściwości. Rozszerzenie znaczników DynamicResource zamiast tego przetwarza klucz przez utworzenie wyrażenia, a to wyrażenie pozostaje nieowarte do momentu uruchomienia aplikacji, w którym wyrażenie jest oceniane w celu podania wartości.

Jeśli odwołujesz się do zasobu, następujące zagadnienia mogą mieć wpływ na to, czy używasz odwołania do zasobów statycznych, czy odwołania do zasobów dynamicznych:

  • Podczas określania ogólnego projektu sposobu tworzenia zasobów dla aplikacji (na stronę w aplikacji, w luźnym języku XAML lub w zestawie tylko do zasobów) należy wziąć pod uwagę następujące kwestie:

  • Funkcjonalność aplikacji. Czy aktualizowanie zasobów w czasie rzeczywistym jest częścią wymagań aplikacji?

  • Odpowiednie zachowanie odnośnika tego typu odwołania do zasobu.

  • Określona właściwość lub typ zasobu oraz natywne zachowanie tych typów.

Zasoby statyczne

Odwołania do zasobów statycznych działają najlepiej w następujących sytuacjach:

  • Projekt aplikacji koncentruje większość swoich zasobów na słownikach zasobów na poziomie strony lub aplikacji.

    Odwołania do zasobów statycznych nie są ponownie oceniane na podstawie zachowań środowiska uruchomieniowego, takich jak ponowne ładowanie strony. W związku z tym może istnieć pewna korzyść z wydajności, aby uniknąć dużej liczby odwołań do zasobów dynamicznych, gdy nie są one niezbędne w oparciu o projekt zasobu i aplikacji.

  • Ustawiasz wartość właściwości, która nie znajduje się na DependencyObject obiekcie lub Freezable.

  • Tworzysz słownik zasobów skompilowany w bibliotekę DLL współdzieloną między aplikacjami.

  • Tworzysz motyw dla kontrolki niestandardowej i definiujesz zasoby, które są używane w motywach.

    W tym przypadku zwykle nie chcesz, aby zachowanie odnośnika odwołania do zasobów dynamicznych było niedozwolone. Zamiast tego należy użyć zachowania odwołania do zasobów statycznych, aby odnośnik był przewidywalny i samodzielny dla motywu. W przypadku odwołania do zasobów dynamicznych nawet odwołanie w ramach motywu pozostaje niezawarte do czasu wykonywania. Istnieje prawdopodobieństwo, że po zastosowaniu motywu niektóre elementy lokalne ponownie zdefiniowają klucz, do którego próbuje się odwołać motyw, a element lokalny spadnie przed samym motywem w wyszukiwaniu. W takim przypadku motyw nie będzie zachowywał się zgodnie z oczekiwaniami.

  • Używasz zasobów do ustawiania dużej liczby właściwości zależności. Właściwości zależności mają efektywne buforowanie wartości włączone przez system właściwości, więc jeśli podasz wartość właściwości zależności, którą można ocenić w czasie ładowania, właściwość zależności nie musi sprawdzać ponownego oszacowania wyrażenia i może zwrócić ostatnią obowiązującą wartość. Ta technika może być korzyścią dla wydajności.

  • Chcesz zmienić bazowy zasób dla wszystkich użytkowników lub zachować oddzielne wystąpienia z możliwością zapisu dla każdego konsumenta przy użyciu atrybutu x:Shared.

Zachowanie wyszukiwania zasobów statycznych

Poniżej opisano proces wyszukiwania, który odbywa się automatycznie, gdy zasób statyczny jest przywołyny przez właściwość lub element:

  1. Proces wyszukiwania sprawdza żądany klucz w słowniku zasobów zdefiniowanym przez element, który ustawia właściwość.

  2. Następnie proces wyszukiwania przechodzi przez drzewo logiczne w górę do elementu nadrzędnego i jego słownika zasobów. Ten proces będzie kontynuowany do momentu osiągnięcia elementu głównego.

  3. Zasoby aplikacji są sprawdzane. Zasoby aplikacji to te zasoby w słowniku zasobów, które są definiowane przez Application obiekt dla aplikacji WPF.

Odwołania do zasobów statycznych z poziomu słownika zasobów muszą odwoływać się do zasobu, który został już zdefiniowany leksykalnie przed odwołaniem do zasobu. Nie można rozpoznać odwołań do przekazywania przez odwołanie do zasobów statycznych. Z tego powodu należy zaprojektować strukturę słownika zasobów, tak aby zasoby zostały zdefiniowane na początku każdego odpowiedniego słownika zasobów lub w pobliżu nich.

Wyszukiwanie zasobów statycznych może rozszerzyć na motywy lub zasoby systemowe, ale to wyszukiwanie jest obsługiwane tylko dlatego, że moduł ładujący XAML odchyla żądanie. Odroczenie jest konieczne, aby motyw środowiska uruchomieniowego w czasie ładowania strony był prawidłowo stosowany do aplikacji. Jednak odwołania do zasobów statycznych do kluczy, które są znane tylko w motywach lub jako zasoby systemowe nie są zalecane, ponieważ takie odwołania nie są ponownie oceniane, jeśli motyw zostanie zmieniony przez użytkownika w czasie rzeczywistym. Dynamiczna dokumentacja zasobów jest bardziej niezawodna podczas żądania motywu lub zasobów systemowych. Wyjątek występuje, gdy sam element motywu żąda innego zasobu. Te odwołania powinny być odwołaniami do zasobów statycznych ze względów wymienionych wcześniej.

Zachowanie wyjątku, jeśli nie znaleziono odwołania do zasobów statycznych, różni się. Jeśli zasób został odroczony, wyjątek występuje w czasie wykonywania. Jeśli zasób nie został odroczony, wyjątek występuje w czasie ładowania.

Zasoby dynamiczne

Zasoby dynamiczne działają najlepiej, gdy:

  • Wartość zasobu, w tym zasobów systemowych lub zasobów, które w przeciwnym razie można ustawić przez użytkownika, zależy od warunków, które nie są znane do czasu uruchomienia. Można na przykład utworzyć wartości ustawiające, które odwołują się do właściwości systemu uwidacznianych przez SystemColors, SystemFontslub SystemParameters. Te wartości są naprawdę dynamiczne, ponieważ ostatecznie pochodzą ze środowiska uruchomieniowego użytkownika i systemu operacyjnego. Możesz również mieć motywy na poziomie aplikacji, które mogą ulec zmianie, gdzie dostęp do zasobów na poziomie strony musi również przechwycić zmianę.

  • Tworzysz lub odwołujesz się do stylów motywu dla kontrolki niestandardowej.

  • Zamierzasz dostosować zawartość elementu ResourceDictionary w okresie istnienia aplikacji.

  • Masz skomplikowaną strukturę zasobów, która ma współzależności, gdzie może być wymagane odwołanie do przodu. Odwołania do zasobów statycznych nie obsługują odwołań do przekazywania dalej, ale odwołania do zasobów dynamicznych obsługują je, ponieważ zasób nie musi być oceniany do momentu, gdy środowisko uruchomieniowe i odwołania do przodu nie są zatem istotnym pojęciem.

  • Odwołujesz się do zasobu, który jest duży z perspektywy kompilowania lub zestawu roboczego, a zasób może nie być używany natychmiast po załadowaniu strony. Odwołania do zasobów statycznych zawsze ładują się z kodu XAML podczas ładowania strony. Jednak odwołanie do zasobów dynamicznych nie jest ładowane, dopóki nie zostanie użyte.

  • Tworzysz styl, w którym wartości ustawiające mogą pochodzić z innych wartości, które mają wpływ na motywy lub inne ustawienia użytkownika.

  • Stosujesz zasoby do elementów, które mogą być ponownie używane w drzewie logicznym w okresie istnienia aplikacji. Zmiana elementu nadrzędnego również potencjalnie zmienia zakres wyszukiwania zasobów, więc jeśli chcesz, aby zasób ponownie przewartego elementu został przeszacowany na podstawie nowego zakresu, zawsze użyj odwołania do zasobów dynamicznych.

Zachowanie dynamicznego wyszukiwania zasobów

Zachowanie wyszukiwania zasobów dla dynamicznego odwołania do zasobów jest równoległe zachowanie wyszukiwania w kodzie, jeśli wywołasz FindResource metodę lub SetResourceReference:

  1. Wyszukiwanie sprawdza żądany klucz w słowniku zasobów zdefiniowanym przez element, który ustawia właściwość:

  2. Odnośnik przechodzi przez drzewo logiczne w górę do elementu nadrzędnego i jego słownika zasobów. Ten proces będzie kontynuowany do momentu osiągnięcia elementu głównego.

  3. Zasoby aplikacji są sprawdzane. Zasoby aplikacji to te zasoby w słowniku zasobów, które są definiowane przez Application obiekt dla aplikacji WPF.

  4. Słownik zasobów motywu jest sprawdzany pod kątem aktualnie aktywnego motywu. Jeśli motyw zmieni się w czasie wykonywania, wartość zostanie ponownie zceniona.

  5. Zasoby systemowe są sprawdzane.

Zachowanie wyjątku (jeśli istnieje) różni się:

  • Jeśli zasób został żądany przez FindResource wywołanie i nie został znaleziony, zgłaszany jest wyjątek.

  • Jeśli zasób został żądany przez TryFindResource wywołanie i nie został znaleziony, nie zostanie zgłoszony wyjątek, a zwrócona wartość to null. Jeśli ustawiona właściwość nie akceptuje nullwartości , nadal jest możliwe, że zostanie zgłoszony większy wyjątek, w zależności od ustawionej właściwości indywidualnej.

  • Jeśli zasób został żądany przez odwołanie do zasobu dynamicznego w języku XAML i nie został znaleziony, zachowanie zależy od ogólnego systemu właściwości. Ogólne zachowanie jest takie, jakby na poziomie, na którym istnieje zasób, nie wystąpiła żadna operacja ustawienia właściwości. Jeśli na przykład próbujesz ustawić tło na pojedynczym elemencie przycisku przy użyciu zasobu, którego nie można ocenić, nie ustawiono żadnych wyników, ale efektywna wartość nadal może pochodzić od innych uczestników systemu właściwości i pierwszeństwa wartości. Na przykład wartość tła może nadal pochodzić ze stylu przycisku zdefiniowanego lokalnie lub stylu motywu. W przypadku właściwości, które nie są zdefiniowane przez style motywu, efektywna wartość po nieudanej ocenie zasobu może pochodzić z wartości domyślnej w metadanych właściwości.

Ograniczenia

Odwołania do zasobów dynamicznych mają pewne istotne ograniczenia. Co najmniej jeden z następujących warunków musi być spełniony:

Ponieważ ustawiana właściwość musi być właściwością DependencyProperty lub Freezable , większość zmian właściwości może być propagowana do interfejsu użytkownika, ponieważ zmiana właściwości (zmieniona wartość zasobu dynamicznego) jest potwierdzana przez system właściwości. Większość kontrolek obejmuje logikę, która wymusza inny układ kontrolki, jeśli DependencyProperty zmiany i ta właściwość może mieć wpływ na układ. Jednak nie wszystkie właściwości, które mają rozszerzenie znaczników DynamicResource, ponieważ ich wartość jest gwarantowana, aby zapewnić aktualizacje w czasie rzeczywistym w interfejsie użytkownika. Ta funkcja nadal może się różnić w zależności od właściwości i w zależności od typu, który jest właścicielem właściwości, a nawet logicznej struktury aplikacji.

Style, DataTemplates i niejawne klucze

Mimo że wszystkie elementy w obiekcie ResourceDictionary muszą mieć klucz, nie oznacza to, że wszystkie zasoby muszą mieć jawny x:Keyelement . Kilka typów obiektów obsługuje niejawny klucz zdefiniowany jako zasób, gdzie wartość klucza jest powiązana z wartością innej właściwości. Ten typ klucza jest znany jako niejawny klucz, a x:Key atrybut jest kluczem jawnym. Dowolny niejawny klucz można zastąpić, określając jawny klucz.

Jednym z ważnych scenariuszy dla zasobów jest zdefiniowanie elementu Style. W rzeczywistości element Style jest prawie zawsze zdefiniowany jako wpis w słowniku zasobów, ponieważ style są z założenia przeznaczone do ponownego użycia. Aby uzyskać więcej informacji na temat stylów, zobacz Style i szablony (WPF .NET).

Style dla kontrolek można utworzyć za pomocą i odwołać się do niejawnego klucza. Style motywu definiujące domyślny wygląd kontrolki opierają się na tym niejawnym kluczu. Z punktu widzenia żądania niejawnego klucza jest Type sam formant. Z punktu widzenia definiowania zasobów niejawny klucz jest TargetType stylem. W związku z tym, jeśli tworzysz motywy dla kontrolek niestandardowych lub tworzysz style, które wchodzą w interakcje z istniejącymi stylami motywu, nie musisz określać dyrektywy x:Key dla tego Styleelementu . A jeśli chcesz używać stylów motywowych, nie musisz w ogóle określać żadnego stylu. Na przykład poniższa definicja stylu działa, mimo że Style zasób nie ma klucza:

<Style TargetType="Button">
    <Setter Property="Background" Value="#4E1A3D" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="BorderThickness" Value="5" />
    <Setter Property="BorderBrush">
        <Setter.Value>
            <LinearGradientBrush>
                <GradientStop Offset="0.0" Color="#4E1A3D"/>
                <GradientStop Offset="1.0" Color="Salmon"/>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
</Style>

Ten styl naprawdę ma klucz: niejawny klucz: System.Windows.Controls.Button typ. W znacznikach można określić TargetType bezpośrednio nazwę typu (lub opcjonalnie możesz użyć elementu {x:Type...} , aby zwrócić element Type.

Za pomocą domyślnych mechanizmów stylu motywu używanych przez WPF ten styl jest stosowany jako styl Button środowiska uruchomieniowego strony, mimo że Button sam nie próbuje określić jego Style właściwości ani określonego odwołania do zasobu do stylu. Styl zdefiniowany na stronie znajduje się wcześniej w sekwencji odnośników niż styl słownika motywu, używając tego samego klucza, który ma styl słownika motywu. Możesz po prostu określić <Button>Hello</Button> dowolne miejsce na stronie, a styl zdefiniowany za TargetTypeButton pomocą polecenia będzie miał zastosowanie do tego przycisku. Jeśli chcesz, nadal możesz jawnie określić styl z taką samą wartością typu, jak TargetType w przypadku jasności w adiustacji, ale jest to opcjonalne.

Niejawne klucze stylów nie mają zastosowania do kontrolki, jeśli OverridesDefaultStyle jest to true. (Należy również pamiętać, że OverridesDefaultStyle można ustawić jako część zachowania natywnego dla klasy formantu, a nie jawnie w wystąpieniu kontrolki). Ponadto aby obsługiwać niejawne klucze dla scenariuszy klasy pochodnej, kontrolka musi przesłonić DefaultStyleKey (wszystkie istniejące kontrolki udostępniane w ramach WPF obejmują to zastąpienie). Aby uzyskać więcej informacji na temat stylów, motywów i projektowania kontrolek, zobacz Wytyczne dotyczące projektowania kontrolek stylowalnych.

DataTemplate ma również klucz niejawny. Niejawny DataType klucz elementu DataTemplate to wartość właściwości. DataType Można również określić jako nazwę typu, a nie jawnie przy użyciu { x:Type...}. Aby uzyskać szczegółowe informacje, zobacz Omówienie tworzenia szablonów danych.

Zobacz też