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

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

Uwaga

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

Ważne

Dokumentacja przewodników klasycznych dla platform .NET 6 i .NET 5 (w tym .NET Core 3.1) jest w trakcie budowy.

Korzystanie z zasobów w XAML

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

<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 struktury (FrameworkElement lub FrameworkContentElement) ma właściwość Resources , która jest typem ResourceDictionary zawierającym zdefiniowane zasoby. Zasoby można definiować na dowolnym elemencie, takim jak Button. Jednak zasoby są najczęściej definiowane w elemencie głównym, który znajduje się w Window przykładzie.

Każdy zasób w słowniku zasobów musi mieć unikatowy klucz. Podczas definiowania zasobów w znacznikach przypisujesz unikatowy klucz za pośrednictwem dyrektywy x:Key. Zazwyczaj klucz jest ciągiem; Można jednak również ustawić go 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, szczególnie w przypadku stylów, zasobów składników i stylów danych.

Możesz 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 elemencie.

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

W poprzednim przykładzie, gdy modułu ładującego XAML {StaticResource MyBrush}BackgroundButtonprzetwarza wartość dla właściwości w , logika wyszukiwania zasobów najpierw sprawdza słownik zasobów dla Button elementu. Jeśli Button nie ma definicji MyBrush klucza zasobu (w tym przykładzie nie jest; jego kolekcja zasobów jest pusta), podczas wyszukiwania sprawdzany jest element nadrzędny Buttonelementu . Jeśli zasób nie jest zdefiniowany w obiekcie nadrzędnym, sprawdza drzewo logiczne obiektu w górę, aż zostanie znalezione.

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

Zasoby statyczne i dynamiczne

Zasób może być przywołyny jako statyczny lub dynamiczny. Odwołania są tworzone przy użyciu rozszerzenia znaczników StaticResource lub rozszerzenia DynamicResource Markup. Rozszerzenie znaczników to funkcja języka XAML, która umożliwia określenie odwołania do obiektu przez proces rozszerzenia znaczników ciągu atrybutu 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.

W przypadku korzystania z rozszerzenia znaczników zwykle należy podać co najmniej jeden parametr w postaci ciągu, który jest przetwarzany przez to konkretne rozszerzenie znaczników. Rozszerzenie znaczników StaticResource przetwarza klucz, szukając wartości tego klucza we wszystkich dostępnych słownikach zasobów. Przetwarzanie odbywa się podczas ładowania, czyli gdy proces ładowania musi przypisać wartość właściwości. Rozszerzenie znaczników DynamicResource przetwarza klucz, tworząc wyrażenie, a to wyrażenie pozostaje nieocenione do momentu działania aplikacji, po czym wyrażenie jest oceniane w celu podania wartości.

