2010-08-31 13 views
17

estoy usando una cuadrícula de datos unido a un CollectionViewSource (jugadores), vinculada al elemento seleccionado de un cuadro de lista ( niveles), cada elemento que contenga una colección que desee ordenar/que aparece en la cuadrícula de datos:CollectionViewSource clasificación sólo la primera vez que se enlaza a un origen

<ListBox Name="lstLevel" 
     DisplayMemberPath="Name" 
     IsSynchronizedWithCurrentItem="True" /> 

...

<!-- DataGrid source, as a CollectionViewSource to allow for sorting and/or filtering --> 
<CollectionViewSource x:Key="Players" 
         Source="{Binding ElementName=lstLevel, 
             Path=SelectedItem.Players}"> 
    <CollectionViewSource.SortDescriptions> 
    <scm:SortDescription PropertyName="Name" /> 
    </CollectionViewSource.SortDescriptions> 
</CollectionViewSource> 

...

<DataGrid Name="lstPlayers" AutoGenerateColumns="False" 
      CanUserSortColumns="False" 
      ItemsSource="{Binding Source={StaticResource Players}}"> 
    <DataGrid.Columns> 
     <DataGridTextColumn Header="Name" 
          Binding="{Binding Path=Name, Mode=TwoWay}" 
          Width="*" /> 
     <DataGridTextColumn Header="Age" 
          Binding="{Binding Path=Age, Mode=TwoWay}" 
          Width="80"> 
     </DataGridTextColumn> 
    </DataGrid.Columns> 
    </DataGrid> 

(conjunto de código C# here, el código XAML here, todo proyecto de prueba here - además de la cuadrícula de datos He añadido un cuadro de lista simple para los jugadores, para asegurarse de que no era una cuestión DataGrid)

El problema es que los jugadores se ordenan la primera vez que se muestran, pero tan pronto como selecciono otro nivel del ListBox, ya no se ordenan. Además, modificar los nombres la primera vez que se muestran los jugadores los clasificará de acuerdo a los cambios, pero ya no se aplicará una vez que se haya cambiado el nivel.

Parece que cambiar la fuente de CollectionViewSource de alguna manera rompe la función de ordenación, pero no tengo ni idea de por qué ni cómo solucionarlo. ¿Alguien sabe lo que estoy haciendo mal?

(Hice una prueba con un filtro, pero que uno siguió trabajando como se esperaba)

El marco es .NET 4.

+0

He experimentado lo mismo antes: en lugar de crear un objeto nuevo cada vez, ¿puede quitar y volver a insertar su contenido? – Dave

+0

Además del trabajo extra, que fragmentaría innecesariamente el montón administrado al crear/liberar objetos, preferiría evitarlo si pudiera. – RedGlyph

Respuesta

10

gran pregunta y una observación interesante. Tras una inspección más cercana, parece que DataGrid borra las descripciones de clasificación de una ItemsSource anterior antes de que se establezca una nueva. Aquí es su código de OnCoerceItemsSourceProperty:

private static object OnCoerceItemsSourceProperty(DependencyObject d, object baseValue) 
{ 
    DataGrid grid = (DataGrid) d; 
    if ((baseValue != grid._cachedItemsSource) && (grid._cachedItemsSource != null)) 
    { 
     grid.ClearSortDescriptionsOnItemsSourceChange(); 
    } 
    return baseValue; 
} 

Este comportamiento sólo ocurre en una cuadrícula de datos. Si usó un ListBox en su lugar (para mostrar la colección de "Jugadores" anterior), el comportamiento será diferente y SortDescriptions permanecerá después de seleccionar diferentes elementos de la cuadrícula de datos principal.

Así que supongo que la solución a esto es volver a aplicar las descripciones de ordenación de la colección Players cada vez que cambie el elemento seleccionado en el DataGrid primario (es decir, "lstLevel").

Sin embargo, no estoy 100% seguro de esto y probablemente necesite más pruebas/investigación. Sin embargo, espero haber contribuido algo. =)

EDIT:

Como solución sugerida, puede poner un controlador para lstLevel.SelectionChanged en su constructor, para establecer la propiedad lstLevel.ItemsSource. Algo como esto:

