ComboBox does not select binding value initially

自古美人都是妖i 提交于 2019-12-14 03:56:05

问题


First of all, not a duplicate.

TextBox and CheckBox work correctly, just not ComboBox.

Model

internal class Word : _Model
{
    public enum Categories
    {
        Noun,
        Verb,
    }

    private Categories category;
    public Categories Category
    {
        get
        {
            return this.category;
        }
        set
        {
            this.SetProperty(ref this.category,
                             value);
        }
    }
}

Notify (safe to skip reading this part)

internal abstract class _Model : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (object.Equals(storage, value))
        {
            return false;
        }
        storage = value;
        this.OnPropertyChanged(propertyName);
        return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var eventHandler = this.PropertyChanged;
        if (eventHandler != null)
        {
            eventHandler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Control (Model)

public sealed partial class WordEditor : UserControl
{
    public WordEditor()
    {
        this.Model = new Models.Word();
        this.Model.Category = Models.Word.Categories.Verb;
        this.InitializeComponent();
    }

    private Models.Word Model { get; set; }

    internal IList<KeyValuePair<Models.Word.Categories, string>> Categories { get { return new List<KeyValuePair<Models.Word.Categories, string>>() { new KeyValuePair<Models.Word.Categories, string>(Models.Word.Categories.Noun, "Noun"), new KeyValuePair<Models.Word.Categories, string>(Models.Word.Categories.Verb, "Verb") }; } }
}

Binding

<UserControl.Resources>
    <converters:AnyConverter x:Key="AnyConverter"/>
</UserControl.Resources>

<StackPanel>
    <ComboBox ItemsSource="{x:Bind Path=Categories, Mode=OneTime}"
              DisplayMemberPath="Value"
              SelectedValuePath="Key"
              SelectedValue="{x:Bind Path=Model.Category, Mode=TwoWay, Converter={StaticResource ResourceKey=AnyConverter}}"/>
</StackPanel>

Converter

internal class AnyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        return value;
    }
}

Issue

The ComboBox has "Verb" and "Noun" displayed correctly, and selection on "Verb" or "Noun" is reflected by the model correctly.

However, even though Category is set to Verb at the very first place, the ComboBox does not show that.

How to fix it so that the ComboBox selects "Verb" when it first appears?


回答1:


I reproduced your issue. The problem here is ComboBox.SelectedValue doesn't work with enumeration type. To test this, we can use a ComboBox without binding and set the SelectedValue in code-behind like:

<ComboBox x:Name="cmb" DisplayMemberPath="Value" SelectedValuePath="Key" />

cmb.ItemsSource = Categories;
cmb.SelectedValue = Models.Word.Categories.Verb;

Although I've set Models.Word.Categories.Verb to SelectedValue, but its value still be null.

However, it works when the Value type is int or string. From my WPF experience, enumeration type should also work. I think this may be a bug in UWP.

As a workaround, I think you can use SelectedIndex or SelectedItem property to select "Verb" when the ComboBox first appears. For example:

In the XAML, use SelectedIndex instead of SelectedValue:

<ComboBox DisplayMemberPath="Value"
          ItemsSource="{x:Bind Path=Categories, Mode=OneTime}"
          SelectedIndex="{x:Bind Path=Model.Category, Mode=TwoWay, Converter={StaticResource ResourceKey=AnyConverter}}"
          SelectedValuePath="Key" />

And change the Converter like:

internal class AnyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return (int)(Models.Word.Categories)value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        return (Models.Word.Categories)(int)value;
    }
}

After this, setting this.Model.Category = Models.Word.Categories.Verb; will work.




回答2:


At first I thought @Jay Zuo - MSFT was right in that you'd have to convert it to another form (e.g., int); however, by using SelectedItem, it works! I was using SelectedValue like OP, but didn't define SelectedValuePath and it wouldn't select the initial value. Binding to SelectedItem does even without defining SelectedValuePath.

Note, if you bind to SelectedItem versus SelectedIndex, there's no need whatsoever for a converter.

I tested this with Windows 10 Anniversary Edition (10.0; Build 14393).

Update

Turns out I was wrong ;p I ended up rolling out my own converter for more convenience.

public abstract class EnumToIntConverter<TEnum> : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value is Enum)
            return (int)value;

        return DependencyProperty.UnsetValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        if (value is int)
            return (TEnum)value;

        return DependencyProperty.UnsetValue;
    }
}

public sealed class MyEnumToIntConverter : EnumToIntConverter<MyEnum>
{
    //Do nothing!
}

To clarify, SelectedIndex is absolutely needed.



来源:https://stackoverflow.com/questions/35599479/combobox-does-not-select-binding-value-initially

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!