sobota, 5 maja 2012

70-511 Rozdział 1 - Tworzenie interfejsu użytwkownika - Zasoby

W tym poście zostaną omówione zasoby, czyli obiekty i pliki które nie są tworzone w czasie działania programu, a dostarczone przed kompilacją.


Używanie binarnych zasobów (plików)
Aby dodać zasób do naszej aplikacji, postępujemy według poniższego schematu:
  1. Z menu projektu wybieramy Add Existing Item
  2. Po otwarciu okna dialogowego wyboru plików, wskazujemy plik który chcemy dodać
  3. W oknie właściwości zmieniamy dla naszego pliku właściwość Build Action na Resource.
Ważne! Dla WPF nie ustawiamy nigdy Build Action na Embedded Resource
Uaktualnienie pliku zasobów odbywa się poprzez ponowne wykonanie powyższych kroków.


Ładowanie zasobów
Ładowanie zasobów np. do kontrolki Image jest bardzo proste. Wystarczy do właściwości Source podstawić ścieżkę do pliku graficznego. W zależności czy plik znajduje się na tej samej wysokości w drzewie folderów, czy na innym ścieżka przyjmie odpowiednią postać:


Code:
        <Image Source="folder/pic.png" Stretch="Fill"/>
        <Image Source="pic.png" Stretch="Fill"/>


Pack URIs
Pokazana powyżej składnia jest skróconą wersją tzw. Pack URI. Pełna składnia ma następującą postać:
pack://<Authority>/<Folder>/<FileName>
  • Authority przyjmuje jedną z dwóch postaci application:,,, - jeżeli zasób pochodzi z aktualnie uruchomionej aplikacji lub siteOfOrigin:,,, - aplikacja ma szukać zasobu w jej źródłach.
  • Folder - folder w którym znajduje się zasób
  • FileName - nazwa zasobu 
Przydaje się to zwłaszcza w przypadku podawania ścieżki do zasobu w kodzie:


Code:
image.Source = new BitmapImage(new Uri("pack://application:,,,/folder/pic.png"));


Używanie zasobów z innych bibliotek Dll
Jest możliwe użycie zasobu znajdującego się w innej bibliotece dll niż aplikacja. W tym celu należy skorzystać z następującego Pack URI:
pack://application:,,,/<AssemblyName>;component/<Folder>/<FileName>


Pliki nie wkompilowywane
W dużej ilości przypadków będzie potrzeba wymiany któregoś z plików, np. będzie to szablon na podstawie którego będą generowane zaproszenia. Użycie w takim przypadku wkompilowywanych zasobów, zmuszałoby nas do kompilacji aplikacji za każdym razem kiedy szablon się zmienił. Dodatkowo pliki dźwiękowe czy też filmowe, które są odtwarzane za pomocą kontrolki MediaPlayer nie mogą korzystać z składni Pack URI.
Aby dodać plik jako zasób nie kompilowany, postępujemy według poniższych kroków:
  1. Z menu projektu wybieramy opcję Add Existing Item.
  2. Wybieramy plik który chcemy dodać
  3. Ustawiamy we właściwościach Build Action na Content oraz Copy To Output Directory na Copy Always
Po dodaniu pliku używamy relatywnej ścieżki przy odwołaniu się do niego:


Code:
<MediaElement Source="music.mp3"/>



Użycie siteOfOrigin Pack URI dla plików często zmienianych
Zdarza się, że mamy zamiar użyć pliku który jeszcze nie znajduje się w solucji bądź też będzie często zmieniany. W przypadku XAML-a plik musi się znajdować już w czasie kompilacji w odpowiednim katalogu aby można go było użyć w aplikacji. Rozwiązaniem tego problemu jest wcześniej wspomniany siteOfOrigi, który nie wkompilowuje zasobu do Dll. Dzięki temu plik może być podmieniony bez potrzeby rekompilacji całej aplikacji. Przykład:


Code:
<Image Source="pack://siteOfOrigin:,,,/image.jpg"/>




Odwołanie do zasobów z kodu
Do zasobów zawartych w aplikacji można odwołać się z kodu C#:


Code:
            StreamResourceInfo myInfo = Application.GetResourceStream(new Uri("text.txt", UriKind.Relative));
            var myReader = new StreamReader(myInfo.Stream);
            var textContent = myReader.ReadToEnd();

obiekt klasy StreamResourceInfo posiada dwie właściwości:
  • Stream - strumień do zawartości pliku
  • ContentType - rodzaj zawartości


