C# 改进枚举到组合框绑定(使用MarkupExtension)

C# 改进枚举到组合框绑定(使用MarkupExtension),c#,wpf,binding,member,C#,Wpf,Binding,Member,我试图最大限度地简化枚举到组合框的绑定 在多个解决方案(ObjectDataProvider、Converter…)中,我选择了以下MarkupExtension: public class EnumSource : MarkupExtension { public class EnumMember { public string Display { get; set; } public object Value { get; set; }

我试图最大限度地简化枚举到组合框的绑定

在多个解决方案(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" />
公共类枚举源:MarkupExtension
{
公共类枚举成员
{
公共字符串显示{get;set;}
公共对象值{get;set;}
公共重写字符串ToString()
{
返回显示;
}
}
私有只读类型EnumType;
公共枚举源(类型)
{
枚举类型=类型;
}
公共覆盖对象ProviderValue(IServiceProvider服务提供程序)
{
var enumValues=Enum.GetValues(EnumType);
返回(
从枚举值中的对象枚举值
选择新成员
{
值=枚举值,
Display=GetDescription(枚举值)
}).ToArray();
}
私有字符串GetDescription(对象枚举值)
{
变量descriptionAttribute=EnumType
.GetField(enumValue.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute),false)
.FirstOrDefault()作为描述属性;
return(descriptionAttribute!=null)?descriptionAttribute.Description:enumValue.ToString();
}
}
您可以注意到,我通过在EnumMember类中添加一个ToString()方法,成功地摆脱了DisplayMemberPath=“Display”

是否可以将SelectedValuePath=“Value”属性替换为类运算符(位于EnumMember内部)或类似的内容


谢谢

这是不可能的,因为SelectedValue根据SelectedValuePath中提到的属性从SelectedItem中提取特定属性的值。这意味着SelectedValue和SelectedValuePath一起工作。但是,如果您想在不提及SlectedValuePath的情况下执行此操作,请使用SelectedItem属性进行绑定

更新这是ViewModel中的一个属性

xaml


实现这一目标的方法有很多:

方法1 标记扩展的
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();
}
    <ComboBox ItemsSource="{my:EnumSource {x:Type my:Options}}"
              SelectedValue="{Binding CurrentOption}"/>

方法2 如果应用程序中的所有组合框都将绑定到包装类
EnumMember
,您可以在Application.Resources下的组合框的
全局样式下定义它,这样您就不必为每个组合框复制它

即使在这种情况下,您并没有将ComboBox绑定到枚举值,SelectedValuePath也可以覆盖每个实例

<Application.Resources>
   <Style TargetType="ComboBox">
      <Setter Property="SelectedValuePath" Value="Value"/>
   </Style>
</Application.Resources>
XAML中的用法:

<my:MyComboBox ItemsSource="{my:EnumSource {x:Type my:Options}}"
               SelectedValue="{Binding Path=CurrentOption}"/>

谢谢你的解释,虽然我没有设法让它与SelectedItem绑定一起工作。如何指定确切的路径?我更新了答案。请参阅更新我尝试了它并为我工作。只需在ViewModel中选择EdItem.value即可获得该值。
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);
    }
}
<my:MyComboBox ItemsSource="{my:EnumSource {x:Type my:Options}}"
               SelectedValue="{Binding Path=CurrentOption}"/>
    <ComboBox ItemsSource="{my:EnumSource {x:Type my:Options}}"
              SelectedValue="{Binding CurrentOption}"/>