public enum IVAC_VT_SHIFT_SPD
{
[Description("16.1299991607666")]
DATA16,
[Description("32.130001068115234")]
DATA32,
}
<ComboBox Grid.Row="8"
Grid.Column="2"
Width="194"
Height="40"
Margin="0,0,3,0"
HorizontalAlignment="Right"
VerticalContentAlignment="Center"
ItemsSource="{Binding Source={StaticResource IVAC_VT_SHIFT_SPD}}"
SelectedValue="{Binding VTShiftSPD}" />
<ObjectDataProvider x:Key="IVAC_VT_SHIFT_SPD"
MethodName="GetValues"
ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="enm:IVAC_VT_SHIFT_SPD" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
private IVAC_VT_SHIFT_SPD _VTShiftSPD = IVAC_VT_SHIFT_SPD.DATA16;
public IVAC_VT_SHIFT_SPD VTShiftSPD
{
get => _VTShiftSPD;
set
{
_VTShiftSPD = value;
OnPropertyChanged(nameof(VTShiftSPD));
}
}
我想要的是将枚举类型值绑定到组合框。但是,我希望组合框的内容显示为说明的内容。该选择必须选择为枚举。
我认为您可以非常接近地实现您正在寻找的目标。我确实使用了几个 C# 类来完成它,但这些类将适用于未来的任何枚举,而无需您不断创建其他类或代码来方便在下拉列表中获取描述。
在我的示例中,我能够产生以下结果
public enum IVAC_VT_SHIFT_SPD
{
[Description("16.1299991607666")]
DATA16,
[Description("32.130001068115234")]
DATA32,
}
<Window ...
xmlns:local="clr-namespace:StackOverflowAnswers.Wpf"
...>
<ComboBox x:Name="Combo"
ItemsSource="{Binding Source={enums:EnumBindingSource {x:Type enums:IVAC_VT_SHIFT_SPD}}}"
DisplayMemberPath="Description"
SelectedValuePath="Value" />
为了促进这一点,我使用了此链接中概述的方法的改编版:https://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/。如果您直接遵循这一点,则无需枚举的 ViewModel 即可获得非常相似的结果。然而,我喜欢使用 ViewModel,因为它允许我根据需要调整一些东西,比如添加对图标属性的支持。
在我的示例中,我创建了一个
EnumViewModel
类来容纳组合框中将显示的内容:
public class EnumViewModel
{
public EnumViewModel(Enum value)
{
Value = value;
Description = getDescription(value);
}
public Enum Value { get; }
public string Description { get; }
private string getDescription(Enum value)
{
if (!(value?.GetType().GetField(value?.ToString()) is FieldInfo enumField))
// value is null...
return string.Empty;
var descriptionAttribute = enumField.GetCustomAttributes(typeof(DescriptionAttribute), false)
.OfType<DescriptionAttribute>()
.FirstOrDefault();
if (descriptionAttribute is null || string.IsNullOrEmpty(descriptionAttribute.Description))
// description attribute is missing or blank...
return value.ToString();
return descriptionAttribute.Description;
}
}
还有一个
MarkupExtension
允许我直接绑定到它,而不必将其包含在视图模型或后面的代码中。这设置了使用 Source={enums:EnumBindingSource ...}
的能力。
public class EnumBindingSourceExtension : MarkupExtension
{
private Type _enumType;
public Type EnumType
{
get => this._enumType;
set
{
if (value == this._enumType) return;
if (!isEnumType(value)) throw new ArgumentException("Type must be for an Enum.");
this._enumType = value;
}
}
private bool isEnumType(Type type)
{
if (type is null) return false;
var enumType = Nullable.GetUnderlyingType(type) ?? type;
return enumType.IsEnum;
}
public EnumBindingSourceExtension() { }
public EnumBindingSourceExtension(Type enumType)
{
this.EnumType = enumType;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (this._enumType is null)
throw new InvalidOperationException("The EnumType must be specified.");
var actualEnumType = Nullable.GetUnderlyingType(this._enumType) ?? this._enumType;
var enumValues = Enum.GetValues(actualEnumType)
.OfType<Enum>()
.Select(x => new EnumViewModel(x))
.ToList();
return enumValues;
}
}
枚举不应该显示在 UI 中。枚举是整数的简单枚举,其中每个值都有一个关联的名称。
您的场景根本超出了
enum
类型的意图。
如果您想定义一组常量,其中每个常量携带数据,那么您应该定义一个类型(
class
或 struct
)。这也将为您在视图中呈现数据提供更大的灵活性,而无需转换器和反射的额外开销。您最终会得到更干净、自然/直观的代码。在您的场景中使用 enum
并不优雅,也没有任何好处。 enum
必须具有代表性,属性或表中不存储任何元数据。
如果
enum
本身确实有意义,您甚至可以考虑使用此自定义类型来定义 enum
的属性 - 仅当该类型不是 enum
的扩展(用于携带元数据)时才会出现这种情况数据),因为 enum
值必须能够独立,即在没有元数据的情况下使用。
例如,如果您创建一个枚举
Color
,其中包含 Color.Red
等值,并且如果没有实际的红色值,则无法使用这些值,那么 Color.Red
不应该是枚举,而应该是 class
或 struct
能够定义该元数据。
建议的解决方案如下所示:
VTShiftSPD.cs
// TODO::Implement IEquatable<VTShiftSPD> (don't forget to override GetHashCode)
public readonly struct VTShiftSPD : IEquatable<VTShiftSPD>
{
public const string Data16 = "DATA16";
public const string Data32 = "DATA32";
public VTShiftSPD(string id, double value)
{
this.Id = id;
this.Value = value;
}
public string Id { get; }
public double Value { get; }
}
您可以使用和比较它,例如如下(没有任何反映!):
static Main()
{
var vTShiftSPD = new VTShiftSPD(VTShiftSPD.Data16, 16.1299991607666);
Evaluate(vTShiftSPD);
}
private static void Evaluate(VTShiftSPD vTShiftSPD)
{
switch (vTShiftSPD.Id)
{
case VTShiftSPD.Data16: break;
case VTShiftSPD.Data32: break;
default: break;
}
}
如果从
VTShiftSPD
创建的实例集不是开放集,您甚至可以将它们本身定义为常量:
VTShiftSPDs.cs
static class VTShiftSPDs
{
public static readonly VTShiftSPD Spd16 = new VTShiftSPD(VTShiftSPD.Data16, 16.1299991607666);
public static readonly VTShiftSPD Spd32 = new VTShiftSPD(VTShiftSPD.Data32, 32.130001068115234);
}
并按如下方式使用它:
static Main()
{
Evaluate(VTShiftSPDs.Spd16);
}
private static void Evaluate(VTShiftSPD vTShiftSPD)
{
switch (vTShiftSPD.Id)
{
case VTShiftSPD.Data16: break;
case VTShiftSPD.Data32: break;
default: break;
}
}
并优雅(且高效)地使用它来填充数据视图:
MainWindow.xaml.cs
// The data source for the ComboBox.ItemsSource
public ObservableCollection<VTShiftSPD> VTShiftSPDs { get; }
= new ObservableCollection<VTShiftSPD>
{
VTShiftSPDs.Spd16,
VTShiftSPDs.Spd32,
};
MainWindow.xaml
<Window>
<StackPanel>
<ComboBox x:Name="VTShiftSPDSelector"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType Window}, Path=VTShiftSPDs}"
DisplayMemberPath="Value" />
<Stackpanel DataContext="{Binding Elementname=VTShiftSPDSelector, Path=SelectedItem}>
<TextBlock x:Name="Id"
Text{Binding Id}" /> <!-- Displays e.g. DATA16 -->
<TextBlock x:Name="Value"
Text{Binding Value}" /> <!-- Displays e.g. 16.1299991607666 -->
</StackPanel>
</StackPanel>
</Window>