Tworzenie Dll zawierających tylko zasoby
Rozwiązanie to jest przydatne w przypadku gdy chcemy często zmieniać pliki zasobów. Dzięki stworzeniu takiej Dll, nie ma potrzeby kompilowania i podmiany następnie całej aplikacji, a wystarczy podmienić jedną bibliotekę na nową. Kroki które należy wykonać aby stworzyć taką bibliotekę:
  1. W VS tworzymy projekt typu Empty Project.
  2. W Solution Explorer klikamy prawym przyciskiem myszy na nazwie projektu i wybieramy opcję Properties. Na zakładce Application, ustawiamy Application Type na Class Library
  3. W menu projektu wybieramy Add Existing Item aby dodać plik zasobów.
  4. Dodanemu pliku zmieniamy Build Action na Embedded Resource
  5. Budujemy Dll
Dostęp z kodu do zasobu:
  1. Pobieramy nazwę Dll: 
    Code:
    System.Reflection.AssemblyName.GetAssemblyName("C:\\myAssembly.dll"));
  2. Ładujemy Dll-kę do pamięci używając pobranej nazwy:
    Code:
    var asm = System.Reflection.Assembly.Load(aName);
  3. Następnie za pomocą metod GetManifestResourceNames - pobieramy nazwy zasobów w Dll a za pomocą metody GetManifestResourceStream - pobieramy strumień do nich. Poniżej przykład pobrania obrazka i przypisania go do kontrolki Image:
    Code:
    var res = asm.GetManifestResourceNames();
    pictureBox1.Image = new
    System.DrawingBitmap(asm.GetManifestResourceStream(res[0]));


Używanie logicznych zasobów
Zasoby logiczne to zasoby które nie są widoczne w wizualnym drzewie naszej aplikacji. Przykładem takiego zasobu jest np. Brush. Dzięki temu raz stworzony zasób pędzla możemy użyć w wielu miejscach.
Resource Logiczne możemy tworzyć na różnych poziomach: od aplikacji po zasoby kontrolki, w zależności gdzie chcemy reużywać raz stworzony zasób. Przykład:


Code:
<Window.Resources>
<RadialGradientBrush x:Key="myBrush">
<GradientStop Color="CornflowerBlue" Offset="0" />
<GradientStop Color="Crimson" Offset="1" />
</RadialGradientBrush>
</Window.Resources>

Każdy zasób musi mieć właściwość x:Key która jest unikalna w danej kolekcji zasobów.
Dostęp do tak zdefiniowanych zasobów odbywa się poprzez przypisanie zasobu do właściwości która jest z nim kompatybilna np.:


Code:
<Grid Background="{StaticResource myBrush}">
</Grid>



Statyczne i dynamiczne zasoby
Oprócz statycznych zasobów istnieją także dynamiczne zasoby. Główne różnice to:
  • Dynamiczne zasoby są tworzone za każdym odwołaniem się do nich
  • Ponieważ są tworzone za każdym razem, zmniejszają wydajność ogólną aplikacji
Jak jest więc sens ich stosowania? Jeżeli w kodzie chcielibyśmy zmienić np. kolor stosowany przez pędzel, to w przypadku StaticResource zmiana nie odniesie rezultatu, w przypadku DynamicResources zmiana zostanie odnotowana przez użytkownika.



Słowniki zasobów (Resource Dictionary)
Słowniki zasobów tworzą osobny plik w którym są przechowywane zasoby. Dzięki temu można je reużywać w wielu aplikacjach w prosty sposób. Aby dodać nowy słownik postępujemy według poniższych kroków:
  1. Dodajemy nowy element do solucji typu Resource Dictionary
  2. W pliku dodajemy kolejne zasoby
Aby można było teraz użyć zasobów ze słownika, należy go połączyć z zasobami aplikacji (bądź okna lub innej kontrolki gdzie chcemy z nich skorzystać). Przykład mergowania:


Code:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" />
<ResourceDictionary Source="Dictionary2.xaml" />
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="BlueBrush" Color="Blue" />
</ResourceDictionary>
</Window.Resources>



Dostęp do zasobów w kodzie
Zasoby w kodzie można zarówno odczytywać jak i nadpisywać. Dostęp jest możliwy na dwa sposoby:
  • Za pomocą metody (SolidColorBrush)Button1.FindResource("myBrush");
  • Odwołując się konkretnie do zasobów kontrolki (SolidColorBrush)this.Resources["myBrush"];

Brak komentarzy:

Prześlij komentarz