可以在WPF应用程序中使用ObjectDataProvider
将枚举的字符串值绑定到ComboBox的ItemsSource,如此问题所示。
然而,在UWP应用程序中使用类似的片段时,会显示以下错误消息:
"在Windows通用项目中不支持ObjectDataProvider。"
在UWP中有简单的替代方法吗?
以下是我其中一个原型的工作示例。
枚举
public enum GetDetails
{
test1,
test2,
test3,
test4,
test5
}
项目源
var _enumval = Enum.GetValues(typeof(GetDetails)).Cast<GetDetails>();
cmbData.ItemsSource = _enumval.ToList();
这将把下拉框绑定到枚举值。
<ComboBox ItemsSource="{Binding ...}" SelectedItem="{Binding ...}"/>
相信我,在 UWP 中使用 ComboBox 和枚举是一个坏主意。节省时间,不要在 UWP 的 ComboBox 中使用枚举。我花了几个小时试图使其工作。你可以尝试其他答案中提到的解决方案,但你会遇到一个问题,即当 SelectedValue 绑定到枚举时,属性更改将不会触发。因此,我只是将它转换为 int。
你可以在 VM 中创建一个属性,并将 GetDetails 枚举强制转换为 int。
public int Details
{
get { return (int)Model.Details; }
set { Model.Details = (GetDetails)value; OnPropertyChanged();}
}
那么您可以使用包含整数和字符串的类列表进行工作,不确定是否可以使用KeyValuePair。
public class DetailItem
{
public int Value {get;set;}
public string Text {get;set;}
}
public IEnumerable<DetailItem> Items
{
get { return GetItems(); }
}
public IEnumerable<DetailItem> GetItems()
{
yield return new DetailItem() { Text = "Test #1", Value = (int)GetDetails.test1 };
yield return new DetailItem() { Text = "Test #2", Value = (int)GetDetails.test2 };
yield return new DetailItem() { Text = "Test #3", Value = (int)GetDetails.test3 };
// ..something like that
}
然后在 Xaml 中进行操作
<Combobox ItemsSource="{Binding Items, UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding Details, UpdateSourceTriggerPropertyChanged, Mode=TwoWay}"
SelectedValuePath="Value"
DisplayMemberPath="Text" />
这是我为UWP想到的最简单、最通用的枚举绑定方法。也许这可以帮助一些人,因为这种方法容易绑定和本地化。
/// <summary>
/// Helper class to bind an Enum type as an control's ItemsSource.
/// </summary>
/// <typeparam name="T"></typeparam>
public class EnumItemsSource<T> where T : struct, IConvertible
{
public string FullTypeString { get; set; }
public string Name { get; set; }
public string LocalizedName { get; set; }
public T Value { get; set; }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <param name="fullString"></param>
public EnumItemsSource(string name, T value, string fullTypeString)
{
if (!typeof(T).IsEnum)
throw new ArgumentException("EnumItemsSource only accept Enum type.");
Name = name;
Value = value;
FullTypeString = fullTypeString;
// Retrieve localized name
LocalizedName = AppResource.GetResource(FullTypeString.Replace('.', '-'));
}
/// <summary>
/// Create a list of EnumItemsSource from an enum type.
/// </summary>
/// <returns></returns>
public static List<EnumItemsSource<T>> ToList()
{
// Put to lists
var namesList = Enum.GetNames(typeof(T));
var valuesList = Enum.GetValues(typeof(T)).Cast<T>().ToList();
// Create EnumItemsSource list
var enumItemsSourceList = new List<EnumItemsSource<T>>();
for (int i = 0; i < namesList.Length; i++)
enumItemsSourceList.Add(new EnumItemsSource<T>(namesList[i], valuesList[i], $"{typeof(T).Name}.{namesList[i]}"));
return enumItemsSourceList;
}
}
public List<EnumItemsSource<AccountType>> AccountTypes
{
get => EnumItemsSource<AccountType>.ToList();
}
在视图中:
<ComboBox ItemsSource="{Binding AccountTypes}" DisplayMemberPath="LocalizedName" SelectedValuePath="Value" SelectedValue="{Binding Path=Account.Type, Mode=TwoWay}" />
我知道这是一篇旧文章,但是你可以将下拉框的SelectedIndex绑定到枚举属性上,并像下面这样定义你的值转换器:
public object Convert(object value, Type targetType, object parameter, string language)
{
string v = value.ToString();
string[] vs = Enum.GetNames(typeof(YourEnumType));
int index = vs.IndexOf(v);
if (index > -1)
return index;
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
int index = (int)value;
if (index > -1)
return (YourEnumType)index;
return YourEnumType.DefaultValue;
}
ComboBox
使用ItemSource
绑定到枚举,同时使用SelectedItem
。并且提供选项以替换枚举名称为自定义字符串(例如翻译)。遵循MVVM模式。
枚举:
public enum ChannelMark
{
Undefinned,Left, Right,Front, Back
}
视图模型
private ChannelMark _ChannelMark = ChannelMark.Undefinned;
public ChannelMark ChannelMark
{
get => _ChannelMark;
set => Set(ref _ChannelMark, value);
}
private List<int> _ChannelMarksInts = Enum.GetValues(typeof(ChannelMark)).Cast<ChannelMark>().Cast<int>().ToList();
public List<int> ChannelMarksInts
{
get => _ChannelMarksInts;
set => Set(ref _ChannelMarksInts, value);
}
XAML
<ComboBox ItemsSource="{x:Bind ViewModel.ChannelMarksInts}" SelectedItem="{x:Bind ViewModel.ChannelMark, Converter={StaticResource ChannelMarkToIntConverter}, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource IntToChannelMarkConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
转换器:
switch ((ChannelMark)value)
{
case ChannelMark.Undefinned:
return "Undefinned mark";
case ChannelMark.Left:
//translation
return Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView().GetString("ChannelMarkEnumLeft");
case ChannelMark.Right:
return "Right Channel";
case ChannelMark.Front:
return "Front Channel";
case ChannelMark.Back:
return "Back Channel";
default:
throw new NotImplementedException();
}
public class IntToChannelMarkConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language) => ((ChannelMark)value).ToString();
public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException();
}
我选择了Karnalta的答案,因为它支持使用resx文件进行简单的本地化。但是我在从实体到控件和从控件回到实体时遇到了问题。我通过改变绑定方式,使用SelectedIndex并添加转换器来解决了这个问题。这样,当显示时,下拉框可以正确地获取初始状态。
ViewModel与Karnalta的解决方案保持不变。
在视图中更改绑定:
<ComboBox ItemsSource="{Binding AccountTypes}" DisplayMemberPath="LocalizedName" SelectedIndex={x:Bind ViewModel.Account.Type, Mode=TwoWay, Converter={StaticResource EnumToIntConverer}" />
public class EnumToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return (int)value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return Enum.Parse(targetType, value.ToString());
}
}
别忘了在你的页面/控件中添加转换器:
<Grid.Resources>
<ResourceDictionary>
<converters:EnumToIntConverter x:Key="EnumToIntConverter" />
</ResourceDictionary>
</Grid.Resources>
NavigationViewPaneDisplayMode
枚举填充的ComboBox
。public class EnumOptions<T> : ReadOnlyCollection<T> where T : Enum
{
public EnumOptions() : this(Enum.GetValues(typeof(T)).Cast<T>().ToArray())
{
}
private EnumOptions(IList<T> list) : base(list)
{
}
}
EnumOptions<T>
。public class NavigationViewPaneDisplayModeOptions : EnumOptions<NavigationViewPaneDisplayMode>
{
}
public partial class EnumCamelCaseToSpacedStringConverter : IValueConverter
{
public bool OnlyTheFirstWordFirstCharacterIsUpperCase { get; set; }
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value?.ToString() is string stringValue)
{
if (stringValue.Length is 0)
{
return string.Empty;
}
string spacedString = Regex.Replace(stringValue, "(\\B[A-Z])", " $1");
if (OnlyTheFirstWordFirstCharacterIsUpperCase is false)
{
return spacedString;
}
string onlyTheFirstCharacterUpperCase = $"{spacedString[0].ToString()?.ToUpper()}{spacedString.Substring(1).ToLower()}";
return onlyTheFirstCharacterUpperCase;
}
throw new ArgumentException("Value must be a string", nameof(value));
}
public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException();
}
我们像这样使用它:
<Page.Resources>
<local:NavigationViewPaneDisplayModeOptions x:Key="NavigationViewPaneDisplayModeOptions" />
<local:EnumCamelCaseToSpacedStringConverter
x:Key="CamelCaseStringConverter"
OnlyTheFirstWordFirstCharacterIsUpperCase="True" />
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ComboBox
x:Name="NavigationViewPaneDisplayModeComboBox"
Grid.Row="0"
ItemsSource="{StaticResource NavigationViewPaneDisplayModeOptions}"
SelectedIndex="0">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="NavigationViewPaneDisplayMode">
<TextBlock Text="{x:Bind Converter={StaticResource CamelCaseStringConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<NavigationView
Grid.Row="1"
PaneDisplayMode="{x:Bind (NavigationViewPaneDisplayMode)NavigationViewPaneDisplayModeComboBox.SelectedValue, Mode=OneWay}">
<NavigationView.MenuItems>
<NavigationViewItem Content="Menu 1" />
<NavigationViewItem Content="Menu 2" />
<NavigationViewItem Content="Menu 3" />
</NavigationView.MenuItems>
</NavigationView>
</Grid>