将UWP ComboBox的ItemsSource绑定到枚举。

13

可以在WPF应用程序中使用ObjectDataProvider将枚举的字符串值绑定到ComboBox的ItemsSource,如此问题所示。

然而,在UWP应用程序中使用类似的片段时,会显示以下错误消息:

"在Windows通用项目中不支持ObjectDataProvider。"

在UWP中有简单的替代方法吗?

8个回答

16

以下是我其中一个原型的工作示例。

枚举

public enum GetDetails
{
    test1,
    test2,
    test3,
    test4,
    test5
}

项目源

var _enumval = Enum.GetValues(typeof(GetDetails)).Cast<GetDetails>();
cmbData.ItemsSource = _enumval.ToList();

这将把下拉框绑定到枚举值。


感谢您的输入。您知道是否有相应的XAML可以使用吗? - miguelarcilla
1
非常简单。创建一个视图模型来返回字符串。在您的 UI 上设置数据上下文和 XAML 绑定。 - AVK

3
如果您尝试通过xaml和绑定设置您的SelectedItem,请确保先设置ItemsSource!
例如:
<ComboBox ItemsSource="{Binding ...}" SelectedItem="{Binding ...}"/>

3

相信我,在 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" />

2

这是我为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;
    }
}

在ViewModel中:
    public List<EnumItemsSource<AccountType>> AccountTypes
    {
        get => EnumItemsSource<AccountType>.ToList();
    }

在视图中:

<ComboBox ItemsSource="{Binding AccountTypes}" DisplayMemberPath="LocalizedName" SelectedValuePath="Value" SelectedValue="{Binding Path=Account.Type, Mode=TwoWay}" />

2

我知道这是一篇旧文章,但是你可以将下拉框的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;
    }

1

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();
}


1

我选择了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>

0
让我发表另一个选项。
假设我们想要一个用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)
    {
    }
}

我们需要这个类在XAML中使用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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接