したがって、WebとStackOverflowには、コンボボックスをWPFの列挙型プロパティにバインドする方法について多くの素晴らしい答えがあります。しかし、Silverlightには、これを可能にするすべての機能がありません:(。次に例を示します。
x:Type
をサポートしていないため、型パラメーターを受け入れる一般的な EnumDisplayer
-style IValueConverter
は使用できません。ObjectDataProvider
は、Silverlightには存在しないため、 このアプローチ のように使用できません。Type
プロパティの代わりにジェネリックを使用して#1のバージョンを実行することはできません。ジェネリックはXAMLでサポートされていないためです(そして、それらを機能させるためのハックは、すべてでサポートされていないマークアップ拡張機能に依存しますSilverlight)。大失敗!
私が見ているように、この作品を作る唯一の方法は、
IValueConverter
を作成します。より一般的な代替手段はありますか?つまり、必要な列挙型ごとに同じコードを何度も書く必要はありませんか?列挙型を型パラメーターとして受け入れるジェネリッククラスを使用してソリューション#2を実行し、必要な列挙型ごとに新しいクラスを作成することができると思います
class MyEnumConverter : GenericEnumConverter<MyEnum> {}
皆さん、どう思いますか?
ああ、私はあまりにも早く話しました! 完全に優れた解決策 があります。少なくともSilverlight 3にあります(- this thread は、Silverlightでこの問題に関連するバグが修正されたことを示しているため、3にしか存在しない可能性があります) 3.)
基本的に、ItemsSource
プロパティには単一のコンバーターが必要ですが、タイプがMyEnum
であるプロパティの名前を渡せば、禁止されているメソッドを使用せずに完全に汎用的にすることができます。そして、SelectedItem
へのデータバインディングは完全に簡単です。コンバーターは必要ありません!まあ、少なくともそれはあなたが各列挙値にカスタム文字列を必要としない限りです。 DescriptionAttribute
、hmm ...はおそらくそのための別のコンバーターが必要になります。私がそれをジェネリックにできることを願っています。
更新:私はコンバーターを作りました、そしてそれは動作します!悲しいことに、SelectedIndex
にバインドする必要がありますが、問題ありません。これらの人を使用してください:
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:
<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>
少しXAML/Silverlight/WPF /などなので、コメントをいただければ幸いです。初心者。たとえば、EnumToIntConverter.ConvertBack
遅いので、キャッシュの使用を検討する必要がありますか?
選択したアイテムのカスタムコンバーターを必要とせずにComboBoxを列挙型にバインドする別の方法があります。あなたはそれをチェックすることができます
http://charlass.wordpress.com/2009/07/29/binding-enums-to-a-combobbox-in-silverlight/
それはDescriptionAttributesを使用しません....しかし、それは私にとっては完璧に機能するので、使用されるシナリオに依存すると思います
Enumデータを単純にカプセル化する方が使いやすいことがわかりました。
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}" />
そして、これはコンバーターの必要性を完全に取り除きます... :)
これは、Windows 8.1/Windows Phoneユニバーサルアプリと同じ設定です。主な変更点は次のとおりです。
XAMLの順序も重要なようです。ItemsSourceをSelectedIndexの前に配置する必要がありました。そうしないと、ItemsSourceバインディングが呼び出されませんでした。
<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;
}
}
}