Artykuł

freeimages.com freeimages.com
lis 23 2014
0

Pliki resw w aplikacjach uniwersalnych dla Windows

Aplikacje uniwersalne przyniosły sporo zmian w procesie tworzenia oprogramowania na systemy Microsoftu. Oprócz zupełnie odmienionej koncepcji budowy aplikacji (uwspólnienie architektury dla różnych platform), mamy tutaj również sporo niuansów, które dzieją się niejako za kulisami. Zmiany tego rodzaju dotknęły bardzo wielu obszarów i pewnie gdybym napisał po zdaniu na temat każdej z nich, mógłbym tutaj stworzyć niezły elaborat. Nie w tym jednak rzecz.

Dziś chciałbym skupić się na jednym konkretnym temacie, a mianowicie na starych, poczciwych plikach zasobów resx, które w aplikacjach uniwersalnych stały się passé;-)

Jak to robiło się dotychczas?

Przez wiele lat w rozwiązaniach Microsoftu królowały pliki Resx. Znaleźć je można było praktycznie wszędzie. Począwszy od ">aplikacji konsolowych, poprzez programy wykorzystujące WPF, strony WWW, a skończywszy na aplikacjach mobilnych.

Pliki resx były w stanie przechowywać zarówno łańcuchy tekstowe (wykorzystywane np. do lokalizacji aplikacji) jak również pliki zewnętrzne wykorzystywane przez aplikację (obrazy, pliki audio, dokumenty itd.). Do zasobów umieszczonych w plikach resx, mogliśmy odwoływać się za pomocą klasy ResourceManager, bądź też wykorzystując silne typowanie według schematu nazwa_pliku_zasobu_bez_rozserzenia.nazwa_zasobu.

Jak widać było to rozwiązanie sprawdzone i powszechne. Pytanie więc dlaczego Microsoft zdecydował się wprowadzić nowy, uproszczony format i czy przynosi on jakieś realne benefity deweloperom?

Idea plików RESW

Pliki resw nie są tak do końca takie nowe, ponieważ zadebiutowały one już wcześniej przy okazji startu platformy Windows 8 i dedykowanych dla niej aplikacji.

Pliki resw skupiają się głównie na łańcuchach tekstowych. Nie przechowacie więc za ich pomocą jakichkolwiek innych formatów - tylko tekst. Nie zadziała również w tym przypadku silne typowanie (choć ten problem akurat można stosunkowo łatwo obejść wykorzystując zewnętrzne narzędzia). Czy są w tym przypadku zatem jakieś zmiany na plus?

Jest tutaj pewna dość istotna nowość, która w jakimś stopniu rewolucjonizuje sposób tworzenia kontrolek w kodzie XAML. Pliki resw pozwalają bowiem na definiowanie nie tylko tłumaczeń, ale także na określanie wartości dla poszczególnych właściwości wskazanej kontrolki. Być może brzmi to odrobinę abstrakcyjnie, ale poniższy przykład powinien trochę rozjaśnić cały przykład;-)

Wyobraźmy sobie że chcemy do naszej strony dodać przycisk. Zakładając, że nie zdefiniowaliśmy wcześniej żadnego określonego stylu, jego odpowiednie przygotowanie będzie kosztować nas trochę pracy. Będziemy musieli np. wskazać wielkość tekstu widocznego w obszarze kontrolki, szerokość, wysokość itd. Koniec końców, skończymy z dość brzydkim i rozwlekłym XAMLem:

<Button x:Name="MyButton" Content="Przycisk" FontSize="30" Width="300" Height="100" />

Wykorzystując nowy mechanizm, możemy powyższy kod uprościć do postaci:

<Button x:Name="MyButton" x:Uid="MyButton" />

Czyż tak nie jest prościej i czytelniej? Pytanie tylko co zrobić z pozostałymi informacjami? Cóż, wystarczy je umieścić w pliku RESW, tak jak na rysunku poniżej;-)

Jak widać, przepisaliśmy tutaj właściwie wszystkie właściwości kontrolki, które wcześniej były umieszczone w XAMLu. Cały mechanizm jest bardzo prosty, ponieważ wystarczy podać nazwę kontrolki, a następnie po kropce określić którą jej właściwość chcemy opisać. Da się tak zrobić z wszystkimi propertisami, które można opisać za pomocą liczb i tekstu;-) Warto jeszcze również zwrócić uwagę, że dzięki takiemu podejściu, możemy zmieniać wyglądać aplikacji np. w zależności od języka z jakiego korzysta użytkownik. W praktyce możemy np. zrobić szerszy przycisk, dla języka w którym określone tłumaczenie jest dłuższe. Możliwości są więc naprawdę duże.

