WPF seriál 20. díl – Resources

Binární Resources

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.

Logické Resources

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.

  • Vytvoříme Class Library ve které budeme mít definovaný obrázek "Koala.jpg"
  • V hlavní aplikaci se na obrázek Koala.jpg odkážeme ve Windows.Resources
<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>
  • Dále do StackPanelu přidáme WrapPanel, který bude obsahovat 2 obrázky a jedno tlačítko, kterým budeme nahrávat další obrázek 
<StackPanel>
        <Button Content="Load" Click="Button_Click"/>
        <WrapPanel x:Name="LayoutRoot">
            <StaticResource ResourceKey="Koala"/>
            <StaticResource ResourceKey="Koala"/>
        </WrapPanel>
    </StackPanel>
  • A úplně nakonec nastavíme Background okna na DynamicResource + přidáme tlačítko, kterým změníme barvu pozadí. Výsledný XAML vypadá následovně:
<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.

Závěr

Doufám, že po této krátké ukázce už máte trošku více o jasno o používání resources

Materiály ke stažení

Demo

 
Ohodnoťte článek: starstarstarstarstar

Komentáře

Přidat komentář

jméno

text komentáře

opište text z obrázku


O autorovi

Lukáš Kubis

Lukáš Kubis

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!

web: http://web.lukaskubis.net

blog: http://lukaskubis.net

©2010 | David Beinhauer | Lukáš Kubis