W WPF można wyróżnić dwa rodzaje zasobów:
• Assembly resources (binary resources)
◦ pliki z danymi (binarne), wbudowane w skompilowany podzespół aplikacji (np.
obrazki)
◦ działają prawie identycznie jak assembly resources w innych aplikacjach .NET, jedyną różnicą jest system adresujący, wykorzystywany przy odwoływaniu się od zasobów
◦ zasobem typu assembly w aplikacjach WPF jest np. plik BAML (skompilowany plik XAML)
• Object resources
◦ mogą być nimi dowolne obiekty, zdefiniowany przeważnie w XAMLu, umożliwiają przechowywanie różnego rodzaju informacji w centralnym miejscu, zapobiegając powtarzaniu kodu
Dodawanie zasobów typu assembly
• Należy dodać odpowiednie pliki do projektu i ustawić i właściwość Build Action na Resource (w celu lepszej organizacji, pliki mogą być grupowane w foldery).
Odwoływanie się do zasobów typu assembly
• Obiekt StreamResourceInfo daje dostęp m. in. do typu oraz strumienia danych zasobu:
StreamResourceInfo sri = Application.GetResourceStream(
new Uri("images/info.png", UriKind.Relative));
◦ ContentType zwraca string opisujący typ danych
◦ Stream – strumień (typu UnmanagedMemoryStream), z którego można odczytać bajty danych
• Niektóre klasy mają wbudowaną obsługę zasobów i potrafią z nimi współpracować (odnajdują je przez adres URI):
<Image Source="images/info.png" />
• lub:
image1.Source = new BitmapImage(
new Uri("images/info.png", UriKind.Relative));
Odwoływanie się do zasobów typu assembly
• Przez adres URI w przypadku zasobów zawartych w innym podzespole (bibliotece dll, z której korzysta nasza aplikacja):
img.Source = new BitmapImage(
new Uri("ImageLibrary;component/images/winter.jpg", UriKind.Relative));
Dodawanie zasobów z Build Action na Content (i Copy to Output Directory)
• Warto używać, gdy:
◦ chcemy zmieniać zasób bez ponownej kompilacji
◦ zasób jest bardzo duży
◦ plik jest opcjonalny i chcemy dostarczać aplikację również bez niego
◦ jest to plik dźwiękowy
• Content jest wygodniejsze, niż po prostu zwykłe dostarczenie plików z aplikacją i odczytywanie ich z dysku.
Object Resources
• Nowy system zasobów zintegrowany z XAML
• Możliwość definiowania zasobów w różnych miejscach (kontrolki, okna, cała aplikacja)
• Zasoby obiektowe (deklaratywne, logiczne):
◦ pozwalają na zdefiniowanie obiektu raz i używanie go w wielu miejscach kodu
◦ umożliwiają przeniesienie np. szczegółów formatujących kontrolek do centralnego miejsca, w którym mogą być w łatwy sposób zmieniane
◦ gdy pewna informacja jest oddzielona od reszty aplikacja, może być modyfikowana dynamicznie
Przykład wykorzystania Object resources definiowanie zasobu:
<Window.Resources>
<SolidColorBrush x:Key="zielony" Color="Green" />
</Window.Resources>
korzystanie z zasobu:
<Button Background="{StaticResource zielony}">
Statycznie
</Button>
<Button Background="{DynamicResource zielony}">
Dynamicznie
</Button>
Kolekcja zasobów
• Każdy element posiada właściwość Resources, która przechowuje kolekcję zasobów (ResourceDictionary)
• Kolekcja zasobów może przechowywać dowolne typy obiektów
• Przeważnie zasoby definiowane są na poziomie okna, gdyż wszystkie dzieci mają dostęp do zasobów ojca
Zasoby statyczne i dynamiczne
• Zasoby statyczne, w przeciwieństwie do dynamicznych, są pobierane raz (po deklaracji w kodzie XAML) i nie reagują na zmianę zasobu z poziomu kodu
this.Resources["zielony"] =
new SolidColorBrush(Colors.Yellow);
• Zasoby dynamiczne pobierane są za każdym razem, gdy są potrzebne. Ponieważ wiąże się to z dodatkowym narzutem, z zasobów dynamicznych należy korzystać tylko wtedy, gdy:
◦ zasób zależy od ustawień systemowych (np. kolory systemowe)
◦ planujemy podmieniać obiekty dynamicznie
Zasoby statyczne i dynamiczne
• Dlaczego jednak to działa?
SolidColorBrush brush =
(SolidColorBrush)this.Resources["zielony"];
brush.Color = Colors.Red;
• Ponieważ nie podmieniliśmy obiektu, a jedynie zmieniliśmy jego stan wewnętrzny.
Zaś Brush informuje każdą używającą go kontrolkę o swojej zmianie.
Techniki zaawansowane – zasoby niewspółdzielone
• Zazwyczaj powstaje jeden obiekt, z którego korzystają wszyscy używajacy tego zasobu.
• Możemy jednak udostępniać każdemu jego własny obiekt – można rozważać wykorzystanie tego, jeśli każdy użytkownik chce osobno modyfikować zasób lub zasobem jest coś, czego nie możemy dzielić (np. element) – są jednak lepsze sposoby by to osiągnąć.
<SolidColorBrush x:Key="TileBrush" x:Shared="False" ...>
</SolidColorBrush>
Techniki zaawansowane – dostęp do zasobów w kodzie
• W ten sposób mamy dostęp do zasobów zdefiniowanych w tej kontrolce:
Button cmd = (Button)sender;
Brush brush = (Brush)cmd.Resources["zielony"];
• Dzięki temu nie musimy znać położenia zasobu – nastąpi poszukiwanie, jak w wypadku korzystania z zasobu w XAMLu:
Brush brush = (Brush)cmd.FindResource("zielony");
• Jest też TryFindResource(), które w razie niepowodzenia nie rzuca wyjątku, tylko zwraca null.
Zasoby aplikacji:
<Application ...>
<Application.Resources>
<SolidColorBrush x:Key="zielony" Color="Green" />
<SolidColorBrush x:Key="czerwony" Color="Red" />
<SolidColorBrush x:Key="niebieski" Color="Blue" />
</Application.Resources>
</Application>
• Są dostępne w całej aplikacji
◦ są trochę jak zmienne globalne: nie należy z nimi przesadzać
• Jeszcze wyżej w drzewie poszukiwania znajdują się zasoby systemowe
Zasoby systemowe:
• Udostępniane są poprzez trzy klasy (przestrzeń nazw System.Windows):
SystemColors, SystemFonts, SystemParameters label.Foreground =
new SolidBrush(SystemColors.WindowTextColor);
• lub:
label.Foreground = SystemColors.WindowTextBrush;
• lub:
<Label Foreground="{x:Static SystemColors.WindowTextBrush}">
Napis
</Label>
• lub:
<Label Foreground="{DynamicResource
{x:Static SystemColors.WindowTextBrushKey}}">
Napis</Label>
Organizacja zasobów
• W celu umożliwienia współdzielenia zasobów między różnymi projektami tworzy się słowniki zasobów. Są one zapisywane w plikach XAML (dołączanych do aplikacji, z Build Action ustawionym na Page.
w pliku AppBrushes.xaml:
<ResourceDictionary ... >
<SolidColorBrush x:Key="zielony" Color="Green" />
<SolidColorBrush x:Key="czerwony" Color="Red" />
<SolidColorBrush x:Key="niebieski" Color="Blue" />
</ResourceDictionary>
Organizacja zasobów
• Następnie powinny być dołączone do jakiejś kolekcji zasobów w aplikacji.
<Application ... >
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="AppBrushes.xaml"/>
<ResourceDictionary Source="..."/>
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="inne zasoby" ... />
...
</ResourceDictionary>
</Application.Resources>
</Application>