This article is currently in the process of being translated into Swedish (~99% done).
Resources
WPF introducerar ett mycket praktiskt koncept: Möjligheten att lagra data som en resurs, antingen lokalt för en kontroll, lokalt för hela fönstret eller globalt för hela applikationen. Datan kan vara i stort sett vad som helst, från faktisk information till en hierarki av WPF-kontroller. Detta gör det möjligt att placera data på ett ställe och sedan använda den från ett eller flera andra ställen. Detta är mycket användbart.
Detta koncept används ofta för styles och mallar, vilket vi diskuterar senare i denna guide. Du kan dock använda det även för andra saker. Låt mig demonstrera detta med ett enkelt exempel:
<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>

Resurser tilldelas en nyckel genom attributet x:Key, vilket gör att du kan hänvisa till den från andra delar av applikationen i kombination med markup-tillägget StaticResource. I det här exemplet lagrar jag bara en enkel sträng som jag stedan använder från två olika TextBlock-kontroller.
StaticResource vs DynamicResource
I de exempel jag har visat hittills har jag använd markup-tillägget StaticResource för att hänvisa till en resurs. Det finns dock ett alternativ i form av DynamicResource.
Den huvudsakliga skillnaden är att en statisk resurs endast hanteras en gång, nämligen då XAML laddas. Om resursen ändras efteråt kommer denna förändring inte att synas där du har använt StaticResource.
DynamicResource däremot hämtas när den verkligen behövs, och hämtas igen om resursen ändras. Föreställ det som att binda till ett statiskt värde jämfört med att binda till en funktion som övervakar detta värde och skickar till dig varje gång det ändras. Det fungerar inte exakt så men bör ge dig en bättre uppfattning om när du ska använda vad. DynamicResource gör det också möjligt att använda resurser som inte finns när du designar applikationen, exempelvis om du lägger till dem i underliggande koden medan du startar upp applikationen.
Fler typer av resurser
Att dela en enkel sträng är lätt, men du kan göra mycket mera. I nästa exempel kommer jag även lagra en array av strängar tillsammans med en gradientfärg för bakgrunden. Detta bör ge dig en ganska bra uppfattning om hur mycket du kan göra med resurser:
<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>

Den här gången har vi lagt till ett par extra resurser, så att vårt fönster nu innehåller en enkel sträng, en array av strängar och en linjär gradientfärg med LinearGradientBrush. Strängen används för etiketten, arrayen av strängar används som items för ComboBox-kontrollen och gradientfärgen används som bakgrund för hela fönstret. Som du kanske märker så kan i princip vad som helst lagras som en resurs.
Lokala och applikationsövergripande resurser
Hittills har vi lagrat resurser på en fönsternivå, vilket innebär att du kan komma åt dem från hela fönstret.
Om du bara behöver en viss resurs för en specifik kontroll kan du göra den mer lokal genom att lägga till den i just den kontrollen istället för i fönstret. Det fungerar på precis samma sätt, med skillnaden att du nu endast kan komma åt den från inuti kontrollens scope där du la den:
<StackPanel Margin="10">
<StackPanel.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
</StackPanel.Resources>
<Label Content="{StaticResource ComboBoxTitle}" />
</StackPanel>
I det här fallet lägger vi till resursen till StackPanel och använder den sedan från sin child-kontroll. Övriga kontroller inom StackPanel kan också använda den, precis som alla children av dessa child-kontroller skulle kunna komma åt den. Kontroller utanför denna StackPanel skulle dock inte ha tillgång till den.
Om du behöver komma åt resursen från flera fönster är detta också möjligt. Filen App.xaml kan innehålla resurser, precis som ett fönster eller någon annan typ av WPF-kontroll. När du lagrar dem i App.xaml blir de globalt tillgängliga i alla fönster och användarkontroller i projektet. Det fungerar på exakt samma sätt som när resurser lagras och används från ett fönster:
<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>
Att använda den är samma - WPF kommer automatiskt att gå upp genom scope, från den lokala kontrollen till fönstret och sedan till App.xaml för att hitta en given resurs:
<Label Content="{StaticResource ComboBoxTitle}" />
Resurser från underliggande kod
Hittills har vi kommit åt alla våra resurser från XAML genom att använda markup-tillägg. Du kan dock givetvis även komma åt dem från den underliggande koden vilket kan vara användbart i flera situationer. I det föregående exemplet såg vi hur vi kunde lagra resurser på flera olika ställen. I det här exemplet kommer vi att komma åt tre olika resurser från den underliggande koden, var och en lagrad i varsitt scope:
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>
Window:
<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>
Underliggande kod:
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());
}
}
}

Som du kan se så lagrar vi tre olika "Hello, world!"-meddelanden: ett i App.xaml, ett i fönstret och ett lokalt för huvudpanelen. Gränssnittet består av en knapp och en ListBox.
I den underliggande koden hanterar vi klickhändelsen för knappen, där vi lägger till varje textsträng till ListBox enligt bilden. Vi använder metoden FindResource() som returnerar resursen som ett objekt (om den hittas). Sedan omvandlar vi det till en sträng genom att använda ToString()-metoden.
Notera hur vi använder metoden FindResource() på olika scope - först på panelen, sedan på fönstret och sedan på det aktuella objektet Application. Det är logiskt att leta efter resursen där vi vet att den finns, men som tidigare nämnts fortsätter sökningen uppåt i hierarkin om inte resursen hittas. Så i princip hade vi kunnat använda FindResource() på panelen i alla tre fallen, eftersom den då hade fortsatt upp till fönstret och senare till applikationsnivån om den inte hade hittats.
Det omvända gäller dock inte - sökningen sker inte nedåt i trädet. Du kan alltså inte börja leta efter en resurs på applikationsnivå om den har definierats lokalt för kontrollen eller för fönstret.