среда, 21 сентября 2011 г.

Использование перечислений в локализуемых WPF приложениях

Использование перечислений (enum) в WPF очень удобно. Например, можно продекларировать в XAML указать, что поля перечисления надо использовать для наполнения ComboBox, и в выпадающем списке появятся текстовые представления полей перечисления.

Для этого в ресурсах надо объявить ObjectDataProvider, который обратится к методу Enum.GetValues:

<ObjectDataProvider x:Key="IntervalList"
ObjectType="{x:Type s:Enum}"
MethodName="GetValues">
<ObjectDataProvider.MethodParameters>
<x:Type Type="{x:Type m:ProblemDiagnosticInterval}"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>





и непосредственно в ComboBox указать объявленный объект в качестве источника данных:




<ComboBox HorizontalAlignment="Stretch" x:Name="IntervalBox"
SelectedValue="{Binding Value.Interval}"
ItemsSource="{Binding Source={StaticResource IntervalList}}" />





И в выпадающем списке появятся значения из перечисления:




public enum ProblemDiagnosticInterval
{
Never,
TwoWeeks,
OneMonth,
ThreeMonth,
Unlimited
}





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



Я уже описывал технологию локализации WPF приложений. Дальнейшее изложение опирается на этот материал.



Чтобы управлять текстом, который отображается вместо поля перечисления необходимо завести простой класс-конвертер типов, связанный с этим перечислением:




[TypeConverter(typeof(EnumToResourceStringConverter))]
public enum ProblemDiagnosticInterval
{
...
}

/// <summary>
/// переопределяет для перечислений конвертацию
/// в строку из ресурсника приложения
/// </summary>
public class EnumToResourceStringConverter : TypeConverter
{
public static IStringResourceAccessor ResourceAccessor { get; set; }

public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType.Equals(typeof(Enum)))
return true;

return base.CanConvertFrom(context, sourceType);
}

public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType.Equals(typeof(string)))
return true;

return base.CanConvertTo(context, destinationType);
}

public override object ConvertTo(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (value != null && destinationType.Equals(typeof(string)))
{
var originValue = (string)base.ConvertTo(context, culture, value, destinationType);
var typeName = value.GetType().Name;

return ResourceAccessor.GetStringByID(typeName + "_" + originValue);
}

return base.ConvertTo(context, culture, value, destinationType);
}
}





Этот конвертер реализует преобразование элемента перечисления в строку, используя обращение к объекту, реализующему доступ к локализованным строками через интерфейс IStringResourceAccessor.




public interface IStringResourceAccessor
{
string GetStringByID(string stringID);
}





В качестве идентификатора строки в ресурсах передается строка вида <имя перечисления>_<имя элемента перечисления>, например, в описываемом примере это будут строки ProblemDiagnosticInterval_Never, ProblemDiagnosticInterval_TwoWeeks и т.д.Статическое свойство, в котором хранится указатель на интерфейс доступа к ресурсам инициализируется, например, в конструкторе класса приложения:




/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application, IStringResourceAccessor
{
public App(string[] args)
{
...
EnumToResourceStringConverter.ResourceAccessor = this;
...
}

...

public static string RString(string name)
{
try
{
return Application.Current.Resources[name].ToString();
}
catch (System.Exception)
{
return "load string " + name + " failed";
}
}

#region IStringResourceAccessor Members

public string GetStringByID(string stringID)
{
return App.RString(stringID);
}

#endregion

}





После этого необходимо в одном из XAML объявить строки с правильными идентификаторами, которые будут использованы в для показа полей перечисления:




<!-- названия интервалов диагностирования проблем -->
<core:StringObject x:Key="ProblemDiagnosticInterval_Never" Localization.Attributes="Value (Readable Modifiable Text)" Value="Скрыть" />
<core:StringObject x:Key="ProblemDiagnosticInterval_TwoWeeks" Localization.Attributes="Value (Readable Modifiable Text)" Value="2 недели" />
<core:StringObject x:Key="ProblemDiagnosticInterval_OneMonth" Localization.Attributes="Value (Readable Modifiable Text)" Value="1 месяц" />
<core:StringObject x:Key="ProblemDiagnosticInterval_ThreeMonth" Localization.Attributes="Value (Readable Modifiable Text)" Value="3 месяца" />
<core:StringObject x:Key="ProblemDiagnosticInterval_Unlimited" Localization.Attributes="Value (Readable Modifiable Text)" Value="Все" />





Текстовые значения для других языков указываются в отдельном файле в соответствии с технологией локализации.



И вот результат этих манипуляций:



ProjectsProfiler настройка параметров отображения проблем

Комментариев нет:

Отправить комментарий