Как стать автором
Поиск
Написать публикацию
Обновить

Ещё один способ использования resx файлов в WPF

Для начала — небольшой ликбез.

Microsoft предлагает следующие способы для локализации WPF приложений: локализация с помощью утилиты locbaml, с помощью resx файлов и с помощью xml.
Локализация с использованием resx файлов является стандартным .net подходом и является продуманным и проверенным решением. Тем не менее, Microsoft рекомендует именно locbaml подход, который и использовался в нашем приложении. Однако, мы решили отказаться от него, из-за чрезмерной сложности при локализации и переключиться на традиционные resx ресурсы.

С первой проблемой — конвертацией строчек из ResourceDictionary в resx файл справиться легко, благо и то и другое — xml, а вот с использованием resx ресурсов в WPF приложении дело обстоит сложней. В интернете я так и не нашёл достаточно простого способа использовать строчки из resx файлов в xaml разметке. Всё что предлагалось — раличные варианты расширения разметки т.е. для доступа к ресурсам использовать конструкции вида: Text="{x:Static res:Resources.LabelText}. Что абсолютно неприемлимо, так как в приложении таких мест было тысячи. Однако, я всё таки нашёл приемлимое решение.

Итак, для примера создадим пустое WPF приложение. Откроем его ресусры (Properties -> Resources.resx) и добавим строчку с именем LabelText и содержимым:



Далее скопируем этот файл и назовём его Resources.ru-Ru.resx и изменим строчку в ресурсах:



Затем, изменим App.xaml:
Copy Source | Copy HTML
  1. <Application x:ClassWpfApplication1.App»
  2.  xmlnsschemas.microsoft.com/winfx/2006/xaml/presentation»
  3.  xmlns:xschemas.microsoft.com/winfx/2006/xaml»
  4.  StartupApplication_Startup»
  5.  StartupUriMainWindow.xaml»>
  6.  <Application.Resources>
  7.  </Application.Resources>

В обработчик событий пропишем:
Copy Source | Copy HTML
  1. private void Application_Startup(object sender, StartupEventArgs e)
  2. {
  3.     ResourcesLoader.LoadResources(this.Resources);
  4. }

Теперь самое интересное — класс ResourceLoader. Добавим его в проект, класс будет содержать два метода:
Copy Source | Copy HTML
  1. static internal class ResourcesLoader
  2. {
  3.     // Получаем словарь ресурсов для сборки с заданной культурой.
  4.     static private ResourceDictionary _GetResourceDictionary(CultureInfo cultureInfo)
  5.     {
  6.         ResourceDictionary dictionary = new ResourceDictionary();
  7.         var resourceSet = Properties.Resources.ResourceManager.GetResourceSet(cultureInfo, true, true);
  8.         foreach (DictionaryEntry item in resourceSet)
  9.             dictionary.Add(item.Key, item.Value);
  10.  
  11.         return dictionary;
  12.     }
  13.  
  14.     static public void LoadResources(ResourceDictionary resourceDictionary)
  15.     {
  16.         // Получаем словарь для основйной сборки.
  17.         ResourceDictionary defaultDictionary = _GetResourceDictionary(DEFAULT_CULTURE);
  18.  
  19.         // Слдиваем этот словарь с остальными словарями приложения
  20.         resourceDictionary.MergedDictionaries.Add(defaultDictionary);
  21.  
  22.         // Если текущая культура отличается от культуры по умолчанию - 
  23.         // грузим ресурсы из саттелитной сборки текущей культуры
  24.         if (Thread.CurrentThread.CurrentUICulture.CompareInfo !=
  25.             DEFAULT_CULTURE.CompareInfo)
  26.         {
  27.             ResourceDictionary localizedDictionary =
  28.                 _GetResourceDictionary(Thread.CurrentThread.CurrentUICulture);
  29.             resourceDictionary.MergedDictionaries.Add(localizedDictionary);
  30.         }
  31.     }
  32.  
  33.     private static CultureInfo DEFAULT_CULTURE = CultureInfo.GetCultureInfo("en-US");
  34. }

Всё, теперь ресурсы без проблем можно использовать из xaml'a:
Copy Source | Copy HTML
  1. <Window x:Class="WpfApplication1.MainWindow"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.         Title="MainWindow" Height="100" Width="225">
  5.         <Label Content="{DynamicResource LabelText}" FontSize="36" />
  6. </Window>
  7.  

Теперь при запуске приложения в русской системе будет выводиться:



В системе с английской локалью:



Если нужно перевести на другой язык — создаём соответствующий resx файл и переводим.
Насчёт производительности. Я тестировал на файле из 1200 строк, всё слияние словарей занимает 20 миллисекунд, т.е. никакой ощутимой задержки при загрузке это не вызовет. Единственная проблема, которая остаётся — текст не отображается во время разработки в дизайнере студии.

Ссылки:
Статья на хабре, где подробно описан способ с использованием Markup-Extension.
Проект. VS 2010.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.