【问题标题】:Using HashSets with ObservableCollection with WPF通过 WPF 使用带有 ObservableCollection 的 HashSet
【发布时间】:2009-11-24 21:42:09
【问题描述】:

我正在使用 ListBox 来维护 WPF 应用程序中的项目列表。 ListBox 数据源是包装在 ObservableCollection 中的 HashSet。即,我有以下代码:

this.shackSet = new ObservableCollection<Shack>(new HashSet<Shack>());
this.shackListing.ItemsSource = this.shackSet;

... 其中 shackListing 是 ListBox 控件,而 shackSet 在 ICollection 中。但是,每当我在添加第一项后向 shackSet 添加任何内容时,我都会在 ListBox 中看到多个项目。即,就像新添加的项目被添加到列表中,而不管它们是否被添加到集合中。当我查看 ICollection#Add 的签名时:

void Add(T obj);

... 和 HashSet#Add :

bool Add(T obj); 

...这让我相信存在一个影响包装的 HashSet 的错误,其中新添加的项目被添加到 ListBox 中,因为 ObservableCollection 无法判断对象是否实际添加到底层集合中,因为返回类型ICollection#Add 是无效的。其他人可以证实这一点吗?

【问题讨论】:

    标签: c# wpf observablecollection hashset


    【解决方案1】:

    当您使用另一个集合创建新的 ObservableCollection 时,您并没有包装该集合,而是创建了一个新集合,其中传递的集合的所有项目都被复制到 ObservableCollection。如果您只想将 ObservableCollection 用于 DataBinding,请不要再观望,您可以绑定到 WPF 中的任何 IEnumerable。不幸的是,这有一个缺点,即 WPF 不会总是正确地拾取对绑定集合的更改。如果这是一个问题,您可能必须创建自己的可观察哈希集:

    public class ObservableHashSet<T> : ObservableCollection<T>  
    { 
        protected override void InsertItem(int index, T item) 
        { 
            if (Contains(item)) 
            {
                throw new ItemExistsException(item); 
            }
            base.InsertItem(index, item); 
        } 
    
        protected override void SetItem(int index, T item) 
        { 
            int i = IndexOf(item); 
            if (i >= 0 && i != index)
            {
                 throw new ItemExistsException(item); 
            }       
            base.SetItem(index, item); 
        } 
    }
    

    编辑: AS 已经指出,不能从 HashSet 继承来实现 INotifyCollectionChanged。但是,如果您查看 HashSet 类的代码(使用反射器),它非常简单,您自己应该很难模仿该功能。

    【讨论】:

    • 当然你可以直接绑定到hashset,但是如果添加了一个项目,WPF不会注意到。
    • +1 进行解释。但是,关于建议的解决方案,我宁愿从 HashSet 继承并实现 INotifyCollectionChanged...
    • +1 到 Thomas 所说的,除了你不能从 HashSet 继承并实现 INotifyCollectionChanged,因为 HashSet 方法不是虚拟的,不能被覆盖。您必须实现ICollection,并且您的实现必须包装 HashSet 并针对所有变异方法发出通知。
    • 我想指出,虽然这个类可能有一个哈希集的接口,但它确实没有有它的性能。之所以称为散列集,是因为它使用散列码在(理想情况下)恒定时间内定位项目。您的实现需要线性时间。因此,我只会在对性能不重要的情况下使用它。另外,我可能只称它为“ObservableSet”(没有“Hash”)部分。
    • 这不是一个可观察的 HashSet。 HashSet 是具有性能优势的无序集。看看这个github.com/BellaCode/Public/tree/master/ObservableHashSet
    【解决方案2】:

    根据 bitbonk 的回答,但我想覆盖 add(T item) 方法,但你不能,所以我创建了一个 append(T item) 方法:

    public class ObservableSetCollection<T> : ObservableCollection<T> {
        public void Append(T item) {
            if (Contains(item)) return;
            base.Add(item);
        }
    }
    

    然后在我的代码后面:

    public partial class MainWindow : Window {
        private ObservableSetCollection<string> consolidationHeaders;
    
        public MainWindow() {
            InitializeComponent();
            initialize();
        }
    
        private void initialize() {
            consolidationHeaders = new ObservableSetCollection<string>();
            listboxConsolidationColumns.ItemsSource = consolidationHeaders;
        }
    
        .
        .
        .
    
    
        private void listboxAvailableColumns_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
            consolidationHeaders.Append(listboxAvailableColumns.SelectedValue.ToString());
        }
    
        private void listboxConsolidationColumns_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
            consolidationHeaders.Remove(listboxConsolidationColumns.SelectedValue.ToString());
        }
    }
    

    在上面我有两个列表框,listboxAvailableColumns,它有一个字符串列表,用户可以通过双击来选择,这会将选择添加到第二个列表框,listboxConsolidationColumns。不允许重复,这与 ObservableSetCollection 完全一样。

    xaml 很简单:

    <Grid Margin="5,5,5,5">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="1*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>
        <Label Grid.Row="0" Grid.Column="0" Margin="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Available Columns"/>
        <Label Grid.Row="0" Grid.Column="1" Margin="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Consolidation Columns"/>
        <ListBox  Grid.Row="1" Grid.Column="0" Name="listboxAvailableColumns" MouseDoubleClick="listboxAvailableColumns_MouseDoubleClick" />
        <ListBox  Grid.Row="1" Grid.Column="1" Name="listboxConsolidationColumns" MouseDoubleClick="listboxConsolidationColumns_MouseDoubleClick" />
    </Grid>
    

    【讨论】:

      【解决方案3】:

      正如 bitbonk 所说,ObservableCollection 不包装 Hashset,而是复制其元素。

      如果您想要 Observable Hashset,请查看 How can I make an Observable Hashset in C# ?

      【讨论】:

      • 微软是否有任何机制来提交功能请求?在我看来,Observable(Hash)Set 应该已经在 .NET 框架中了。
      猜你喜欢
      • 2018-01-13
      • 2021-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-13
      • 2015-03-29
      • 1970-01-01
      相关资源
      最近更新 更多