c# - 具有更改哈希码的 WPF 列表框和项目

标签 c# wpf mvvm listbox

我有一个 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]);

再次,您的代码。假设,你改变了 Idkey1 .哈希码也发生了变化:

        // let's change the hashcode of key1:
        key1.Id = 4;

再一次,外部代码。这里它试图通过 key1 定位一些数据。 :

Console.WriteLine(dictionary[key1]); // ooops! key1 was not found in dictionary

当然,您可以设计可变类型,它会覆盖 GetHashCodeEquals ,并计算可变数据的哈希码。但是你真的不应该那样做(除非你明确知道你在做什么)。

不能保证任何外部代码都不会使用 Dictionary<TKey, TValue>HashSet<T>内部。

关于c# - 具有更改哈希码的 WPF 列表框和项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16789360/

相关文章:

c# - RestSharp Deserialize 返回空属性,但 xml.deserialize 测试有效

c# - 如何使用 UIImage.FromImage(context.ToImage()) 防止内存泄漏?

c# - 在同一行有多个工具条

c# - MEF - 从其他 View 显示 View

c# - 在 Windows 应用商店应用程序的 TextBlock 和 PasswordBox 中垂直和水平居中文本

c# - KB4344167 安全更新破坏了 TLS 代码

wpf - 在 WPF 中绑定(bind) DataGridTextColumn 可见性属性

c# - WPF 绑定(bind) TabControl TabItem

mvvm - 使用 ViewModel 代替 DTO 作为 CQRS 查询的结果

c# - 只设置单个 View 的 Culture,而不是整个程序