在WPF中使用带有ObservableCollection的HashSets

时间:2009-11-24 21:42:10

标签: c# wpf observablecollection hashset

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

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

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

的签名时
void Add(T obj);

...和HashSet #Add:

bool Add(T obj); 

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

3 个答案:

答案 0 :(得分:8)

当您使用另一个集合创建新的ObservableCollection时,您没有包装该集合,而是创建一个新集合,其中传递的集合的所有项目都将复制到ObservableCollection。如果您想将ObservableCollection用于DataBinding的唯一目的,那么您可以将其绑定到WPF中的任何IEnumerable。这不幸地有一个缺点,即WPF不会总是正确地拾取绑定集合的更改。如果这是一个问题,您可能需要创建自己的obeservable hashset:

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类的代码(使用Reflector),那么很难自己模仿这个功能。

答案 1 :(得分: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>

答案 2 :(得分:0)

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

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