ComboBox 中显示的当前值

Current value displayed in ComboBox

当 ComboBox 中的项目是数据库中的自定义对象时,我似乎无法在 ComboBox 中显示当前选定的值。我把下面的测试ViewModel和View放在一起。 SimpleWidgets 和 ObjectWidgets 的行为符合预期。 CodeWidgets(作为自定义 'Code' 对象从数据库中检索的列表)从不显示 SelectedCodeWidget。

    public class TestViewModel:BaseTabViewModel
    {
    private List<int> simpleWidgets;
    public List<int> SimpleWidgets
    {
        get { return simpleWidgets; }
        set
        {
            simpleWidgets = value;
            NotifyOfPropertyChange(() => SimpleWidgets);
        }
    }

    private int selectedSimpleWidget;
    public int SelectedSimpleWidget
    {
        get { return selectedSimpleWidget; }
        set
        {
            selectedSimpleWidget = value;
            NotifyOfPropertyChange(() => SelectedSimpleWidget);
        }
    }

    private List<Widget> objectWidgets;
    public List<Widget> ObjectWidgets
    {
        get { return objectWidgets; }
        set
        {
            objectWidgets = value;
            NotifyOfPropertyChange(() => ObjectWidgets);
        }
    }

    private Widget _selectedObjectWidget;
    public Widget SelectedObjectWidget
    {
        get { return _selectedObjectWidget; }
        set
        {
            _selectedObjectWidget = value;
            NotifyOfPropertyChange(() => SelectedObjectWidget);
        }
    }

    private List<ICode> codeWidgets;
    public List<ICode> CodeWidgets
    {
        get { return codeWidgets; }
        set
        {
            codeWidgets = value;
            NotifyOfPropertyChange(() => CodeWidgets);
        }
    }
    private Code _selectedCodeWidget;
    public Code SelectedCodeWidget
    {
        get { return _selectedCodeWidget; }
        set
        {
            _selectedCodeWidget = value;
            NotifyOfPropertyChange(() => SelectedCodeWidget);
        }
    }

    public TestViewModel()
    {
        DisplayName = "Test Data";
        IsEnabled = true;//control this with permissions
        //Simple int comboBox
        SimpleWidgets = new List<int> {1, 3, 5, 7, 9};
        SelectedSimpleWidget = 7;
        //Object filled ComboBox
        ObjectWidgets= new List<Widget>();
        Widget w = new Widget {Key = 2, Description = "test2"};
        ObjectWidgets.Add(w);
        w = new Widget { Key = 4, Description = "test4" };
        ObjectWidgets.Add(w);
        w = new Widget {Key = 6, Description = "test6"};
        ObjectWidgets.Add(w);
        SelectedObjectWidget = w;
        //Code filled ComboBox
        CodeWidgets = (new Code().Get("UPFLTY").Result).ToList();
        SelectedCodeWidget = new Code(2707);
    }
}

public class Widget
{
    public int Key { get; set; }
    public string Description { get; set; }
}

TestView.xaml是:

<UserControl x:Class="CAB.DataManager.App.Views.TestView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UniformGrid>

        <ComboBox Name="SimpleWidgets"></ComboBox>
            <ComboBox Name="ObjectWidgets"
                     DisplayMemberPath="Description" >
             </ComboBox>
        <ComboBox x:Name="CodeWidgets">
                 <ComboBox.ItemTemplate>
                     <DataTemplate>
                        <StackPanel>
                            <TextBox Text="{Binding Description}"/>
                        </StackPanel>
                     </DataTemplate>
                 </ComboBox.ItemTemplate>
        </ComboBox>
    </UniformGrid>
</UserControl>

SimpleWidgets 和 ObjectWidgets 显示所选值并在下拉列表中包含所有值。 CodeWidgets 为空白,但下拉列表中包含所有值。我错过了什么?

SelectedItem 中的 实例 必须存在于 ItemsSource.

来自您的代码

//Code filled ComboBox
CodeWidgets = (new Code().Get("UPFLTY").Result).ToList();
SelectedCodeWidget = new Code(2707); // THIS is where you bugged!

您可以看到 SelectedCodeWidget 被设置为 Code 的一个新实例,它不在 CodeWidgets.

答案很简单:

//Code filled ComboBox
CodeWidgets = (new Code().Get("UPFLTY").Result).ToList();
var selectedCode = FindCodeByDERP(CodeWidgets, 2707); // MAKE THIS METHOD
SelectedCodeWidget = selectedCode;

其中 FindCodeByDERP 是您编写的一种方法,用于搜索 CodeWidgets 和 returns 中 2707 中最难的实例.不管它是什么意思。我的意思是,您有代码 UPFLTY,但您创建了另一个传入 2707... 的实例,这毫无意义。是的。

WPF 的 ComboBox 通过引用将 SelectedItem 与 ItemsSource 中的那些进行比较,在您的情况下,new Code(2707) 的实例在内存中与 (new Code().Get("UPFLTY").Result).ToList()[= 中的实例不完全相同19=]

避免这种情况的最常见方法是在具有通用对象类型的对象上使用 SelectedValueSelectedValuePath

例如,

<ComboBox x:Name="CodeWidgets" 
          SelectedValue="{Binding SelectedCodeId}" 
          SelectedValuePath="Id">

另一个解决方案是确保将 SelectedItem 设置为 ItemsSource 中的项目之一,而不是该项目的新实例。

SelectedCodeWidget = CodeWidgets.FirstOrDefault(() => p.Id == 2707);

第三种解决方案(通常不推荐)是覆盖对象的 .Equals,以便它通过参考以外的其他内容比较项目

public override bool Equals(object obj) 
{ 
    if (obj == null || !(obj is Code)) 
        return false; 

    return ((Code)obj).Id == this.Id); 
}

请注意,如果您也走这条路,您可能还应该覆盖 .GetHashCode()