lstLevel.SelectionChanged += 
    (sender, e) => 
    { 
     levels.ToList().ForEach((p) => 
     { 
      CollectionViewSource.GetDefaultView(p.Players) 
       .SortDescriptions 
       .Add(new SortDescription("Name", ListSortDirection.Ascending)); 
     }); 
    }; 

lstLevel.ItemsSource = levels; 

Edit2:

En respuesta a los problemas que estamos encontrando con respecto a la navegación mediante el teclado, sugiero que en lugar de controlar el evento "CurrentChanged", manejar la lstLevel .SelectionChanged event en su lugar. Estoy publicando las actualizaciones necesarias que debes hacer a continuación.Solo copie y pegue su código y vea si funciona bien.

XAML:

<!-- Players data, with sort on the Name column --> 
<StackPanel Grid.Column="1"> 
    <Label>DataGrid:</Label> 
    <DataGrid Name="lstPlayers" AutoGenerateColumns="False" 
     CanUserSortColumns="False" 
     ItemsSource="{Binding ElementName=lstLevel, Path=SelectedItem.Players}"> 
     <DataGrid.Columns> 
      <DataGridTextColumn Header="Name" 
         Binding="{Binding Path=Name, Mode=TwoWay}" 
         Width="*" /> 
      <DataGridTextColumn Header="Age" 
         Binding="{Binding Path=Age, Mode=TwoWay}" 
         Width="80"> 
      </DataGridTextColumn> 
     </DataGrid.Columns> 
    </DataGrid> 
</StackPanel> 

<StackPanel Grid.Column="2"> 
    <Label>ListBox:</Label> 
    <ListBox ItemsSource="{Binding ElementName=lstLevel, Path=SelectedItem.Players}" DisplayMemberPath="Name" /> 
</StackPanel> 

de código subyacente (constructor):

lstLevel.SelectionChanged += 
    (sender, e) => 
    { 
     levels.ToList().ForEach((p) => 
     { 
      CollectionViewSource.GetDefaultView(p.Players) 
       .SortDescriptions 
       .Add(new SortDescription("Name", ListSortDirection.Ascending)); 
     }); 
    }; 
lstLevel.ItemsSource = levels; 
+0

++, muy perspicaz! Agregué un ListBox separado porque no confiaba en DataGrid, pero no intenté probar el ListBox solo sin DataGrid. Cuanto más uso este control, más peculiar resulta (3 errores ya informados en los últimos 2 días). Comenzaré por la sugerencia y te haré saber. ¡Gracias! – RedGlyph

+1

Adaptado unos pocos bits (no funcionó como está, pero lo suficientemente cerca), pregunta actualizada con solución alternativa. ¡Gracias de nuevo! – RedGlyph

+0

Me alegra ayudar. Sí, me pareció bastante sorprendente que DataGrid limpiara las descripciones de Sort detrás de las escenas. No estoy muy seguro de por qué, pero probablemente haya una buena razón para hacerlo ... o sí, tal vez sea un error. =) – ASanch

4

Una mejor solución: CollectionViewSource sorting only the first time it is bound to a source

implementar su propia cuadrícula de datos:

public class SDataGrid : DataGrid 
{ 
    static SDataGrid() 
    { 
     ItemsControl.ItemsSourceProperty.OverrideMetadata(typeof(SDataGrid), new FrameworkPropertyMetadata((PropertyChangedCallback)null, (CoerceValueCallback)null)); 
    } 
} 

Lo único que coacciona la devolución de llamada en la implementación actual es borre las descripciones de clasificación. Simplemente puede "cortar" este código al anulando los metadatos. No es viable en Silverlight: la API OverrideMetadata no es pública. Aunque no estoy seguro de que Silverlight se vea afectado por este error . Pueden aplicarse otros riesgos y efectos secundarios.

+0

Aunque hay informes de que esta solución no funcionaría, parece que es un trabajo en mi código. – linac

5

Pude solucionar esto simplemente llamando a PropertyChanged en la propiedad que expone la vista, permitiendo que la vista se actualice (y borre el orden) y agregue las descripciones de clasificación.

+0

+ 1.This también pareció funcionar para mí. La solución más simple sin problemas de código subyacente. ¡Gracias! – Anttu

Cuestiones relacionadas