Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/300.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# wpflistbox&;具有更改哈希代码的项_C#_Wpf_Mvvm_Listbox - Fatal编程技术网

C# wpflistbox&;具有更改哈希代码的项

C# wpflistbox&;具有更改哈希代码的项,c#,wpf,mvvm,listbox,C#,Wpf,Mvvm,Listbox,我有一个列表框绑定到一组项目,这些项目的ID用于生成GetHashCode()的结果。添加新项目时,它的ID为0,直到它首次保存到我们的数据库。这导致我的列表框投诉;我认为原因是当一个项目第一次被列表框使用时,它存储在一个内部字典中,该字典不希望hashcode发生变化 我可以通过从集合中删除未保存的项(我必须在此阶段通知UI将其从字典中删除)、保存到DB并将其添加回集合来修复此问题。这很混乱,而且我并不总是能够通过Save(businessobjectobj)方法访问集合。有没有人能找到解决这

我有一个
列表框
绑定到一组项目,这些项目的ID用于生成
GetHashCode()
的结果。添加新项目时,它的ID为0,直到它首次保存到我们的数据库。这导致我的
列表框
投诉;我认为原因是当一个项目第一次被
列表框
使用时,它存储在一个内部
字典
中,该字典不希望hashcode发生变化

我可以通过从集合中删除未保存的项(我必须在此阶段通知UI将其从字典中删除)、保存到DB并将其添加回集合来修复此问题。这很混乱,而且我并不总是能够通过
Save(businessobjectobj)
方法访问集合。有没有人能找到解决这个问题的替代方案

根据布拉姆的回答进行编辑:

我正在使用MVVM,因此我修改了代码以使用绑定。要重现问题,请单击“添加”,选择项目,单击“保存”,重复,然后尝试进行选择。我认为这表明
列表框
仍然保留其内部
字典
中的旧哈希代码,因此出现了键冲突错误

<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++;
        }
    }
}

公共部分类主窗口:窗口{
公共可观测集合列表{get;private set;}
已选择公共ListItem{get;set;}
私有Int32-saveId;
公共主窗口(){
this.DataContext=this;
this.List=新的ObservableCollection();
this.saveId=100;
初始化组件();
}
私有无效按钮\u单击\u添加(对象发送方,路由目标){
this.List.Add(新列表项(0));
}
私有无效按钮\u单击\u保存(对象发送方,路由目标){
if(Selected!=null&&Selected.ID==0){
Selected.ID=saveId;
saveId++;
}
}
}
编辑2经过一些测试,我发现了一些东西:

  • 更改
    列表框中项目的哈希代码似乎可以正常工作

  • 更改
    列表框中所选项目的哈希代码将中断
    这是功能性的

进行选择时(单选或多选模式),会更新
IList列表框.SelectedItems
。添加到所选内容的项目将添加到
SelectedItems
,不再包含在所选内容中的项目将被删除

如果项目的哈希代码在选中时被更改,则无法将其从
SelectedItems
中删除。即使手动调用
SelectedItems.Remove(item)
SelectedItems.Clear()
和将
SelectedIndex
设置为-1都无效,并且该项仍保留在
IList
中。这会导致在下次选择它时抛出异常,因为我相信它会再次添加到
SelectedItems

有没有人能找到解决这个问题的替代方案

对象的哈希代码在对象生存期内不得更改。您不应该将可变数据用于哈希代码计算

更新

我没想到,我的回答会引起这样的讨论。这里有一些详细的解释,可能会有帮助

让我们看看代码中定义的一些可变实体类型,它覆盖
GetHashCode
,当然,
等于
。相等性基于
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);
    }
}
这是一些外部代码,它使用
字典
作为内部用途:

        // 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]);
还有,外部代码。在这里,它试图通过
key1
来定位一些数据:

Console.WriteLine(dictionary[key1]); // ooops! key1 was not found in dictionary
当然,您可以设计可变类型,它覆盖
GetHashCode
Equals
,并在可变数据上计算哈希代码。但是你真的不应该这样做(除了那些情况,当你明确知道你在做什么的时候)


您不能保证任何外部代码不会在内部使用
字典
哈希集

我怀疑您的代码的问题在于它没有覆盖
等于

ListBox
使用
Equals
查找一个项目,因此如果有多个Equals返回true,则它将匹配多个项目,只是简单地搞乱了。
列表框中的项目必须是唯一的,基于相等。
如果尝试将
列表框
绑定到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; }
    }
}

使用系统组件模型;
使用System.Collections.ObjectModel;
命名空间ListViewGetHashCode
{
公共部分类主窗口:窗口
{
ObservableCollection li=新的ObservableCollection();
私有Int32 saveId=100;
私有Int32 tempId=-1;
公共主窗口()
{
this.DataContext=this;
对于(int32i=1;i100)中断;
}
}
专用无效按钮
Console.WriteLine(dictionary[key1]); // ooops! key1 was not found in dictionary
<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; }
    }
}