Использование custom Markup Extensions в Silverlight 5
Одна из захватывающих особенностей, представленных в 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. Когда вы запустите приложение в первый раз, вы увидите вот это:
но если вы откроете App.xaml.cs и откоментируете строки кода, которые устанавливают региональные параметры Франции, вы увидите это:
Текст приветствия, 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 чтобы запустить пример.