This article is currently in the process of being translated into Catalan (~67% done).
Resources
WPF introdueix un concepte molt útil: La capacitat d'emmagatzemar dades com si fossin un recurs, ja sigui localment per un control, localment per tota una finestra o globalment per tota l'aplicació. Les dades poden ser pràcticament el que es vulgui, desde informació real fins a un jerarquia de controls WPF. Això permet posar dades en un lloc i fer-les servir des de qualsevol altre, el que esdevé realment útil.
Aquest concepte es fa servir força pels estils i les plantilles, que es discutirà més endavant en aquest tutorial però, com s'il·lustrarà en aquest capítol, es pot fer servir per altres propòsits també. Deixa'm demostrar-ho amb un exemple:
<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>
Als recursos se'ls assigna una clau, fent servir l'atribut x:Key, que permet referenciar-los des d'altres parts de l'aplicació fent servir aquesta clau, en combinació amb la marca d'extensió StaticResource. En aquest exemple només s'emmagatzema una cadena alfanumérica, que es fa servir des de diferents controls TextBox.
StaticResource vs. DynamicResource
Fins ara, en els exemples, he fet servir la marca d'extensió StaticResource per referenciar un recurs. No obstan això, existeix una alternativa, en forma de DynamicResource.
La diferencia més gran és que un recurs estàtic es resol només una vegada, en el moment en que el XAML es carrega. Si el recurs es canvia més tard, aquest canvi no es veurà reflectit allà on tu has utilitzat el recurs estàtic.
D'altra banda, un DynamicResource es resol quan realment es necessita, i de nou si el recurs canvia. Penseu-ho com a una vinculació a un valor estàtic versus una vinculació a una funció que controla aquest valor i us l'envia cada vegada que ha canviat - no és exactament com funciona, però us hauria de donar una millor idea de quan utilitzar què. Els recursos dinàmics també us permeten utilitzar recursos que ni tan sols hi són durant el temps de disseny, per exemple, si els afegiu des del codi durant l'inici de l'aplicació.
Més tipus de recursos
Compartir una cadena senzilla era fàcil, però pots fer molt més. En el següent exemple, també emmagatzemaré una matriu completa de cadenes, juntament amb un pinzell de degradat que s'utilitzarà per al fons. Això us hauria de donar una idea bastant bona de què podeu fer amb els recursos:
<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>
Aquest cop, hem afegit uns quants recursos extres, així doncs, ara la nostra finestra conté un cadena senzilla, una matriu de cadenes i un LinearGradientBrush. La cadena s'utilitza per a l'etiqueta, la matriu de cadenes s'utilitza com "items" pel control ComboBox i el LinearGradientBrush s'utilitza com a fons de tota la finestra. Així doncs, com pots veure, qualsevol cosa es pot emmagatzemar com un recurs.
Recursos locals i recursos de tota l'aplicació
Fins ara, hem guardat els recursos a nivell de finestra, cosa que vol dir que hi pots accedir des de qualsevol lloc de la finestra.
Si només necessiteu un recurs donat per a un control específic, podeu fer-lo més local afegint-lo a aquest control específic, en lloc de la finestra. Funciona exactament de la mateixa manera, l'única diferència és que ara només es pot accedir des de l'interior de l'àmbit del control on es posa:
<StackPanel Margin="10">
<StackPanel.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
</StackPanel.Resources>
<Label Content="{StaticResource ComboBoxTitle}" />
</StackPanel>En aquest cas, afegim el recurs a un StackPanel i l'utilitzem en el seu control fill, una etiqueta. Altres controls dins del StackPanel també el poden utilitzar, ja que son controls fills que hi poden tindre accés. Control fora d'aquest StackPanel, no hi podran tenir accés.
Si tens la necessitat d'accedir al recurs des de diferents finestres, això també és possible. El fitxer App.xaml pot contenir recursos per a la finestra i per a qualsevol control WPF, i quan tu el guardes a App.xaml son accessibles globalment a totes les finestres i tots el controls d'usuari del projecte. funciona exactament de la mateixa manera que quan ho guardem i utilitzem des de una finestra.
<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>Utilitzar-l'ho també és el mateix. WPF automàticament anirà augmentat l'abast, des de els controls locals, als de finestra fins als de l'App.xaml buscant el recurs.
<Label Content="{StaticResource ComboBoxTitle}" />Resources from Code-behind
So far, we've accessed all of our resources directly from XAML, using a markup extension. However, you can of course access your resources from Code-behind as well, which can be useful in several situations. In the previous example, we saw how we could store resources in several different places, so in this example, we'll be accessing three different resources from Code-behind, each stored in a different 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>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());
}
}
}
So, as you can see, we store three different "Hello, world!" messages: One in App.xaml, one inside the window, and one locally for the main panel. The interface consists of a button and a ListBox.
In Code-behind, we handle the click event of the button, in which we add each of the text strings to the ListBox, as seen on the screenshot. We use the FindResource() method, which will return the resource as an object (if found), and then we turn it into the string that we know it is by using the ToString() method.
Notice how we use the FindResource() method on different scopes - first on the panel, then on the window and then on the current Application object. It makes sense to look for the resource where we know it is, but as already mentioned, if a resource is not found, the search progresses up the hierarchy, so in principal, we could have used the FindResource() method on the panel in all three cases, since it would have continued up to the window and later on up to the application level, if not found.
The same is not true the other way around - the search doesn't navigate down the tree, so you can't start looking for a resource on the application level, if it has been defined locally for the control or for the window.