因此,Web和StackOverflow对于如何将组合框绑定到WPF中的枚举属性有很多很好的答案。但Silverlight缺少使这成为可能的所有功能:(例如:
EnumDisplayer
- 样式IValueConverter
,因为Silverlight不支持x:Type
。ObjectDataProvider
,因为它在Silverlight中不存在。Type
属性来执行#1版本,因为XAML中不支持泛型(并且使它们工作的hacks全部依赖于标记扩展,而不是Silverlight支持。)大规模失败!
正如我所看到的,实现这项工作的唯一方法是
IValueConverter
。是否有更通用的替代方案,即不要为我想要的每个枚举反复编写相同的代码?我想我可以使用一个接受enum作为类型参数的泛型类来做解决方案#2,然后为我想要的每个枚举创建新类
class MyEnumConverter : GenericEnumConverter<MyEnum> {}
你有什么想法?伙计们?
答案 0 :(得分:34)
基本上,你需要一个ItemsSource
属性的转换器,但它可以完全通用而不使用任何禁止的方法,只要你传递一个类型为{{1的属性的名称}}。对MyEnum
的数据绑定完全没有痛苦;不需要转换器!好吧,至少只要您不想通过例如每个枚举值自定义字符串。 SelectedItem
,嗯...可能需要另一个转换器;希望我能把它变成通用的。
更新:我制作了转换器,它可以正常工作!遗憾的是,我现在必须绑定到DescriptionAttribute
,但是没关系。使用这些家伙:
SelectedIndex
使用这种绑定XAML:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;
namespace DomenicDenicola.Wpf
{
public class EnumToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// Note: as pointed out by Martin in the comments on this answer, this line
// depends on the enum values being sequentially ordered from 0 onward,
// since combobox indices are done that way. A more general solution would
// probably look up where in the GetValues array our value variable
// appears, then return that index.
return (int)value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Enum.Parse(targetType, value.ToString(), true);
}
}
public class EnumToIEnumerableConverter : IValueConverter
{
private Dictionary<Type, List<object>> cache = new Dictionary<Type, List<object>>();
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var type = value.GetType();
if (!this.cache.ContainsKey(type))
{
var fields = type.GetFields().Where(field => field.IsLiteral);
var values = new List<object>();
foreach (var field in fields)
{
DescriptionAttribute[] a = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (a != null && a.Length > 0)
{
values.Add(a[0].Description);
}
else
{
values.Add(field.GetValue(value));
}
}
this.cache[type] = values;
}
return this.cache[type];
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
这种资源声明XAML:
<ComboBox x:Name="MonsterGroupRole"
ItemsSource="{Binding MonsterGroupRole,
Mode=OneTime,
Converter={StaticResource EnumToIEnumerableConverter}}"
SelectedIndex="{Binding MonsterGroupRole,
Mode=TwoWay,
Converter={StaticResource EnumToIntConverter}}" />
任何评论都会受到赞赏,因为我有点像XAML / Silverlight / WPF /等。新手。例如,<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ddwpf="clr-namespace:DomenicDenicola.Wpf">
<Application.Resources>
<ddwpf:EnumToIEnumerableConverter x:Key="EnumToIEnumerableConverter" />
<ddwpf:EnumToIntConverter x:Key="EnumToIntConverter" />
</Application.Resources>
</Application>
会慢,所以我应该考虑使用缓存吗?
答案 1 :(得分:4)
还有另一种方法可以将ComboBox绑定到枚举,而无需为所选项目定制转换器。你可以在
查看http://charlass.wordpress.com/2009/07/29/binding-enums-to-a-combobbox-in-silverlight/
它没有使用DescriptionAttributes ....但它对我来说非常合适,所以我想这取决于它将被使用的场景
答案 2 :(得分:4)
我发现枚举数据的简单封装更容易使用。
public ReadOnly property MonsterGroupRole as list(of string)
get
return [Enum].GetNames(GetType(GroupRoleEnum)).Tolist
End get
End Property
private _monsterEnum as GroupRoleEnum
Public Property MonsterGroupRoleValue as Integer
get
return _monsterEnum
End get
set(value as integer)
_monsterEnum=value
End set
End Property
...
<ComboBox x:Name="MonsterGroupRole"
ItemsSource="{Binding MonsterGroupRole,
Mode=OneTime}"
SelectedIndex="{Binding MonsterGroupRoleValue ,
Mode=TwoWay}" />
这将完全取消对转换器的需求......:)
答案 3 :(得分:0)
以下是Windows 8.1 / Windows Phone通用应用程序的相同设置,主要更改如下: -
似乎XAML的顺序也很重要,我不得不将ItemsSource放在SelectedIndex之前,否则它不会调用ItemsSource绑定 e.g。
<ComboBox
ItemsSource="{Binding Path=MyProperty,Mode=OneWay, Converter={StaticResource EnumToIEnumerableConverter}}"
SelectedIndex="{Binding Path=MyProperty, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}"
/>
以下代码
namespace MyApp.Converters
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Windows.UI.Xaml.Data;
public class EnumToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
// Note: as pointed out by Martin in the comments on this answer, this line
// depends on the enum values being sequentially ordered from 0 onward,
// since combobox indices are done that way. A more general solution would
// probably look up where in the GetValues array our value variable
// appears, then return that index.
return (int) value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value;
}
}
public class EnumToIEnumerableConverter : IValueConverter
{
private readonly Dictionary<TypeInfo, List<object>> _cache = new Dictionary<TypeInfo, List<object>>();
public object Convert(object value, Type targetType, object parameter, string language)
{
var type = value.GetType().GetTypeInfo();
if (!_cache.ContainsKey(type))
{
var fields = type.DeclaredFields.Where(field => field.IsLiteral);
var values = new List<object>();
foreach (var field in fields)
{
var a = (DescriptionAttribute[]) field.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (a != null && a.Length > 0)
{
values.Add(a[0].Description);
}
else
{
values.Add(field.GetValue(value));
}
}
_cache[type] = values;
}
return _cache[type];
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
[AttributeUsage(AttributeTargets.Field)]
public class DescriptionAttribute : Attribute
{
public string Description { get; private set; }
public DescriptionAttribute(string description)
{
Description = description;
}
}
}