我正在努力简化Enums与ComboBoxes的绑定。
在多个解决方案(ObjectDataProvider,Converter ...)中,我选择了以下MarkupExtension:
public class EnumSource : MarkupExtension
{
public class EnumMember
{
public string Display { get; set; }
public object Value { get; set; }
public override string ToString()
{
return Display;
}
}
private readonly Type EnumType;
public EnumSource(Type type)
{
EnumType = type;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var enumValues = Enum.GetValues(EnumType);
return (
from object enumValue in enumValues
select new EnumMember
{
Value = enumValue,
Display = GetDescription(enumValue)
}).ToArray();
}
private string GetDescription(object enumValue)
{
var descriptionAttribute = EnumType
.GetField(enumValue.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.FirstOrDefault() as DescriptionAttribute;
return (descriptionAttribute != null) ? descriptionAttribute.Description : enumValue.ToString();
}
}
<ComboBox ItemsSource="{Binding Source={my:EnumSource {x:Type my:Options}}}" SelectedValue="{Binding Path=CurrentOption}" SelectedValuePath="Value" />
您可以注意到我设法通过在EnumMember类中添加ToString()方法来摆脱 DisplayMemberPath =“显示”。
是否可以用类操作符(在EnumMember中)或类似的东西替换 SelectedValuePath =“Value”属性?
谢谢!
答案 0 :(得分:4)
有很多方法可以实现这一目标:
标记扩展的ProvideValue
方法采用IServiceProvider
类型的参数,该参数提供IProvideValueTarget
服务等。此接口公开属性TargetObject
,允许检索目标对象(在您的情况下为组合框)。
您可以像这样设置SelectedValuePath
:
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
if (target != null && target.TargetObject is ComboBox)
{
((ComboBox)target.TargetObject).SelectedValuePath = "Value";
}
var enumValues = Enum.GetValues(EnumType);
return (
from object enumValue in enumValues
select new EnumMember
{
Value = enumValue,
Display = GetDescription(enumValue)
}).ToArray();
}
如果你的应用中的所有组合框都被绑定到包装类EnumMember
,你可以在global style of ComboBox under Application.Resources
下定义它,这样你就不必为每个组合框复制它。
即使以防万一,你没有将ComboBox绑定到枚举值,也可以为每个实例覆盖SelectedValuePath。
<Application.Resources>
<Style TargetType="ComboBox">
<Setter Property="SelectedValuePath" Value="Value"/>
</Style>
</Application.Resources>
你可以继承ComboBox和OnItemsSourceChanged
,设置SelectedValuePath to Value
,以防 ItemsSource是EnumMember类的数组。
public class MyComboBox : ComboBox
{
protected override void OnItemsSourceChanged(IEnumerable oldValue,
IEnumerable newValue)
{
if (newValue != null &&
newValue.GetType().Equals(typeof(EnumSource.EnumMember[])))
{
SelectedValuePath = "Value";
}
base.OnItemsSourceChanged(oldValue, newValue);
}
}
在XAML中的用法:
<my:MyComboBox ItemsSource="{my:EnumSource {x:Type my:Options}}"
SelectedValue="{Binding Path=CurrentOption}"/>
在旁注中,您创建了自己的标记扩展,您根本不需要Binding
。您可以像这样绑定ItemsSource
:
<ComboBox ItemsSource="{my:EnumSource {x:Type my:Options}}"
SelectedValue="{Binding CurrentOption}"/>
答案 1 :(得分:1)
不可能这样做是因为 SelectedValue 根据 SelectedValuePath 中提到的属性从 SelectedItem 中提取特定属性的值.Means SelectedValue和SelectedValuePath一起工作。但是,如果您想在不提及SlectedValuePath的情况下执行此操作,请使用 SelectedItem 属性进行绑定。
更新这是ViewModel中的属性
EnumSource.EnumMember selectedItem;
public EnumSource.EnumMember SelectedItem
{
get{return selectedItem;}
set { selectedItem = value; OnPropertyChanged("SelectedItem");}
}
XAML
<ComboBox SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding Source={local:EnumSource {x:Type local:DataType}}}" SelectedValue="{Binding Path=CurrentOption}"/>