This article has been localized into Czech by the community.
Zdroje
WPF představuje velmi užitečný koncept: data je možné uchovávat lokálně pro kontrolní prvek či celé okno nebo globálně pro celou aplikaci. Na tato místa je možné uložit de facto cokoliv chcete, od jednoduché informace až po hierarichii WPF ovládacích prvků. Tento přístup umožňuje umístit všechna data na jedno místo a následně se na ně odkazovat z vícero jiných částí, což je velmi užitečné.
Tento přístup je velmi často využíván pro styly a šablony, o nichž si povíme více později v tomto tutoriálu, avšak dá se použít i pro mnoho jiných věcí, jak v této kapitole ukážeme. Pojďme si to předvést na jednoduchém příkladu:
<Window x:Class="WpfTutorialSamples.WPF_Application.ResourceSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="ResourceSample" Height="150" Width="350">
<Window.Resources>
<sys:String x:Key="strHelloWorld">Hello, world!</sys:String>
</Window.Resources>
<StackPanel Margin="10">
<TextBlock Text="{StaticResource strHelloWorld}" FontSize="56" />
<TextBlock>Just another "<TextBlock Text="{StaticResource strHelloWorld}" />" example, but with resources!</TextBlock>
</StackPanel>
</Window>
Každému zdroji je přiřazen klíč pomocí x:Key atributu, který umožňuje odkázat se na tento zdroj z jiných částí aplikace pomocí tohoto klíče a rozšíření značení StaticResource. V tomto příkladu jsme uložili jednoduchý řetězec, který je následně použitý ve dvou různých TextBlock kontrolních prvcích.
Statické vs. dynamické zdroje
Zatím jsem se ve všech příkladech odkazoval na statický zdroj s označením StaticResource. Existuje ovšem i alternativa - DynamicResource.
Základním rozdílem je, že statický zdroj se vyhodnotí pouze jednou a to v momentě, kdy se načítá XAML. Pokud by se hodnota tohoto zdroje později změnila, tato změna nebude zohledněna na místech, kde je použita StaticResource.
Na rozdíl od StaticResource, DynamicResource se vyhodnotí teprve až je potřeba a následně pokaždé, kdy se hodnota zdroje změní. Představte si to jako přiřazení do statické proměnné vs. přiřazení funkce k dané proměnné, která monitoruje její hodnotu a vrátí ji pokaždé, když se tato hodnota změní - není to úplně přesný způsob, jak tyto zdroje fungují, ale mělo by vám to alespoň základní náhled na to, kdy použít kterou. Dynamické zdroje také umožňují používání zdrojů, které nejsou dostupné v době návrhu. Příkladem může být přidávání zdrojů z Code-behind během startu aplikace.
Další typy zdrojů
Sdílení jednoduchého řetězce bylo snadné, ale dá se zvládnout mnohem více. V následujícím příkladu uložíme kompletní pole řetězců společně s přechodem štětce, který bude použit pro pozadí. Toto by vám mělo ukázat, čeho všeho lze docílit se zdroji:
<Window x:Class="WpfTutorialSamples.WPF_Application.ExtendedResourceSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="ExtendedResourceSample" Height="160" Width="300"
Background="{DynamicResource WindowBackgroundBrush}">
<Window.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
<x:Array x:Key="ComboBoxItems" Type="sys:String">
<sys:String>Item #1</sys:String>
<sys:String>Item #2</sys:String>
<sys:String>Item #3</sys:String>
</x:Array>
<LinearGradientBrush x:Key="WindowBackgroundBrush">
<GradientStop Offset="0" Color="Silver"/>
<GradientStop Offset="1" Color="Gray"/>
</LinearGradientBrush>
</Window.Resources>
<StackPanel Margin="10">
<Label Content="{StaticResource ComboBoxTitle}" />
<ComboBox ItemsSource="{StaticResource ComboBoxItems}" />
</StackPanel>
</Window>
Tentokrát jsme přidali několik zdrojů navíc, takže naše okno nyní obsahuje jednoduchý řetězec, pole řetězců a LinearGradientBrush. Zmíněný řetězec je použit jako štítek, řetězce z pole jsou položky kontrolního prvku ComboBox a přechod štětce je použit jako pozadí pro celé okno. Jak je vidět, de facto cokoliv se dá uložit jako zdroj.
Lokální zdroje a zdroje pro celou aplikaci
Doteď jsme ukládali zdroje na úrovni oken, což mělo za důsledek, že bylo možné k nim přistupovat odkudkoliv z daného okna.
Pokud potřebujete určitý zdroj pouze pro specifický kontrolní prvek, můžete snížit dosah daného zdroje jeho přesunutím z okna do tohoto kontrolního prvku. Vše bude fungovat naprosto stejně jako předtím až na to, že nyní se dá daný zdroj využít pouze z rozsahu kontrolního prvku, v němž se nachází:
<StackPanel Margin="10">
<StackPanel.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
</StackPanel.Resources>
<Label Content="{StaticResource ComboBoxTitle}" />
</StackPanel>
V tomto případě přidáme zdroj do StackPanel a následně ho použijeme z jeho potomka, kontrolního prvku Label. Zbylé kontrolní prvky uvnitř StackPanel by ho mohly také použít, stejně tak jako potomci těchto potomků. Kontrolní prvky mimo tento konkrétní StackPanel by k němu však přístup neměly.
Může se stát, že budete potřebovat využít zdroj napříč několika okny. I to je možné. Soubor App.xaml obsahuje zdroje stejně jako jakékoliv okno nebo WPF kontrolní prvek, avšak pokud do něj uložíte zdroj, bude globálně přístupný ve všech oknech i uživatelských kontrolních prvcích v celém projektu. Funguje to naprosto stejně jako když zdroj ukládáte a používáte z okna:
<Application x:Class="WpfTutorialSamples.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
StartupUri="WPF application/ExtendedResourceSample.xaml">
<Application.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
</Application.Resources>
</Application>
Používání je také stejné - WPF automaticky vyhledává daný zdroj počínaje lokálním rozsahem kontrolního prvku přes rozsah okna až po globální rozsah App.xaml:
<Label Content="{StaticResource ComboBoxTitle}" />
Zdroje z Code-behind
Doposud jsme pouze přistupovali ke všem našim zdrojům přímo z XAML pomocí rozšíření značení. Samozřejmě je také možné využít zdroje i z Code-behind, což se může v mnoha situacích hodit. V předchozím příkladu jsme viděli, jak ukládat zdroje na několika různých místech. V tomto příkladu budeme přistupovat ke třem různým zdrojům z Code-behind, z nichž každý je uložený v jiném rozsahu:
App.xaml:
<Application x:Class="WpfTutorialSamples.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
StartupUri="WPF application/ResourcesFromCodeBehindSample.xaml">
<Application.Resources>
<sys:String x:Key="strApp">Hello, Application world!</sys:String>
</Application.Resources>
</Application>
Okno:
<Window x:Class="WpfTutorialSamples.WPF_Application.ResourcesFromCodeBehindSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="ResourcesFromCodeBehindSample" Height="175" Width="250">
<Window.Resources>
<sys:String x:Key="strWindow">Hello, Window world!</sys:String>
</Window.Resources>
<DockPanel Margin="10" Name="pnlMain">
<DockPanel.Resources>
<sys:String x:Key="strPanel">Hello, Panel world!</sys:String>
</DockPanel.Resources>
<WrapPanel DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10">
<Button Name="btnClickMe" Click="btnClickMe_Click">Click me!</Button>
</WrapPanel>
<ListBox Name="lbResult" />
</DockPanel>
</Window>
Code-behind:
using System;
using System.Windows;
namespace WpfTutorialSamples.WPF_Application
{
public partial class ResourcesFromCodeBehindSample : Window
{
public ResourcesFromCodeBehindSample()
{
InitializeComponent();
}
private void btnClickMe_Click(object sender, RoutedEventArgs e)
{
lbResult.Items.Add(pnlMain.FindResource("strPanel").ToString());
lbResult.Items.Add(this.FindResource("strWindow").ToString());
lbResult.Items.Add(Application.Current.FindResource("strApp").ToString());
}
}
}
Jak je vidět, uložili jsme si tři různé "Hello, world!" zprávy: Jednu v App.xaml, jednu uvnitř okna a jednu lokálně pro hlavní panel. Rozhraní se skládá z tlačítka a ListBox.
V Code-behind zpracováváme událost kliknutí na tlačítko tím, že přidáme každý z textových řetězců do ListBox, jak je vidět na obrázku obrazovky. Používáme metodu FindResource() , která vrací zdroj jako objekt (pokud je nalezen) a následně ho mění na řetězec, což víme, neboť používá metodu ToString().
Všimněte si, že používáme metodu FindResource() v různých rozsazích - nejdříve uvnitř panelu, následně uvnitř okna a nakonec na současném objektu Application. Dává smysl hledat zdroj tam, kde víme, že je, ale jak již bylo zmíněno, pokud zdroj není nalezen, pokračuje vyhledávání směrem vzhůru po hierarchii. Principiálně jsme tudíž mohli použít metodu FindResource() na panelu ve všech třech případech, protože by vyhledávání pokračovalo nejprve v rozsahu okna a následně aplikace, pokud by ani v rozsahu okna nebyl hledaný zdroj nalezen.
Naopak to ovšem neplatí - vyhledávání nebude pokračovat směrem dolů v hierarchii, takže není možné začít vyhledávání zdroje na úrovni aplikace, pokud byl definován lokálně v kontrolním prvku či okně.