2009-11-24 20 views
5

Estoy usando un ListBox para mantener una lista de elementos en una aplicación de WPF. La fuente de datos ListBox es un HashSet envuelto en un ObservableCollection. es decir, que tiene el siguiente código:Uso de HashSets con ObservableCollection con WPF

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

... donde shackListing es un control ListBox, y shackSet en un ICollection. Sin embargo, cada vez que agrego algo a shackSet después de agregar el primer elemento, veo varios elementos en el ListBox. es decir, es como que los elementos recién agregados se agregan a la lista, independientemente de si se agregaron al conjunto. Cuando miro a las firmas de ICollection # Añadir:

void Add(T obj); 

... y HashSet # Añadir:

bool Add(T obj); 

... esto me lleva a creer que hay un error que afecta HashSets envueltos en donde recientemente los elementos agregados se agregan al ListBox independientemente porque el ObservableCollection no tiene forma de saber si el objeto realmente se agregó a la colección subyacente porque el tipo de devolución de ICollection # Add es nulo. Puede alguien mas confirmar esto ?

Respuesta

8

Cuando crea un nuevo ObservableCollection con otra colección, no está envolviendo esa colección, crea una nueva donde todos los elementos de la colección aprobada se copian en el ObservableCollection. Si desea utilizar un ObservableCollection con el único propósito de DataBinding, no busque más, puede enlazar a cualquier IEnumerable en WPF. Desafortunadamente, esto tiene el inconveniente de que WPF no siempre recogerá correctamente los cambios en la colección enlazada. Si esto es un problema que probablemente tiene que crear su propia hashset obeservable:

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: como ya se ha señalado, no se puede heredar de HashSet para implementar INotifyCollectionChanged. Sin embargo, si nos fijamos en el código (utilizando Reflector) para la clase HashSet, es bastante simple que sea demasiado difícil imitar esa funcionalidad usted mismo.

+0

Claro que se puede unir directamente a la hashset, pero si se agrega un elemento de WPF no se dará cuenta. –

+1

+1 para la explicación. Sin embargo, con respecto a la solución propuesta, preferiría heredar de HashSet e implementar INotifyCollectionChanged ... –

+2

+1 a lo que dijo Thomas, excepto que no puede heredar de 'HashSet' y también implementar' INotifyCollectionChanged', ya que los métodos 'HashSet' no son virtuales y no pueden anularse. Tendrá que implementar 'ICollection', y su implementación tendría que _wrap_' HashSet' y aumentar las notificaciones de todos los métodos de mutación. –

0

Como dijo bitbonk, ObservableCollection no ajusta el Hashset sino que copia sus elementos en su lugar.

Si quieren un observable hashset echa un vistazo a How can I make an Observable Hashset in C# ?

+0

¿Tiene Microsoft algún mecanismo para enviar solicitudes de funciones? Me parece que un Conjunto Observable (Hash) es algo que ya debería estar en el framework .NET. –

+0

Claro, en Microsoft Connect. http://connect.microsoft.com/VisualStudio –

1

De acuerdo con la respuesta de bitbonk, pero quería anular el método add (T artículo), pero no se puede, por lo que creó un (artículo T) append método en su lugar:

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

Y luego en mi código detrás:

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

en lo anterior tengo dos cuadros de lista, listboxAvailableColumns, que tiene una lista de cadenas tha El usuario puede seleccionar haciendo doble clic, que agrega la selección al segundo cuadro de lista, listboxConsolidationColumns. No se permiten duplicados, y esto funciona perfectamente con el ObservableSetCollection exactamente como arriba.

El XAML es simplemente:

<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> 
Cuestiones relacionadas