Использование custom Markup Extensions в Silverlight 5

понедельник, 2 мая 2011, bobasoft

Одна из захватывающих особенностей, представленных в Silverlight 5 - это пользовательское расширение разметки (custom makrup extension). Мой приятель и Silverlight MVP Jeremy Likness представил custom markup exntesion который использует MEF для импортирования экземпляров объектов объявленных в XAML. Я же хочу представить custom makrup extension по своему - импортирование RESX ресурсов локализации и упрощение задачи добавления поддержки локализации в Silverlight приложении.

В прошлом, RESX локализация, осуществлялась в Silverlight XAML с помощью встроенного расширения разметки {Binding}. Для демонстрации, следующий пример объявляет экземпляр класса-обертки вокруг ResourceManager называемый Resources (сгенерировано Visual Studio с Resources.resx), присваиваем экземпляр Resources в свойство DataContext компонента TextBlock, и используем выражение привязки данных (data-binding) для установки свойства Text компонента TextBlock равным свойству Greeting объекту Resources:

    <Grid>
      <Grid.Resources>
        <local:Resources x:Key="Localize" />
      </Grid.Resources>
      <TextBlock Text="{Binding Greeting}" DataContext="{StaticResource Localize}" />
    </Grid>

Это работает, но заставляет задуматься, зачем мы должны прибегать к привязке данных, для реализации локализации, когда локализация это стандартная задача для Silverlight приложений.

Можно сделать это немного чище, использовав custom markup exntesion. Такого рода расширение можно применить так:

    <Grid>
      <TextBlock
        Text="{local:Resx ResxKey=Greeting, ResxType=Resources, Default=Welcome}" />
    </Grid>

В этом примере Resx - это custom markup extension, ResxKey - идентифицирует локализационный ресурс, который будет загружен, ResxType - определяет класс-обертку ResourceManager который дает доступ к ресурсам и Default - это необязательное значение которые используется по умолчанию когда нужного локализационного ресурса нету или нельзя получить к нему доступ. Лутше, не так ведь? И это только одно из миллиона разных приложений для custom markup extension.

Реализовать custom markup extension, в большинстве случаев, относительно просто. Для начала нужно унаследоваться от нового в Silverlight 5 класса System.Windows.Markup.MarkupExtension. Потом нужно переопределить метод ProvideValue и возвратить значение сгенерированное расширением. Мой ResxExtension класс реализован вот так:

    public class ResxExtension : MarkupExtension
    {
        public string ResxType { get; set; }
        public string ResxKey { get; set; }
        public object Default { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (!String.IsNullOrEmpty(ResxType) && !String.IsNullOrEmpty(ResxKey))
            {
                try
                {
                    // Create a strongly typed resource manager instance
                    object resman = Activator.CreateInstance(Type.GetType(ResxType));

                    // Get the value of the specified property
                    PropertyInfo pi = resman.GetType().GetProperty(ResxKey);
                    return pi.GetValue(resman, null);
                }
                catch (Exception)
                {
                    // Purposely do nothing here to allow the call to fall through
                }
            }

            // If we make it to here, return the default value (if specified) or,
            // as a last resort, the key name
            if (Default != null)
                return Default;
            else
                return ResxKey;
        }
    }

Три публичных свойства - ResxType, ResxKey и Default - определяют параметры, которые принимает markup extension. Парсер XAML автоматически инициализирует эти свойства значениями, которые даны в разметке. Мое переопределение ProvideValue использует рефлексию для того, чтобы создать объект класса-обертки ResourceManager определенного в ResxType, и потом опять использует рефлексию для получения значения свойства чье имя было определено в свойстве ResxType. Если что-то пошло не так, ProvideValue возвратит значение по умолчанию определенное в свойстве Default или значение ResxKey если не было определенно значение Default.

Если хотите посмотреть ResxExtension в работе, можете скачать zip файл с проектом Visual Studio. Когда вы запустите приложение в первый раз, вы увидите вот это:

English resx

но если вы откроете App.xaml.cs и откоментируете строки кода, которые устанавливают региональные параметры Франции, вы увидите это:

French resx

Текст приветствия, URI флага и ширина рисунка флага идут из файлов RESX с именами Resources.resx, Resources.fr.resx, Resources.es.resx и Resources.de.resx. Откройте эти RESX файлы в Visual Studio и увидите как определены отдельные ресурсы. Применение ресурсов локализации происходит в MainPage.xaml

    <TextBlock Text="{local:Resx ResxKey=Greeting,
      ResxType=CustomMarkupExtensionDemo.Localization.Resources, Default='Nice Try!'}"
      Foreground="LightYellow" FontSize="72" FontWeight="Bold"
      HorizontalAlignment="Center" VerticalAlignment="Center">
      <TextBlock.Effect>
        <DropShadowEffect BlurRadius="12" ShadowDepth="12" Opacity="0.5" />
      </TextBlock.Effect>
    </TextBlock>
    <Image Source="{local:Resx ResxKey=FlagUri,
      ResxType=CustomMarkupExtensionDemo.Localization.Resources}"
      Width="{local:Resx ResxKey=FlagWidth,
      ResxType=CustomMarkupExtensionDemo.Localization.Resources}" />

Изучите исходный код и увидите какие расширения разметки вы можете создавать. И не забудьте установить Silverlight 5 Beta чтобы запустить пример.


Ищите нас в интернетах!

Комментарии

Свежие вакансии