2009-11-24 27 views
5

Tôi đang sử dụng ListBox để duy trì danh sách các mục trong ứng dụng WPF. Nguồn dữ liệu ListBox là một HashSet được bao bọc trong một ObservableCollection. tức là, tôi có mã sau:Sử dụng HashSets với ObservableCollection với WPF

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

... trong đó shackListing là một điều khiển ListBox và shackĐặt trong một ICollection. Tuy nhiên, bất cứ khi nào tôi thêm bất cứ thứ gì vào shackSet sau khi thêm mục đầu tiên, tôi thấy nhiều mục trong ListBox. tức là Nó giống như các mục mới được thêm vào đang được thêm vào danh sách bất kể chúng được thêm vào bộ này hay chưa. Khi tôi nhìn vào chữ ký của ICollection # Địa chỉ:

void Add(T obj); 

... và HashSet # Add:

bool Add(T obj); 

... điều này dẫn tôi để tin rằng có một lỗi có ảnh hưởng đến HashSets bọc nơi mới các mục được thêm vào được thêm vào ListBox bất kể vì ObservableCollection không có cách nào để nói liệu đối tượng có thực sự được thêm vào bộ sưu tập underlaying hay không bởi vì kiểu trả về của ICollection # Add bị vô hiệu. Ai có thể xác nhận điều này?

Trả lời

8

Khi bạn tạo một ObservableCollection mới với một bộ sưu tập khác mà bạn không gói bộ sưu tập đó, bạn tạo một bộ sưu tập mới trong đó tất cả các mục của bộ sưu tập đã truyền được sao chép vào ObservableCollection. Nếu bạn muốn sử dụng ObservableCollection cho mục đích duy nhất của DataBinding, không nhìn xa hơn, bạn có thể liên kết với bất kỳ IEnumerable nào trong WPF. Điều này không may có nhược điểm rằng WPF sẽ không luôn luôn chính xác đón những thay đổi cho bộ sưu tập bị ràng buộc. Nếu đây là một vấn đề có lẽ bạn sẽ phải tạo HashSet obeservable riêng bạn:

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); 
    } 
} 

EDIT: AS đã được chỉ ra, bạn không thể kế thừa từ HashSet để thực hiện INotifyCollectionChanged. Tuy nhiên nếu bạn nhìn vào mã (sử dụng Reflector) cho lớp HashSet, nó khá đơn giản, nó sẽ quá khó để bắt chước chức năng đó.

+0

Chắc chắn bạn có thể liên kết trực tiếp đến HashSet, nhưng nếu một mục được thêm WPF sẽ không để ý. –

+1

+1 để giải thích. Tuy nhiên, về giải pháp được đề xuất, tôi thà kế thừa từ HashSet và triển khai INotifyCollectionChanged ... –

+2

+1 với những gì Thomas nói, ngoại trừ việc bạn không thể kế thừa từ 'HashSet' và cũng thực hiện' INotifyCollectionChanged', vì các phương thức 'HashSet' không phải là ảo và không thể bị ghi đè. Bạn sẽ phải thực hiện 'ICollection' và việc triển khai của bạn sẽ phải _wrap_' HashSet' và nâng cao thông báo trên tất cả các phương thức đột biến. –

0

Như bitbonk đã nói, ObservableCollection không bọc Hashset nhưng sao chép các phần tử của nó thay thế.

Nếu bạn muốn một Quan sát Hashset kiểm tra How can I make an Observable Hashset in C# ?

+0

Microsoft có bất kỳ cơ chế nào để gửi yêu cầu tính năng không? Dường như với tôi rằng một bộ quan sát (Hash) là một cái gì đó đã có trong khung công tác .NET. –

+0

Chắc chắn, tại Microsoft Connect. http://connect.microsoft.com/VisualStudio –

1

Như mỗi câu trả lời bitbonk, nhưng tôi muốn ghi đè phương thức add (T item), nhưng bạn không thể, vì vậy tôi đã tạo ra một append (T item) phương pháp thay vì:

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

Và sau đó trong mã của tôi đằng sau:

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()); 
    } 
} 

ở phía trên tôi có hai listboxes, listboxAvailableColumns, trong đó có một danh sách các chuỗi tha t người dùng có thể chọn bằng cách nhấp đúp, điều này sẽ thêm lựa chọn vào hộp danh sách thứ hai, listboxConsolidationColumns. Không cho phép trùng lặp, và điều này hoạt động hoàn hảo với ObservableSetCollection chính xác như trên.

Các XAML chỉ đơn giản là:

<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> 
Các vấn đề liên quan