我正在使用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为无效。其他人可以证实这一点吗?
答案 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# ?