私はバインドしているすべてのオブジェクトをmy ViewModel
で定義するのが好きなので<ObjectDataProvider>
、可能な場合はxamlでの使用を避けます。
私のソリューションでは、ビューで定義されたデータも分離コードも使用していません。DataBinding、再利用可能なValueConverter、任意のEnumタイプの説明のコレクションを取得するメソッド、およびバインドするViewModelの単一のプロパティのみ。
私はバインドしたいときEnum
にComboBox
、私が表示したいテキストをの値と一致したことがないEnum
ので、私は使用し[Description()]
それを私が実際に見たいというテキストを与える属性をComboBox
。曜日が列挙されている場合、次のようになります。
public enum DayOfWeek
{
// add an optional blank value for default/no selection
[Description("")]
NOT_SET = 0,
[Description("Sunday")]
SUNDAY,
[Description("Monday")]
MONDAY,
...
}
最初に、列挙型を処理するためのいくつかのメソッドを含むヘルパークラスを作成しました。1つのメソッドは特定の値の説明を取得し、もう1つのメソッドはすべての値と型のそれらの説明を取得します。
public static class EnumHelper
{
public static string Description(this Enum value)
{
var attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Any())
return (attributes.First() as DescriptionAttribute).Description;
// If no description is found, the least we can do is replace underscores with spaces
// You can add your own custom default formatting logic here
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " ")));
}
public static IEnumerable<ValueDescription> GetAllValuesAndDescriptions(Type t)
{
if (!t.IsEnum)
throw new ArgumentException($"{nameof(t)} must be an enum type");
return Enum.GetValues(t).Cast<Enum>().Select((e) => new ValueDescription() { Value = e, Description = e.Description() }).ToList();
}
}
次に、を作成しValueConverter
ます。から継承MarkupExtension
すると、XAMLでの使用が容易になるため、リソースとして宣言する必要がありません。
[ValueConversion(typeof(Enum), typeof(IEnumerable<ValueDescription>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return EnumHelper.GetAllValuesAndDescriptions(value.GetType());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
ぼくの ViewModel
とコンボボックスのView
両方にバインドできる1つのプロパティが必要です:SelectedValue
ItemsSource
private DayOfWeek dayOfWeek;
public DayOfWeek SelectedDay
{
get { return dayOfWeek; }
set
{
if (dayOfWeek != value)
{
dayOfWeek = value;
OnPropertyChanged(nameof(SelectedDay));
}
}
}
そして最後にバインドしますComboBox
(使用してビューValueConverter
にItemsSource
結合するのを)...
<ComboBox ItemsSource="{Binding Path=SelectedDay, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectedValue="{Binding Path=SelectedDay}" />
このソリューションを実装するには、EnumHelper
クラスとEnumToCollectionConverter
クラスをコピーするだけです。彼らはすべての列挙型で動作します。また、私はそれをここに含まれていませんでしたが、ValueDescription
クラスは2つのパブリックオブジェクトのプロパティを持つ単純なクラスと呼ばれるものですValue
、と呼ばれる1 Description
。あなたはそれを自分で作成することができたり、あなたが使用するコードを変更することができますTuple<object, object>
かKeyValuePair<object, object>