Я работаю над чем-то, где отдельные части были хорошо обсуждены, но у меня проблемы с соединением их всех.У нас есть приложение, которое имеет множество плагинов, требующих различных входных параметров, которые я пытаюсь сделать многоязычными.Я работал над динамическим графическим интерфейсом, который проверяет плагин для создания массива входных параметров и использует DataTemplateSelector, чтобы выбрать правильный элемент управления в зависимости от типа параметра.Для перечислителей мы пытаемся связать локализованное отображаемое имя со списком.В StackOverflow много потоков о том, как выполнять привязку перечислений / списков, но я не нашел многоязычных и динамических (datatemplate или других).
У Брайана Лагунаса есть отличная запись в блоге, которая почти привела нас туда: http://brianlagunas.com/localize-enum-descriptions-in-wpf. Однако он статически связывает перечисление в XAML.У нас есть сотни перечислений, и мы все время создаем новые.Поэтому я пытаюсь понять, как лучше всего добиться чего-то более динамичного.Где-то вдоль линии мне нужно использовать отражение, чтобы выяснить тип перечислителя и связать его со списком, но я не могу понять, где, когда и как.
Я загрузил расширенный пример здесь: https://github.com/bryandam/Combo_Enum_MultiLingual. Я постараюсь включить соответствующие фрагменты здесь, но трудно сжать их.
public partial class MainWindow : Window
{
public ObservableCollection<Object> InputParameterList { get; set; } = new ObservableCollection<Object>();
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
//Create an example input object.
InputParameter bitlocker_drive = new InputParameter();
bitlocker_drive.Name = "BitLocker Enabled";
bitlocker_drive.Type = typeof(String);
InputParameterList.Add(bitlocker_drive);
InputParameter bitlocker_status = new InputParameter();
bitlocker_status.Name = "Status";
bitlocker_status.Type = typeof(Status);
InputParameterList.Add(bitlocker_status);
InputParameter bitlocker_foo = new InputParameter();
bitlocker_foo.Name = "Foo";
bitlocker_foo.Type = typeof(Foo);
InputParameterList.Add(bitlocker_foo);
}
}
Вот мой XAML:
<Window x:Class="BindingEnums.MainWindow"
....
<Window.Resources>
...
<DataTemplate x:Key="ComboBox">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name, Mode=TwoWay}" />
<ComboBox ItemsSource="{Binding Source={local:EnumBindingSource {x:Type local:Status}}}" Grid.Column="1"/>
</Grid>
</DataTemplate>
...
<local:InputParameterTemplateSelector x:Key="InputDataTemplateSelector" Checkbox="{StaticResource Checkbox}" ComboBox="{StaticResource ComboBox}" DatePicker="{StaticResource DatePicker}" TextBox="{StaticResource TextBox}"/>
</Window.Resources>
<Grid>
<ListBox Name="InputParameters" KeyboardNavigation.TabNavigation="Continue" HorizontalContentAlignment="Stretch" ItemsSource="{Binding InputParameterList}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" Background="Transparent" BorderBrush="Transparent" ItemTemplateSelector="{StaticResource InputDataTemplateSelector}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsTabStop" Value="False" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
Вот два примера перечисления, с которыми я тестирую:
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
public enum Status
{
[Display(Name = nameof(Resources.EnumResources.Good), ResourceType = typeof(Resources.EnumResources))]
Good,
[Display(Name = nameof(Resources.EnumResources.Better), ResourceType = typeof(Resources.EnumResources))]
Better,
Best
}
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
public enum Foo
{
[Display(Name = nameof(Resources.EnumResources.Foo), ResourceType = typeof(Resources.EnumResources))]
Foo,
[Display(Name = nameof(Resources.EnumResources.Bar), ResourceType = typeof(Resources.EnumResources))]
Bar
}
Вот преобразователь типа enum:
public class EnumDescriptionTypeConverter : EnumConverter
{
public EnumDescriptionTypeConverter(Type type)
: base(type)
{}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
if (value != null)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
if (fi != null)
{
//Reflect into the value's type to get the display attributes.
FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
DisplayAttribute displayAttribute = fieldInfo?
.GetCustomAttributes(false)
.OfType<DisplayAttribute>()
.SingleOrDefault();
if (displayAttribute == null)
{
return value.ToString();
}
else
{
//Look up the localized string.
ResourceManager resourceManager = new ResourceManager(displayAttribute.ResourceType);
string name = resourceManager.GetString(displayAttribute.Name);
return string.IsNullOrWhiteSpace(name) ? displayAttribute.Name : name;
}
}
}
return string.Empty;
}
return base.ConvertTo(context, culture, value, destinationType);
}
Вот расширение разметки источника привязки Enum:
public class EnumBindingSourceExtension : MarkupExtension
{
...
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (null == this._enumType)
throw new InvalidOperationException("The EnumType must be specified.");
Type actualEnumType = Nullable.GetUnderlyingType(this._enumType) ?? this._enumType;
Array enumValues = Enum.GetValues(actualEnumType);
if (actualEnumType == this._enumType)
return enumValues;
Array tempArray = Array.CreateInstance(actualEnumType, enumValues.Length + 1);
enumValues.CopyTo(tempArray, 1);
return tempArray;
}
}
Опять же, моя цель состоит в том, чтобы выяснить, как избежать статической привязки к одному типу перечисления (как в XAML ниже), и вместо этого привязать его, основываясь на любом типе входного параметра:
<ComboBox ItemsSource="{Binding Source={local:EnumBindingSource {x:Type local:Status}}}" Grid.Column="1"/
Я поэкспериментировал с этим в коде окна, селекторе шаблонов данных и даже в пользовательском элементе управления без особого успеха.Мое первое «настоящее» приложение WPF, так что я, по общему признанию, немного вне своей лиги, собирая все это вместе против их отдельных частей.
Вот пример запуска