【问题标题】:How to update a WPF ComboBox when assigning an object as its SelectedItem?将对象分配为其 SelectedItem 时如何更新 WPF 组合框?
【发布时间】:2014-09-06 04:57:23
【问题描述】:

我有一个ComboBox,它的ItemsSource 绑定到一个ObservableCollection<CustomObject>,其中CustomObject 有一些属性。

示例类:

public class CustomObject : INotifyPropertyChanged
{
    public string Property1 { /*...omitted for brevity...*/ }
    public string Property2 { /*...omitted for brevity...*/ }
    public string Property3 { /*...omitted for brevity...*/ }
}

我的ComboBoxSelectedItem 属性绑定到出现在DataGrid 行中的CustomObject 属性。

示例类:

public class DataGridEntry : INotifyPropertyChanged
{
    public CustomObject Column1 { /*...omitted for brevity...*/ }
    public string Column2 { /*...omitted for brevity...*/ }
    public string Column3 { /*...omitted for brevity...*/ }
}

我在窗口初始化期间创建了ObservableCollection<CustomObject>,然后将DataGrid 的数据上下文设置为ObservableCollection<DataGridEntry>

我的目标是将初始值加载到我的DataGrid 中,但我不知道如何使ComboBox 意识到指定的CustomObject 可以在其ItemsSource 中找到,因此,@987654338 @ 不会呈现 SelectedItem

这是我加载初始值的方式:

ObservableCollection<DataGridEntry> entries = new ObservableCollection<DataGridEntry>();
MyWindow.DataContext = this;
entries.Add(new DataGridEntry(new CustomObject("val1", "val2", "val3"), "col2", "col3");

你知道我是如何让ComboBox 设置它的SelectedItem 属性的吗?如果我更改我的代码以使DataGridEntry 仅适用于string 属性,那么ComboBox 在按我预期的初始化后呈现SelectedItem。但是对于引用类型,它不起作用。


如果需要,这就是我将数据绑定到Combobox 的方式:
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.CustomObjectsCollection}" SelectedItem="{Binding Column1, UpdateSourceTrigger=PropertyChanged}"/>

编辑:
如果不清楚,ObservableCollection&lt;CustomObject&gt; 包含一个与上面相同的元素:new CustomObject("val1", "val2", "val3");

我怀疑ComboBox 没有意识到这两个CustomObjects 是等价的。由于这种怀疑,我为CustomObject 覆盖了EqualsGetHashCode 函数,但没有成功。显然,ComboBox 在检测非引用数据类型(如字符串)的相等性时没有问题。 谢谢您的帮助! :)

【问题讨论】:

  • 分配 DataContext 时的“this”是什么?它应该非常简单:创建一个 ViewModel,它包含一个集合 ObservableCollection 和一个 ObservableCollection。接下来,将 ComboBox ItemsSource 绑定到 CustomObjects 的集合,并将其 SelectedItem 绑定到 DataGrid 的 SelectedItem.Column1(其中 Column1 是 CustomObject)。最后,将 DataGrid 的 ItemsSource 设置为 DataGridEntries 的集合。当然......你知道你可以在你的数据网格中放置一个 ComboBox inside 对吧?
  • 为简单起见,我省略了我的DataContext。实际上,我有一个包装类,它将这些集合作为属性保存。我将主窗口的DataContext 设置为指向该包装器的一个实例。我相信问题在于添加到DataGridEntryCustomObject 和集合中相应的CustomObject 的相等性。我想我忘了提到ObservableCollection&lt;CustomObject&gt; 包含一个CustomObject,它与插入DataGrid 的那个相同:new CustomObject("val1", "val2", "val3")
  • 具有相同值的 CustomObjects 实际上是同一个对象似乎会更好?

标签: c# wpf datagrid combobox observablecollection


【解决方案1】:

请记住,GetHashCode()Equals() 用于 Object 类型比较(即您的 ObservableCollection&lt;T&gt; 需要用于 Object 类型。)

对于更强的类型化覆盖,您应该实现IEquatable&lt;T&gt; - 这应该允许组合框在设置SelectedItem 属性时比较项目。

【讨论】:

  • 看起来像我要找的东西。我会继续尝试。
  • 太棒了!显然我也没有注意到我的绑定属性名称不正确。但 IEquatable 确实允许 CustomObjectsComboBox 正确比较
  • 修复绑定问题后,我仅使用Object.Equals 进行了测试,ComboBox 更新正确。 IEquatable.Equals具体负责什么?
  • 用于执行精确的类型间比较。例如,使用自动填充 GUID 和名称字符串属性的 ID 属性定义一个类。您可以编写 IEquatable.Equals 以便该方法仅比较 Name 属性。如果您创建两个对象并为它们都赋予“Sally”的 Name 值,则 Object.Equals 返回 false(不同的引用),IEquatable.Equals 将返回 true。
  • 答案在 2017 年的今天仍然有效。我特别喜欢如何按类型制作它,代码只存在于它影响的类中。对我来说,当我尝试将旧的 WPF 解决方案更新到 EF6 时,问题就出现了。在某个地方,在旧版本的 WPF 或 EF4 中,平等必须有不同的处理方式。
【解决方案2】:

ComboBox 使用Object.Equals 来确定相等性。默认情况下,这意味着 ComboBox 在使用引用类型时需要相同的引用。使用值类型时,Object.Equals 会比较这些值。虽然String 在技术上不是一个值类型,但它已经覆盖了Object.Equals 来比较值。

String 类似,可以通过为ComboBox 持有的类类型定义自定义Equals 方法来覆盖相等行为。

对于原始帖子中的示例,Equals 方法应该像这样比较每个属性:

public class CustomObject : INotifyPropertyChanged
{
    //Include properties, constructor, and INotifyPropertyChanged interface members.

    public override bool Equals(object obj)
    {
        CustomObject test = obj as CustomObject;  //test=null if obj cannot be casted.
        if(test == null) return false; //Comparing null against non-null: FALSE
        else
        {   //Check if all properties are equal.
            return ((Property1.CompareTo(test.Property1) == 0) &&
                    (Property2.CompareTo(test.Property2) == 0) &&
                    (Property3.CompareTo(test.Property3) == 0));
        }
    }
}

尽管没有它,ComboBox 仍然可以正常工作。覆盖Object.GetHashCode 也是一个好主意。这超出了此答案的范围,但是,可以在此处找到该函数的详细记录实现:

What is the best algorithm for an overridden System.Object.GetHashCode?

【讨论】:

  • 理想情况下,您可以在这里实现Object.Equals,就像您在else 声明之前所做的那样。此时您将调用IEquatable.Equals,因为您提供的实现是特定于类型的,并且正如IEquatable 文档在 MSDN 上所说的那样“定义值类型或类实现的通用方法以创建特定于类型的方法来确定实例相等。”
  • 您能详细说明一下吗?如果我所做的只是将我的 else 块移动到 IEquatable.Equals,并且如果我从 Object.Equals 调用 IEquatable.Equals,那么除了强制转换和空检查之外,两者将变得相同。由于ComboBox 不关心IEquatable.Equals,我不完全确定这会实现什么。这是否与扩展我的课程有关?
  • 不,这与 Object.Equals 不应该是特定于类型的实现有关,而 IEquatable.Equals 是。您可以将其称为样式偏好,但我喜欢像这样保持代码隔离,以便类型无关的方法实际上是类型无关的(因为它被记录为像这样使用)。同样,一旦确定对象是相同的类型,从特定类型的 Object.Equals 调用特定类型的 IEquatable.Equals 也没有错。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-30
  • 2017-01-14
  • 2018-05-03
  • 1970-01-01
  • 1970-01-01
  • 2021-09-16
  • 1970-01-01
相关资源
最近更新 更多