我试图找到一个简单的示例,其中枚举按原样显示。我见过的所有示例都试图添加外观漂亮的显示字符串,但我不希望这种复杂性。
基本上,我有一个类来保存我绑定的所有属性,方法是首先将DataContext设置为此类,然后在xaml文件中指定如下所示的绑定:
<ComboBox ItemsSource="{Binding Path=EffectStyle}"/>
但这不会在ComboBox
as项目中显示枚举值。
我试图找到一个简单的示例,其中枚举按原样显示。我见过的所有示例都试图添加外观漂亮的显示字符串,但我不希望这种复杂性。
基本上,我有一个类来保存我绑定的所有属性,方法是首先将DataContext设置为此类,然后在xaml文件中指定如下所示的绑定:
<ComboBox ItemsSource="{Binding Path=EffectStyle}"/>
但这不会在ComboBox
as项目中显示枚举值。
Answers:
您可以通过将以下代码放入Window Loaded
事件处理程序中来从代码中完成操作,例如:
yourComboBox.ItemsSource = Enum.GetValues(typeof(EffectStyle)).Cast<EffectStyle>();
如果需要在XAML中绑定它,则需要使用它ObjectDataProvider
来创建可用作绑定源的对象:
<Window x:Class="YourNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:StyleAlias="clr-namespace:Motion.VideoEffects">
<Window.Resources>
<ObjectDataProvider x:Key="dataFromEnum" MethodName="GetValues"
ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="StyleAlias:EffectStyle"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid>
<ComboBox ItemsSource="{Binding Source={StaticResource dataFromEnum}}"
SelectedItem="{Binding Path=CurrentEffectStyle}" />
</Grid>
</Window>
在下一个代码上引起注意:
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:StyleAlias="clr-namespace:Motion.VideoEffects"
指导如何映射可在MSDN上阅读的名称空间和程序集。
xmlns:DllAlias="clr-namespace:NamespaceInsideDll; assembly=DllAssemblyName"
XAML以使用它。这是指南:msdn.microsoft.com/en-us/library/ms747086.aspx
我喜欢将要绑定的所有对象都在自己的中定义ViewModel
,因此我尽量避免<ObjectDataProvider>
在xaml中使用。
我的解决方案不使用View中定义的任何数据,也无需使用任何代码。只有DataBinding,可重用的ValueConverter,用于获取任何Enum类型的描述集合的方法以及要绑定到的ViewModel中的单个属性。
当我想将绑定Enum
到时ComboBox
,我要显示的文本永远不会与的值匹配Enum
,因此我使用[Description()]
属性为它提供了我实际上想在文本中看到的文本ComboBox
。如果我一周中有几天的时间,它将看起来像这样:
public enum DayOfWeek
{
// add an optional blank value for default/no selection
[Description("")]
NOT_SET = 0,
[Description("Sunday")]
SUNDAY,
[Description("Monday")]
MONDAY,
...
}
首先,我创建了带有几个处理枚举的方法的帮助器类。一种方法获取特定值的描述,另一种方法获取类型的所有值及其描述。
public static class EnumHelper
{
public static string Description(this Enum value)
{
var attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Any())
return (attributes.First() as DescriptionAttribute).Description;
// If no description is found, the least we can do is replace underscores with spaces
// You can add your own custom default formatting logic here
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " ")));
}
public static IEnumerable<ValueDescription> GetAllValuesAndDescriptions(Type t)
{
if (!t.IsEnum)
throw new ArgumentException($"{nameof(t)} must be an enum type");
return Enum.GetValues(t).Cast<Enum>().Select((e) => new ValueDescription() { Value = e, Description = e.Description() }).ToList();
}
}
接下来,我们创建一个ValueConverter
。从继承MarkupExtension
可以更轻松地在XAML中使用,因此我们不必将其声明为资源。
[ValueConversion(typeof(Enum), typeof(IEnumerable<ValueDescription>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return EnumHelper.GetAllValuesAndDescriptions(value.GetType());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
我ViewModel
只需要1个属性View
即可绑定到SelectedValue
和ItemsSource
组合框:
private DayOfWeek dayOfWeek;
public DayOfWeek SelectedDay
{
get { return dayOfWeek; }
set
{
if (dayOfWeek != value)
{
dayOfWeek = value;
OnPropertyChanged(nameof(SelectedDay));
}
}
}
最后绑定ComboBox
视图(ValueConverter
在ItemsSource
绑定中使用)...
<ComboBox ItemsSource="{Binding Path=SelectedDay, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectedValue="{Binding Path=SelectedDay}" />
要实现此解决方案,您只需要复制我的EnumHelper
课程和EnumToCollectionConverter
课程。他们将与任何枚举一起工作。另外,我没有在此处包括它,但是ValueDescription
该类只是一个简单的类,具有2个公共对象属性,一个称为Value
,一个称为Description
。您可以自己创建,也可以更改代码以使用Tuple<object, object>
或KeyValuePair<object, object>
ValueDescription
具有公共属性的类Value
和Description
Tuple<T1, T2>
or或or KeyValuePair<TKey, TValue>
代替ValueDescription
该类,这样就不必创建自己的类。
我使用了另一个使用MarkupExtension的解决方案。
我制作了提供项目来源的课程:
public class EnumToItemsSource : MarkupExtension
{
private readonly Type _type;
public EnumToItemsSource(Type type)
{
_type = type;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Enum.GetValues(_type)
.Cast<object>()
.Select(e => new { Value = (int)e, DisplayName = e.ToString() });
}
}
几乎就是所有...现在在XAML中使用它:
<ComboBox DisplayMemberPath="DisplayName"
ItemsSource="{persons:EnumToItemsSource {x:Type enums:States}}"
SelectedValue="{Binding Path=WhereEverYouWant}"
SelectedValuePath="Value" />
将“ enums:States”更改为您的枚举
e.ToString()
显示名称。您可以使用自己的转换器,descrtiption属性解析器。
{Binding Path=WhereEverYouWant}
),并且如果您希望它支持双向绑定,那么您也将为此提供一个后备字段。因此,您这样做并不是要替换2个属性和1个后备字段,而只是替换1个单行只读属性。
使用ObjectDataProvider:
<ObjectDataProvider x:Key="enumValues"
MethodName="GetValues" ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:ExampleEnum"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
然后绑定到静态资源:
ItemsSource="{Binding Source={StaticResource enumValues}}"
根据本文
xmlns:System="clr-namespace:System;assembly=mscorlib"
Nick的回答确实对我有所帮助,但我意识到可以对其进行一些微调,以避免额外的类ValueDescription。我记得框架中已经存在一个KeyValuePair类,因此可以代替使用它。
代码仅稍有变化:
public static IEnumerable<KeyValuePair<string, string>> GetAllValuesAndDescriptions<TEnum>() where TEnum : struct, IConvertible, IComparable, IFormattable
{
if (!typeof(TEnum).IsEnum)
{
throw new ArgumentException("TEnum must be an Enumeration type");
}
return from e in Enum.GetValues(typeof(TEnum)).Cast<Enum>()
select new KeyValuePair<string, string>(e.ToString(), e.Description());
}
public IEnumerable<KeyValuePair<string, string>> PlayerClassList
{
get
{
return EnumHelper.GetAllValuesAndDescriptions<PlayerClass>();
}
}
最后是XAML:
<ComboBox ItemSource="{Binding Path=PlayerClassList}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding Path=SelectedClass}" />
我希望这对其他人有帮助。
KeyValuePair
但最终我决定使用a KeyValuePair
来表示不是键值对的某些东西,只是为了避免编写平凡的简单类并没有多大意义。该ValueDescription
班只有5条线路,其中2只是{
和}
您将需要在枚举中创建值的数组,可以通过调用System.Enum.GetValues()来创建该数组,并将所需Type
项的枚举传递给它。
如果为该ItemsSource
属性指定此属性,则应使用所有枚举的值填充该属性。您可能想绑定SelectedItem
到EffectStyle
它(假设它是同一个枚举的属性,并且包含当前值)。
以上所有帖子都错过了一个简单的技巧。可以从SelectedValue的绑定中找出如何自动地填充ItemsSource,以便XAML标记正确。
<Controls:EnumComboBox SelectedValue="{Binding Fool}"/>
例如在我的ViewModel中
public enum FoolEnum
{
AAA, BBB, CCC, DDD
};
FoolEnum _Fool;
public FoolEnum Fool
{
get { return _Fool; }
set { ValidateRaiseAndSetIfChanged(ref _Fool, value); }
}
ValidateRaiseAndSetIfChanged是我的INPC挂钩。您的可能会有所不同。
EnumComboBox的实现如下,但是首先我需要一个小帮手来获取我的枚举字符串和值
public static List<Tuple<object, string, int>> EnumToList(Type t)
{
return Enum
.GetValues(t)
.Cast<object>()
.Select(x=>Tuple.Create(x, x.ToString(), (int)x))
.ToList();
}
和主类(请注意,我正在使用ReactiveUI通过WhenAny来挂接属性更改)
using ReactiveUI;
using ReactiveUI.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Documents;
namespace My.Controls
{
public class EnumComboBox : System.Windows.Controls.ComboBox
{
static EnumComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(EnumComboBox), new FrameworkPropertyMetadata(typeof(EnumComboBox)));
}
protected override void OnInitialized( EventArgs e )
{
base.OnInitialized(e);
this.WhenAnyValue(p => p.SelectedValue)
.Where(p => p != null)
.Select(o => o.GetType())
.Where(t => t.IsEnum)
.DistinctUntilChanged()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(FillItems);
}
private void FillItems(Type enumType)
{
List<KeyValuePair<object, string>> values = new List<KeyValuePair<object,string>>();
foreach (var idx in EnumUtils.EnumToList(enumType))
{
values.Add(new KeyValuePair<object, string>(idx.Item1, idx.Item2));
}
this.ItemsSource = values.Select(o=>o.Key.ToString()).ToList();
UpdateLayout();
this.ItemsSource = values;
this.DisplayMemberPath = "Value";
this.SelectedValuePath = "Key";
}
}
}
您还需要在Generic.XAML中正确设置样式,否则您的框将不会呈现任何内容,您将把头发拉出来。
<Style TargetType="{x:Type local:EnumComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
</Style>
就是这样。显然可以将其扩展为支持i18n,但会使职位更长。
通用应用程序的工作方式似乎有所不同。它没有功能齐全的XAML的所有功能。对我有用的是:
只是为了好玩,我整理了一个模板化类来帮助解决这个问题,并将其发布到MSDN Samples页面。多余的位使我可以选择覆盖枚举的名称,并让我隐藏一些枚举。我的代码看起来很像Nick的代码(如上),我希望我之前已经看过。
这个问题有很多很好的答案,我谦卑地提交我的。我发现我的更简单,更优雅。它仅需要一个值转换器。
给定一个枚举...
public enum ImageFormat
{
[Description("Windows Bitmap")]
BMP,
[Description("Graphics Interchange Format")]
GIF,
[Description("Joint Photographic Experts Group Format")]
JPG,
[Description("Portable Network Graphics Format")]
PNG,
[Description("Tagged Image Format")]
TIFF,
[Description("Windows Media Photo Format")]
WDP
}
还有一个价值转换器...
public class ImageFormatValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is ImageFormat format)
{
return GetString(format);
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string s)
{
return Enum.Parse(typeof(ImageFormat), s.Substring(0, s.IndexOf(':')));
}
return null;
}
public string[] Strings => GetStrings();
public static string GetString(ImageFormat format)
{
return format.ToString() + ": " + GetDescription(format);
}
public static string GetDescription(ImageFormat format)
{
return format.GetType().GetMember(format.ToString())[0].GetCustomAttribute<DescriptionAttribute>().Description;
}
public static string[] GetStrings()
{
List<string> list = new List<string>();
foreach (ImageFormat format in Enum.GetValues(typeof(ImageFormat)))
{
list.Add(GetString(format));
}
return list.ToArray();
}
}
资源...
<local:ImageFormatValueConverter x:Key="ImageFormatValueConverter"/>
XAML声明...
<ComboBox Grid.Row="9" ItemsSource="{Binding Source={StaticResource ImageFormatValueConverter}, Path=Strings}"
SelectedItem="{Binding Format, Converter={StaticResource ImageFormatValueConverter}}"/>
查看模型...
private ImageFormat _imageFormat = ImageFormat.JPG;
public ImageFormat Format
{
get => _imageFormat;
set
{
if (_imageFormat != value)
{
_imageFormat = value;
OnPropertyChanged();
}
}
}
结果组合框...
public class EnumItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!value.GetType().IsEnum)
return false;
var enumName = value.GetType();
var obj = Enum.Parse(enumName, value.ToString());
return System.Convert.ToInt32(obj);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Enum.ToObject(targetType, System.Convert.ToInt32(value));
}
}
如果直接绑定到枚举对象模型属性,则应使用此类Enum值转换器扩展Rogers和Greg的答案。
如果要绑定到ViewModel上的实际枚举属性,而不是枚举的int表示形式,则事情会很棘手。我发现有必要绑定到字符串表示形式,而不是上面所有示例中期望的int值。
您可以通过将一个简单的文本框绑定到要在ViewModel上绑定到的属性来判断是否是这种情况。如果显示文本,请绑定到字符串。如果显示数字,请绑定到该值。注意我曾经两次使用Display,这通常是一个错误,但这是它唯一的工作方式。
<ComboBox SelectedValue="{Binding ElementMap.EdiDataType, Mode=TwoWay}"
DisplayMemberPath="Display"
SelectedValuePath="Display"
ItemsSource="{Binding Source={core:EnumToItemsSource {x:Type edi:EdiDataType}}}" />
格雷格
我喜欢tom.maruska的答案,但是我需要支持模板在运行时可能遇到的任何枚举类型。为此,我必须使用绑定来指定标记扩展的类型。我可以通过nicolay.anykienko的答案来解决这个非常灵活的标记扩展问题,无论我想到什么,它都可以工作。它的消耗方式如下:
<ComboBox SelectedValue="{Binding MyEnumProperty}"
SelectedValuePath="Value"
ItemsSource="{local:EnumToObjectArray SourceEnum={Binding MyEnumProperty}}"
DisplayMemberPath="DisplayName" />
上面引用的混搭标记扩展的来源:
class EnumToObjectArray : MarkupExtension
{
public BindingBase SourceEnum { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
DependencyObject targetObject;
DependencyProperty targetProperty;
if (target != null && target.TargetObject is DependencyObject && target.TargetProperty is DependencyProperty)
{
targetObject = (DependencyObject)target.TargetObject;
targetProperty = (DependencyProperty)target.TargetProperty;
}
else
{
return this;
}
BindingOperations.SetBinding(targetObject, EnumToObjectArray.SourceEnumBindingSinkProperty, SourceEnum);
var type = targetObject.GetValue(SourceEnumBindingSinkProperty).GetType();
if (type.BaseType != typeof(System.Enum)) return this;
return Enum.GetValues(type)
.Cast<Enum>()
.Select(e => new { Value=e, Name = e.ToString(), DisplayName = Description(e) });
}
private static DependencyProperty SourceEnumBindingSinkProperty = DependencyProperty.RegisterAttached("SourceEnumBindingSink", typeof(Enum)
, typeof(EnumToObjectArray), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// Extension method which returns the string specified in the Description attribute, if any. Oherwise, name is returned.
/// </summary>
/// <param name="value">The enum value.</param>
/// <returns></returns>
public static string Description(Enum value)
{
var attrs = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs.Any())
return (attrs.First() as DescriptionAttribute).Description;
//Fallback
return value.ToString().Replace("_", " ");
}
}
简单明了的解释:http : //brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/
xmlns:local="clr-namespace:BindingEnums"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
...
<Window.Resources>
<ObjectDataProvider x:Key="dataFromEnum" MethodName="GetValues"
ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:Status"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
...
<Grid>
<ComboBox HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="150"
ItemsSource="{Binding Source={StaticResource dataFromEnum}}"/>
</Grid>
使用ReactiveUI
,我创建了以下替代解决方案。这不是一个优雅的多合一解决方案,但我认为至少它是可读的。
就我而言,将列表绑定enum
到控件是很少见的,因此我不需要在整个代码库中扩展解决方案。但是,可以通过将代码更改EffectStyleLookup.Item
为来使代码更通用Object
。我用我的代码对其进行了测试,不需要其他修改。这意味着一个助手类可以应用于任何enum
列表。尽管那样会降低其可读性-对其影响ReactiveList<EnumLookupHelper>
不大。
使用以下帮助程序类:
public class EffectStyleLookup
{
public EffectStyle Item { get; set; }
public string Display { get; set; }
}
在ViewModel中,转换枚举列表并将其公开为属性:
public ViewModel : ReactiveObject
{
private ReactiveList<EffectStyleLookup> _effectStyles;
public ReactiveList<EffectStyleLookup> EffectStyles
{
get { return _effectStyles; }
set { this.RaiseAndSetIfChanged(ref _effectStyles, value); }
}
// See below for more on this
private EffectStyle _selectedEffectStyle;
public EffectStyle SelectedEffectStyle
{
get { return _selectedEffectStyle; }
set { this.RaiseAndSetIfChanged(ref _selectedEffectStyle, value); }
}
public ViewModel()
{
// Convert a list of enums into a ReactiveList
var list = (IList<EffectStyle>)Enum.GetValues(typeof(EffectStyle))
.Select( x => new EffectStyleLookup() {
Item = x,
Display = x.ToString()
});
EffectStyles = new ReactiveList<EffectStyle>( list );
}
}
在中ComboBox
,利用SelectedValuePath
属性绑定到原始enum
值:
<ComboBox Name="EffectStyle" DisplayMemberPath="Display" SelectedValuePath="Item" />
在View中,这使我们可以将原始对象绑定enum
到SelectedEffectStyle
ViewModel中,但是在中显示ToString()
值ComboBox
:
this.WhenActivated( d =>
{
d( this.OneWayBind(ViewModel, vm => vm.EffectStyles, v => v.EffectStyle.ItemsSource) );
d( this.Bind(ViewModel, vm => vm.SelectedEffectStyle, v => v.EffectStyle.SelectedValue) );
});
我要添加我的评论(可悲的是,在VB中,但是这个概念很容易在心跳中复制到C#中),因为我只需要引用此内容,并且不喜欢任何答案,因为它们太复杂了。不必那么困难。
所以我想出了一种简单的方法。将枚举器绑定到字典。将该字典绑定到组合框。
我的组合框:
<ComboBox x:Name="cmbRole" VerticalAlignment="Stretch" IsEditable="False" Padding="2"
Margin="0" FontSize="11" HorizontalAlignment="Stretch" TabIndex="104"
SelectedValuePath="Key" DisplayMemberPath="Value" />
我的后台代码。希望这可以帮助其他人。
Dim tDict As New Dictionary(Of Integer, String)
Dim types = [Enum].GetValues(GetType(Helper.Enumerators.AllowedType))
For Each x As Helper.Enumerators.AllowedType In types
Dim z = x.ToString()
Dim y = CInt(x)
tDict.Add(y, z)
Next
cmbRole.ClearValue(ItemsControl.ItemsSourceProperty)
cmbRole.ItemsSource = tDict
尼克的解决方案可以进一步简化,没有花哨的事情,您只需要一个转换器即可:
[ValueConversion(typeof(Enum), typeof(IEnumerable<Enum>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var r = Enum.GetValues(value.GetType());
return r;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
然后,您可以在希望出现组合框的任何地方使用它:
<ComboBox ItemsSource="{Binding PagePosition, Converter={converter:EnumToCollectionConverter}, Mode=OneTime}" SelectedItem="{Binding PagePosition}" />
我不建议直接执行此操作,但希望这可以激发一个好的解决方案。
假设您的枚举是Foo。然后,您可以执行类似的操作。
public class FooViewModel : ViewModel
{
private int _fooValue;
public int FooValue
{
get => _fooValue;
set
{
_fooValue = value;
OnPropertyChange();
OnPropertyChange(nameof(Foo));
OnPropertyChange(nameof(FooName));
}
}
public Foo Foo
{
get => (Foo)FooValue;
set
{
_fooValue = (int)value;
OnPropertyChange();
OnPropertyChange(nameof(FooValue));
OnPropertyChange(nameof(FooName));
}
}
public string FooName { get => Enum.GetName(typeof(Foo), Foo); }
public FooViewModel(Foo foo)
{
Foo = foo;
}
}
然后在Window.Load
方法上,您可以将所有枚举加载到ObservableCollection<FooViewModel>
,您可以将其设置为组合框的DataContext。
我只是保持简单。我在ViewModel中创建了带有枚举值的项目列表:
public enum InputsOutputsBoth
{
Inputs,
Outputs,
Both
}
private IList<InputsOutputsBoth> _ioTypes = new List<InputsOutputsBoth>()
{
InputsOutputsBoth.Both,
InputsOutputsBoth.Inputs,
InputsOutputsBoth.Outputs
};
public IEnumerable<InputsOutputsBoth> IoTypes
{
get { return _ioTypes; }
set { }
}
private InputsOutputsBoth _selectedIoType;
public InputsOutputsBoth SelectedIoType
{
get { return _selectedIoType; }
set
{
_selectedIoType = value;
OnPropertyChanged("SelectedIoType");
OnSelectionChanged();
}
}
在我的xaml代码中,我只需要这样:
<ComboBox ItemsSource="{Binding IoTypes}" SelectedItem="{Binding SelectedIoType, Mode=TwoWay}">