我有一个 ListBox
绑定(bind)到一组项目,这些项目具有用于生成 GetHashCode()
结果的 ID。添加新项目时,它的 ID 为 0,直到它首次保存到我们的数据库中。这导致我的 ListBox
提示;我相信原因是因为当一个项目第一次被 ListBox
使用时,它存储在一个内部 Dictionary
中,它不希望 hashcode 发生变化。
我可以通过从集合中删除未保存的项目来解决这个问题(我必须在这个阶段通知用户界面将其从字典中删除),保存到数据库,然后将其添加回收藏。这很困惑,我并不总是可以从我的 Save(BusinessObject obj)
方法访问集合。有人有解决这个问题的替代方案吗?
编辑以回应 Blam 的回答:
我正在使用 MVVM,因此我修改了代码以使用绑定(bind)。要重现问题,请单击添加,选择项目,单击保存,重复,然后尝试进行选择。我认为这表明 ListBox
仍保留其内部 Dictionary
中的旧哈希码,因此出现键冲突错误。
<Window x:Class="ListBoxHashCode.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Button Click="Button_Click_Add" Content="Add"/>
<Button Click="Button_Click_Save" Content="Save Selected"/>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding List}" DisplayMemberPath="ID" SelectedItem="{Binding Selected}"/>
</Grid>
</Window>
public partial class MainWindow : Window {
public ObservableCollection<ListItem> List { get; private set; }
public ListItem Selected { get; set; }
private Int32 saveId;
public MainWindow() {
this.DataContext = this;
this.List = new ObservableCollection<ListItem>();
this.saveId = 100;
InitializeComponent();
}
private void Button_Click_Add(object sender, RoutedEventArgs e) {
this.List.Add(new ListItem(0));
}
private void Button_Click_Save(object sender, RoutedEventArgs e) {
if (Selected != null && Selected.ID == 0) {
Selected.ID = saveId;
saveId++;
}
}
}
EDIT 2 经过一些测试,我发现了一些事情:
更改
ListBox
中项目的哈希码似乎工作正常。更改
ListBox
中所选项目 的哈希码会中断
这是功能。
进行选择(单选或多选模式)时,IList ListBox.SelectedItems
会更新。添加到选择中的项目将添加到 SelectedItems
,不再包含在选择中的项目将被删除。
如果项目的哈希码在选中时发生更改,则无法将其从 SelectedItems
中删除。即使手动调用 SelectedItems.Remove(item)
、SelectedItems.Clear()
并将 SelectedIndex
设置为 -1 也没有任何效果,并且项目保留在 IList
中。这会导致在下次选择它时抛出异常,因为我相信它会再次添加到 SelectedItems
。
最佳答案
Does anyone have an alternative solution to this problem?
对象的哈希码在对象生命周期内不得更改。您不应使用可变数据进行哈希码计算。
更新。
没想到,我的回答会引起这样的讨论。这里有一些详细的解释,可能会对OP有所帮助。
让我们看看代码中定义的一些可变实体类型,它覆盖了 GetHashCode
当然还有 Equals
.平等基于Id
平等:
class Mutable : IEquatable<Mutable>
{
public int Id { get; set; }
public override int GetHashCode()
{
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
var mutable = obj as Mutable;
if (mutable == null)
{
return false;
}
return this.Equals(mutable);
}
public bool Equals(Mutable other)
{
return Id.Equals(other.Id);
}
}
您在代码中的某处创建了多个此类实例:
// here's some mutable entities with hash-code, calculated using mutable data:
var key1 = new Mutable { Id = 1 };
var key2 = new Mutable { Id = 2 };
var key3 = new Mutable { Id = 3 };
这是一些外部代码,它使用 Dictionary<Mutable, string>
出于内部目的:
// let's use them as a key for the dictionary:
var dictionary = new Dictionary<Mutable, string>
{
{ key1, "John" },
{ key2, "Mary" },
{ key3, "Peter" }
};
// everything is ok, all of the keys are located properly:
Console.WriteLine(dictionary[key1]);
Console.WriteLine(dictionary[key2]);
Console.WriteLine(dictionary[key3]);
再次,您的代码。假设,你改变了 Id
的 key1
.哈希码也发生了变化:
// let's change the hashcode of key1:
key1.Id = 4;
再一次,外部代码。这里它试图通过 key1
定位一些数据。 :
Console.WriteLine(dictionary[key1]); // ooops! key1 was not found in dictionary
当然,您可以设计可变类型,它会覆盖 GetHashCode
和 Equals
,并计算可变数据的哈希码。但是你真的不应该那样做(除非你明确知道你在做什么)。
您不能保证任何外部代码都不会使用 Dictionary<TKey, TValue>
或 HashSet<T>
内部。
关于c# - 具有更改哈希码的 WPF 列表框和项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16789360/