W plikach RESW wciąż możemy umieszczać oczywiście normalne stringi, dokładnie w taki sam sposób jaki robiliśmy to w plikach RESX.

Gdzie umieszczać pliki RESW?

Pliki RESW zasadniczo możemy umieszczać w trzech różnych lokalizacjach:

  • W projekcie aplikacji zarówno Windows jak i Windows Phone
  • W części Shared projektu Universal App
  • W bibliotece Portable, z której korzysta projekt

Niezależnie od wybranej miejscówki, warto zasoby jest grupować i umieszczać w katalogach (Resources, Strings itp.). Jeśli tworzycie aplikację, która będzie obsługiwać różne języki, to wewnątrz wspomnianego wyżej katalogu powinniście umieścić również katalogi dla wszystkich obsługiwanych języków. Poniższy rysunek pokazuje przykład projektu w który mamy jeden domyślny język (Solution Explorer po lewej) oraz dwa różne języki (Solution Explorer po prawej).

Tworząc aplikacje wielojęzyczne musimy również pamiętać o określeniu domyślnego języka dla naszej aplikacji. Możemy to uczynić w plikach manifestów zarówno dla aplikacji dla W8.1 jak i WP8.1. Domyślnie znajdziemy tam język angielski z dialektem amerykańskim, więc w większości przypadków jest to dobra opcja.

W jaki sposób korzystać z zasobów?

Pokazałem już w jaki sposób korzystać z zasobów bezpośrednio w XAMLu, warto również wiedzieć jak można się do nich dobrać z kodu. W tym miejscu musimy skorzystać z klasy ResourceLoader (nie ResourceManager jak wcześniej):

var resLoader = ResourceLoader.GetForCurrentView();
string appTitle = resLoader.GetString("AppTitle");

Powyższy przepis zadziała jednak tylko w sytuacji, gdy będziemy chcieli uzyskać zasób z domyślnego pliku Resources.resw. Według konwencji, to właśnie tam zagląda ResourceLoader, jeśli nie powiemy mu inaczej. Na dodatek plik ten musi znajdować się bezpośrednio w projekcie aplikacji, bądź też w jej części współdzielonej. Co w sytuacji gdy plik zasobów nazywa się inaczej? Cóż można to stosunkowo łatwo obejść w XAMLu:

<Button x:Name="ShowLogBtn" x:Uid="/Errors/ShowLogBtn" />

Oraz w code-behind:

var resLoader = ResourceLoader.GetForCurrentView("/Errors");
string errorDesc = resLoader.GetString("ErrorDesc");

W tym przykładzie wskazaliśmy klasie ResourceLoader, że powinna ona szukać naszych zasobów w pliku Errors.resw (rozszerzenie pomijamy).

W sytuacji gdy chcemy odnieść się do stringu z biblioteki portable, będziemy musieli również podać pełną przestrzeń nazw, co w praktyce nie jest do końca wygodnym rozwiązaniem:

<Button x:Name="ShowLogBtn" x:Uid="/My.Portable.Namespace/Errors/ShowLogBtn" />

Oraz w code-behind:

var resLoader = ResourceLoader.GetForCurrentView("/My.Portable.Namespace/Errors");
string errorDesc = resLoader.GetString("ErrorDesc");

ResW File Code Generator

Na samym początku tekstu, pisałem o tym, że domyślnie korzystając z plików RESW, nie można używać silnego typowania. Na szczęście można ten problem stosunkowo łatwo obejść, wykorzystując narzędzie ResW File Code Generator. Nie będę tutaj opisywał w jaki sposób skonfigurować projekt do pracy z tym rozszerzeniem, ponieważ wszystkie niezbędne informacje znajdziecie w linku. Nie mniej jednak dzięki temu dodatkowi, znów będziecie mogli korzystać z zasobów w stary, sprawdzony sposób z poziomu Code-behind;-)

Podsumowanie

Nowy format plików zasobów budzi sporo kontrowersji i niestety nie do końca można powiedzieć, że jest to krok do przodu. Jeśli nie do końca podoba Wam się to co prezentuje format RESW, to pocieszę Was, że wciąż możecie korzystać plików RESX, które każdy z nas dobrze zna. Alternatywnie możecie skorzystać z innego, nowego rozwiązania zwanego Multilingual App Toolkit.

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus