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に追加されるというバグがあると思われます.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 クラスの (Reflector を使用した) コードを見ると、非常に単純であるため、その機能を自分で模倣するのは難しすぎるはずです。

于 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 という 2 つのリストボックスがあります。これには、ユーザーがダブルクリックして選択できる文字列のリストがあり、2 番目のリストボックス 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

ビットボンクが言ったように、ObservableCollectionはハッシュセットをラップせず、代わりにその要素をコピーします。

監視可能なハッシュセットが必要な場合は、C#で監視可能なハッシュセットを作成するにはどうすればよいですか?

于 2009-11-24T23:20:15.763 に答える