我有一个ListBox
绑定到一个具有ID的项集合,该ID用于生成GetHashCode()
的结果。添加新项时,它的ID为0,直到首次将其保存到我们的数据库。这导致了我的ListBox
的抱怨;我相信原因是当一个项目最初被一个ListBox
使用时,它被存储在一个内部Dictionary
中,它不会期望hashcode发生变化。
我可以通过从集合中删除未保存的项(我必须在现阶段通知UI以从字典中删除它)、将其保存到DB并将其添加回集合中来解决这个问题。这很麻烦,而且我并不总是可以访问来自我的Save(BusinessObject obj)
方法的集合。有谁有解决这个问题的办法吗?
编辑回复Blam的答案:
我使用的是MVVM,所以我修改了代码以使用绑定。若要再现问题,请单击“添加”,选择项目,单击“保存”,“重复”,然后尝试作出选择。我认为这表明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++;
}
}
}
在经过一些测试之后,我发现了一些东西:
ListBox
中项的哈希代码似乎是正常的。ListBox
中断中选定项的哈希代码
这是功能。当选择(单个或多个选择模式)时,IList ListBox.SelectedItems
将被更新。添加到选定内容中的项将添加到SelectedItems
中,不再包含在所选内容中的项将被移除。
如果项的哈希代码在选定时更改,则无法将其从SelectedItems
中删除。即使是手动调用SelectedItems.Remove(item)
、SelectedItems.Clear()
和将SelectedIndex
设置为-1也没有任何效果,并且项目仍然保留在IList
中。这将在下次选择异常后引发异常,因为我认为它再次添加到SelectedItems
中。
发布于 2013-05-28 15:06:14
我怀疑您的代码的问题在于它没有覆盖Equals
。
ListBox
使用Equals
来查找一个条目,所以如果if多个等于返回true,那么它就匹配多个条目,并且简单地搞砸了。
ListBox
中的项必须是基于等号的唯一项。
如果您尝试将ListBox
绑定到List Int32或List string并重复任何值,那么它也有相同的问题。
你说抱怨的时候。它怎么抱怨的?
在下面这个简单的例子中,ListView
并没有因为对GetHashCode
的更改而收支平衡。
您实现INotifyPropertyChanged
了吗?
<Window x:Class="ListViewGetHashCode.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 Grid.Row="0" Orientation="Horizontal">
<Button Click="Button_Click" Content="Button"/>
<Button Click="Button_Click2" Content="Add"/>
<Button Grid.Row="0" Click="Button_Click_Save" Content="Save"/>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding BindingList}" DisplayMemberPath="ID" SelectedItem="{Binding Selected}" VirtualizingStackPanel.VirtualizationMode="Standard"/>
<!--<ListBox Grid.Row="1" x:Name="lbHash" ItemsSource="{Binding}" DisplayMemberPath="ID"/>-->
</Grid>
</Window>
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace ListViewGetHashCode
{
public partial class MainWindow : Window
{
ObservableCollection<ListItem> li = new ObservableCollection<ListItem>();
private Int32 saveId = 100;
private Int32 tempId = -1;
public MainWindow()
{
this.DataContext = this;
for (Int32 i = 1; i < saveId; i++) li.Add(new ListItem(i));
InitializeComponent();
}
public ObservableCollection<ListItem> BindingList { get { return li; } }
public ListItem Selected { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
Int32 counter = 0;
foreach (ListItem l in li)
{
l.ID = -l.ID;
counter++;
if (counter > 100) break;
}
}
private void Button_Click2(object sender, RoutedEventArgs e)
{
//li.Add(new ListItem(0)); // this is where it breaks as items were not unique
li.Add(new ListItem(tempId));
tempId--;
}
private void Button_Click_Save(object sender, RoutedEventArgs e)
{
if (Selected != null && Selected.ID <= 0)
{
Selected.ID = saveId;
saveId++;
}
}
}
public class ListItem : Object, INotifyPropertyChanged
{
private Int32 id;
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public Int32 ID
{
get
{
return (id < 0) ? 0 : id;
//if you want users to see 0 and not the temp id
//internally much use id
//return id;
}
set
{
if (id == value) return;
id = value;
NotifyPropertyChanged("ID");
}
}
public override bool Equals(object obj)
{
if (obj is ListItem)
{
ListItem comp = (ListItem)obj;
return (comp.id == this.id);
}
else return false;
}
public bool Equals(ListItem comp)
{
return (comp.id == this.id);
}
public override int GetHashCode()
{
System.Diagnostics.Debug.WriteLine("GetHashCode " + id.ToString());
return id;
//can even return 0 as the hash for negative but it will only slow
//things downs
//if (id > 0) return id;
//else return 0;
}
public ListItem(Int32 ID) { id = ID; }
}
}
https://stackoverflow.com/questions/16789360
复制相似问题