W przypadku odwołania do zasobu następujące kwestie mogą mieć wpływ na to, czy używasz odwołania do zasobu statycznego, czy dynamicznego:

  • Podczas określania ogólnego projektu sposobu tworzenia zasobów dla aplikacji (na stronie, 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 jest częścią wymagań aplikacji w czasie rzeczywistym?

  • Odpowiednie zachowanie wyszukiwania tego typu odwołania do zasobu.

  • 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 okolicznościach:

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

    Odwołania do zasobów statycznych nie są ponownie wyceny na podstawie zachowań środowiska uruchomieniowego, takich jak ponowne załadowanie strony. Z tego względu unikanie dużej liczby dynamicznych odwołań do zasobów, gdy nie jest to konieczne w zależności od projektu zasobu i aplikacji, może być korzystne ze względu na wydajność.

  • Ustawiasz wartość właściwości, która nie znajduje się w obiekcie ani DependencyObjectFreezable.

  • Tworzysz słownik zasobów, który jest kompilowany do biblioteki DLL współdzielonych między aplikacjami.

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

    W tym przypadku zwykle nie chcesz zachowania wyszukiwania odwołania do zasobu dynamicznego. Zamiast tego użyj zachowania odwołania do zasobu statycznego, aby odnośnik był przewidywalny i samodzielny dla motywu. W przypadku dynamicznego odwołania do zasobów nawet odwołanie w motywie pozostaje nieocenione do czasu uruchomienia. Istnieje również możliwość, że po zastosowaniu motywu jakiś element lokalny ponownie zdefiniuje klucz, do którym próbuje się odwołać motyw, a element lokalny upłynie przed samym motywem w odnośniku. W takim przypadku motyw nie będzie działać zgodnie z oczekiwaniami.

  • Używasz zasobów do ustawienia 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 po podaniem wartości właściwości zależności, która może być oceniana w czasie ładowania, właściwość zależności nie musi sprawdzać ponownie ocenianego wyrażenia i może zwrócić ostatnią wartość efektywną. Ta technika może być zaletą wydajności.

  • Chcesz zmienić podstawowy zasób dla wszystkich użytkowników lub zachować oddzielne wystąpienia z zapisywalnymi wystąpieniami 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 właściwość lub element odwołuje się do zasobu statycznego:

  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 jest kontynuowany, dopóki element główny nie zostanie osiągnięty.

  3. Zasoby aplikacji są sprawdzane. Zasoby aplikacji to zasoby w słowniku zasobów, który jest definiowany przez Application obiekt aplikacji WPF.

Odwołania do zasobów statycznych w słowniku zasobów muszą odwoływać się do zasobu, który został już zdefiniowany leksykalnie przed odwołaniem do zasobu. Odwołania do przodu nie mogą być rozpoznawane przez statyczne odwołanie do zasobów. Z tego powodu zaprojektuj strukturę słownika zasobów tak, aby zasoby były definiowane na początku lub na początku każdego odpowiedniego słownika zasobów.

Statyczne wyszukiwania zasobów można rozszerzyć na motywy lub zasoby systemowe, ale to wyszukiwania jest obsługiwane tylko dlatego, że modułu ładującego XAML odracza żądania. Odroczenie jest niezbędne, aby motyw środowiska uruchomieniowego podczas ładowania strony był prawidłowo stosowana do aplikacji. Jednak statyczne odwołania do zasobów do kluczy, o których wiadomo, że istnieją tylko w motywach lub zasoby systemowe, nie są zalecane, ponieważ takie odwołania nie są ponownie oswalane, jeśli motyw zostanie zmieniony przez użytkownika w czasie rzeczywistym. Dynamiczne odwołanie do zasobów jest bardziej niezawodne w przypadku żądania motywu lub zasobów systemowych. Wyjątek występuje, gdy element motywu żąda innego zasobu. Te odwołania powinny być odwołaniami do zasobów statycznych z powodów wymienionych wcześniej.

Zachowanie wyjątku w przypadku, gdy nie znaleziono odwołania do zasobu statycznego, 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 inny sposób można ustawić przez użytkownika, zależy od warunków, które nie są znane do czasu uruchomienia. Na przykład można utworzyć wartości ustawiające, które odwołują się do właściwości systemu jako udostępniane 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 również musi przechwycić zmianę.

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

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

  • Masz złożoną strukturę zasobów, która ma współzależności, w której może być wymagane odwołanie do przodu. Odwołania do zasobów statycznych nie obsługują odwołań do przodu, ale odwołania do zasobów dynamicznych obsługują je, ponieważ zasób nie musi być oceniany do czasu uruchomienia, a w związku z tym odwołania do przodu nie są odpowiednim pojęciem.

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

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

  • Stosujesz zasoby do elementów, które mogą być ponownie przejmowane w drzewie logicznym podczas okresu istnienia aplikacji. Zmiana elementu nadrzędnego może również potencjalnie zmienić zakres wyszukiwania zasobów, więc jeśli chcesz, aby zasób elementu ponownie nadrzędnego był ponownie wartościowany na podstawie nowego zakresu, zawsze używaj dynamicznego odwołania do zasobu.

Dynamiczne zachowanie wyszukiwania zasobów

Zachowanie wyszukiwania zasobów w dynamicznym odwołaniu do zasobów jest równoległe do zachowania wyszukiwania w kodzie w przypadku wywołania metody FindResource lub SetResourceReference:

  1. Odnośnik 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 jest kontynuowany, dopóki element główny nie zostanie osiągnięty.

  3. Zasoby aplikacji są sprawdzane. Zasoby aplikacji to zasoby w słowniku zasobów, które są definiowane przez Application obiekt 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 przesądzona.

  5. Zasoby systemowe są sprawdzane.

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

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

  • Jeśli zasób został zażądany przez wywołanie TryFindResource i nie został znaleziony, wyjątek nie jest zgłaszany, a zwracana wartość to null. Jeśli ustawiana właściwość nie nullakceptuje wartości , nadal jest możliwe, że zostanie zgłoszony głębszy wyjątek w zależności od ustawionej indywidualnej właściwości.

  • Jeśli zasób był żądany przez dynamiczne odwołanie do zasobu 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 żadna operacja ustawienia właściwości nie miała miejsca na poziomie, na którym istnieje zasób. Jeśli na przykład próbujesz ustawić tło dla pojedynczego elementu przycisku przy użyciu zasobu, który nie może zostać oceniony, to żadne wyniki zestawu wartości, ale wartość efektywna 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ć z lokalnie zdefiniowanego stylu przycisku lub ze stylu motywu. W przypadku właściwości, które nie są zdefiniowane przez style motywu, wartość efektywna po nieudanej ocenie zasobów może pochodzić z wartości domyślnej w metadanych właściwości.

Ograniczenia

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

Ponieważ ustawiana właściwość musi DependencyProperty być właściwością 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 zawiera logikę, która wymusza inny układ DependencyProperty kontrolki w przypadku zmiany i może mieć wpływ na układ tej właściwości. Jednak nie wszystkie właściwości, które mają rozszerzenie DynamicResource Markup, 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 typu, który jest właścicielem właściwości, a nawet struktury logicznej aplikacji.

Style, dataTemplates i klucze niejawne

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

Jednym z ważnych scenariuszy dla zasobów jest zdefiniowanie .Style W rzeczywistości element jest Style prawie zawsze definiowany 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 kontrolek można tworzyć za pomocą i przywoływać za pomocą klucza niejawnego. Style motywu definiujące domyślny wygląd kontrolki są zależne od tego niejawnego klucza. Z punktu widzenia żądania, niejawny klucz jest kluczem Type samej kontrolki. Z punktu widzenia definiowania zasobów klucz niejawny jest elementem TargetType stylu. W związku z tym, jeśli tworzysz motywy dla kontrolek niestandardowych lub tworzysz style, które współdziałają z istniejącymi stylami motywu, nie musisz określać dyrektywy x:Key dla tego .Style A jeśli chcesz użyć stylów z tematami, nie musisz w ogóle określać żadnego stylu. Na przykład następująca definicja stylu działa, mimo Style że zasób nie wydaje się mieć 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ć bezpośrednio TargetType jako nazwę typu (lub opcjonalnie użyć elementu {x:Type...} w celu zwrócenia elementu Type.

Za pomocą domyślnych mechanizmów stylu motywu używanych przez WPF Button , ten styl jest stosowany jako styl środowiska uruchomieniowego obiektu na stronie, ButtonStyle nawet jeśli sam nie próbuje określić jego właściwości lub konkretnego odwołania do zasobu do stylu. Styl zdefiniowany na stronie znajduje się wcześniej w sekwencji wyszukiwania niż styl słownika motywu przy użyciu tego samego klucza, który ma styl słownika motywu. Wystarczy określić dowolne miejsce <Button>Hello</Button> na stronie, TargetTypeButton a styl zdefiniowany za pomocą opcji będzie miał zastosowanie do tego przycisku. Jeśli chcesz, nadal możesz jawnie utworzyć klucz stylu z taką samą TargetType wartością typu, jak dla przejrzystości w znacznikach, ale jest to opcjonalne.

Klucze niejawne stylów nie mają zastosowania do kontrolki, jeśli wartość to OverridesDefaultStyletrue. (Należy również zauważyć, OverridesDefaultStyle że element może być ustawiany jako część natywnego zachowania klasy kontroli, a nie jawnie w wystąpieniu kontrolki). Ponadto, aby zapewnić obsługę niejawnych kluczy dla scenariuszy klas pochodnych, DefaultStyleKey kontrolka musi zostać przesłonięcie (wszystkie istniejące kontrolki dostarczane jako część WPF obejmują to przesłonięcie). Aby uzyskać więcej informacji na temat stylów, motywów i projektowania kontrolek, zobacz Wytyczne dotyczące projektowania kontrolek ze stylami.

DataTemplate Ma również klucz niejawny. Niejawny klucz dla jest DataTemplate wartością DataType 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 Data Templating Overview (Omówienie szablonów danych).

Zobacz też