Při vytváření aplikace se můžeme dostat do situace, že budeme chtít použít např. ikonky pro naše tlačítka, abychom vylepšili vzhled naší aplikace. V aplikaci můžeme uvést absolutní cestu, což s sebou přináší řadu nevýhod. Spustíme-li aplikaci na jiném PC tak s největší pravděpodobností uživatel tyto ikonky nemá a aplikace může vyhodit výjimku, že nenalezla potřebnou ikonu. Proto je možné obrázek zakomponovat do naší assembly a tím se vyhneme předchozí chybě. Pro uložení obrázku v naší assembly musíme ve vlastnostech nastavit BuildAction na Resource nebo Content. Pozor na EmbeddedResource, který NENÍ ze XAMLu jednoduše dostupné (museli bychom napsat část vlastního kódu)
Přístup k obrázku je v XAMLu jednoduchý a podle Uri jsme schopni říct, kde se přesně náš obrázek nachází (internet, síť, disk, jiná assembly)
<Image Source="Images/img.jpg" />
Někdy se ale můžeme setkat se zápisem, který vypadá následovně:
<Image Source="pack://application:,,,/Images/img.jpg" /> <Image Source="pack://application:,,,/Assembly;component/Images/img.jpg" /> <Image Source="pack://siteOfOrigin:,,,/Images/img.jpg" />
Při použití application, se bude pomocí URI v naší aplikaci hledat obrázek, který má nastaven build action na Resource nebo Content. SiteofOrigin zase říká, že se bude hledat v umístění, odkud aplikace pochází. Při použití Windows Installeru to bude root složka aplikace, při nasazení pomocí ClickOnce to bude pro změnu umístění odkud byla aplikace nainstalována.
Poznámka: Obrázky se při startu aplikace načítají synchronně, takže v případě, že načítám obrázek z umístění, které není momentálně dostupné, dojde k výjimce.
Kromě binárních resources existují také i logické resources. Každý element ve WPF aplikaci má kolekci resources, do které si můžeme uložit nějaký předpřipravený element, který pak budeme chtít v naší aplikaci využít. Např. můžeme si vytvořit nějaké pěkné tlačítko, uložit do resourcech objektu Windows, či celé aplikace a pak toto tlačítko můžeme vesele využívat, budeme se pouze odkazovat na naší definici v resources. Pokud takto definujeme nějaké tlačítko či jakýkoliv jiný element, nesmíme zapomenout nastavit atribut x:key na název, pomocí kterého se pak budeme na to naše tlačítko odvolávat. Jak lze použít resources, můžete vidět na následující ukázce:
<Window.Resources>
<Image x:Key="MyImage" Source="pack://application:,,,/Images/img.jpg"/>
</Window.Resources>
<StackPanel>
<StaticResource ResourceKey="MyImage"/>
</StackPanel>
Do Window.Resources si uložíme obrázek a pak v dále v XAMLu použijeme element StaticResource a přes vlastnost ResourceKey se odkážeme na náš obrázek, který jsme si uložili do resources. Pokud použijeme tento element ještě jednou, tak se mi při kompilaci objeví chyba:
Specified Visual is already a child of another Visual or the root of a CompositionTarget
To je sice správně, ale k čemu by nám takový objekt byl, když by jsme ho mohli použít pouze jednou? Řešení je velice jednoduché, stačí přidat k obrázku atribut x:Shared a nastavit na false a pak můžeme tento obrázek použít na několika místech najednou
Poznámka: Intellisense ve VS nějak nezná atribut x:Shared takže se nedivte, pokud Vám ho nenabídne k použití
<Window.Resources>
<Button Content="Click me!" x:Key="MyButton" x:Shared="False" Height="50" Width="70"/>
</Window.Resources>
<StackPanel>
<StaticResource ResourceKey="MyButton"/>
<StaticResource ResourceKey="MyButton"/>
</StackPanel>
Poznámka: Pokud přistupujeme k nějakému objektu, který je definován jako StaticResource, musí být definice tohoto objektu výše než jeho použití. Tzn. Že nemůžu např. v resources vytvořit tlačítko, které využívá jiný resources a ten definuju až za tímto tlačítkem. Nejdříve bych musel uvést resources, který využívá tlačítko a až poté to samotné tlačítko, které ten daný resources použije.
Určitě jste si všimli, že zde používáme termín StaticResource, ale abych Vás nezklamal, tak ve WPF existují i tzv. DynamicResources a teď abych Vás zmátl úplně tak to co jsem psal v poznámce o StaticResource u DynamicResource neplatí. Další podstatný rozdíli mezi Static a Dynamic resource je, že static resource se načte pouze jednou a to tehda až je potřeba, ale dynamic resource se mění dynamicky. Např. pokud nastavím background objektu Window jako dynamic resource na nějakou barvu kterou uvedu v resources, tak po její změně se změní i barva pozadí celého okna, viz. Následující ukázka:
<Window x:Class="Demo._11.DynamicResourcesXAML"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="300" Height="300" Background="{DynamicResource myColor}">
<Window.Resources>
<SolidColorBrush x:Key="myColor" Color="Blue"/>
</Window.Resources>
<StackPanel>
<Button Content="Zmen barvu" Click="Button_Click" />
</StackPanel>
</Window>
A po stisku tlačítka si získám objekt myColor a nastavím mu jinou barvu. V tu chvíle dojde k tomu, že DynamicResource zjistí, že došlo ke změně hodnoty a provede příslušnou akci, v mém případě změní pozadí okna.
private void Button_Click(object sender, RoutedEventArgs e)
{
// najdu si resources myColor
var brush = this.FindResource("myColor") as SolidColorBrush;
brush.Color = Colors.White;
}
Na závěr si vytvoříme ukázkovou aplikaci, kde dáme všechno dohromady.
<Window.Resources>
<Image x:Key="Koala" Source="pack://application:,,,/Images;component/Images/Koala.jpg"
x:Shared="false" Width="100" Height="100"/>
<SolidColorBrush x:Key="MyBackColor" Color="Blue"/>
</Window.Resources>
<StackPanel>
<Button Content="Load" Click="Button_Click"/>
<WrapPanel x:Name="LayoutRoot">
<StaticResource ResourceKey="Koala"/>
<StaticResource ResourceKey="Koala"/>
</WrapPanel>
</StackPanel>
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Background="{DynamicResource MyBackColor}">
<Window.Resources>
<Image x:Key="Koala" Source="pack://application:,,,/Images;component/Images/Koala.jpg"
x:Shared="false" Width="100" Height="100"/>
<SolidColorBrush x:Key="MyBackColor" Color="Blue"/>
</Window.Resources>
<StackPanel>
<Button Content="Load" Click="Button_Click"/>
<Button Content="Change Background" Click="Button_Click_1"/>
<WrapPanel x:Name="LayoutRoot">
<StaticResource ResourceKey="Koala"/>
<StaticResource ResourceKey="Koala"/>
</WrapPanel>
</StackPanel>
</Window>
Když spustíme aplikaci tak můžeme pomocí tlačítka Load načítat další a další obrázky koaly a pomocí tlačítka Change Background si najdeme náš objekt Dynamic resource, kterému změníme barvu na bílou. Ihned po změně barvy dojde k aktualizaci pozadí okna.
Doufám, že po této krátké ukázce už máte trošku více o jasno o používání resources
Působí jako Microsoft Student Partner na VŠB-TU Ostrava a zároveň pracuje jako software developer.
Mezi jeho portfolio patří platforma Microsoft .NET především WPF, WCF, Silverlight, ASP.NET MVC, ale i oblast týmového vývoje a Design Patterns.
Rovněž publikuje na MSTV.cz a je držitelem ocenění Microsoft Most Valuable Professional (MVP)
Máte-li zájem o školení, konzultaci či zakázkový vývoj pak mě neváhejte kontaktovat!
email: me(at)lukaskubis.net
web: http://web.lukaskubis.net
blog: http://lukaskubis.net