7

我正在使用 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 的返回类型#添加无效。其他人可以证实这一点吗?

4

3 回答 3

9

当您使用另一个集合创建新的 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); 
    } 
}

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

于 2009-11-24T23:03:28.153 回答
1

根据 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>
于 2015-04-22T06:16:04.423 回答
0

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

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

于 2009-11-24T23:20:15.763 回答