2010-08-31 9 views
17

sto usando un DataGrid associato a un CollectionViewSource (giocatori), vincolata alla voce selezionata di un ListBox (livelli), ogni elemento contenente una collezione da ordinare/visualizzato nel DataGrid:CollectionViewSource l'ordinamento solo la prima volta che viene associato a un'origine

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

(intero codice C# here, il codice XAML here, dell'intero progetto di test here - oltre al DataGrid Ho una semplice ListBox per i giocatori, per assicurarsi che non era un problema di DataGrid)

Il problema è che i giocatori vengono ordinati la prima volta che vengono mostrati, ma non appena seleziono un altro livello dal ListBox, non vengono più ordinati. Inoltre, modificando i nomi la prima volta che i giocatori vengono mostrati li ordinerai in base alle modifiche, ma non più una volta che il livello è stato cambiato.

Quindi sembra che la modifica dell'origine di CollectionViewSource interrompa in qualche modo la funzionalità di ordinamento, ma non ho idea del perché, né di come risolverlo. Qualcuno sa cosa sto sbagliando?

(ho fatto un test con un filtro, ma che si continuò a lavorare come previsto)

Il quadro è NET 4.

+0

Ho sperimentato la stessa cosa prima - invece di creare ogni volta un nuovo oggetto, puoi rimuovere e reinserire il suo contenuto? – Dave

+0

Oltre al lavoro extra, che frammenterebbe inutilmente l'heap gestito creando/rilasciando oggetti, preferirei evitarlo se potessi. – RedGlyph

risposta

10

grande domanda e una interessante osservazione. Dopo un'attenta ispezione, sembra che DataGrid cancelli le descrizioni di ordinamento di una precedente ItemsSource prima che ne venga impostato uno nuovo. Ecco il suo codice per OnCoerceItemsSourceProperty:

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

Questo comportamento si verifica solo su un DataGrid. Se invece hai usato un ListBox (per visualizzare la raccolta "Giocatori" sopra), il comportamento sarà diverso e le Ordinatorie rimarranno dopo aver selezionato elementi diversi dal datagrid principale.

Quindi immagino che la soluzione a questo è di riapplicare in qualche modo le descrizioni di ordinamento della collezione di giocatori ogni volta che l'elemento selezionato nel padre DataGrid (cioè "lstLevel") cambia.

Tuttavia, non ne sono sicuro al 100% e probabilmente ho bisogno di più test/indagini. Spero di essere stato in grado di contribuire con qualcosa. =)

EDIT:

Come una soluzione proposta, si può mettere un gestore per lstLevel.SelectionChanged nel costruttore, prima di impostare la proprietà lstLevel.ItemsSource. Qualcosa di simile a questo:

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

lstLevel.ItemsSource = levels; 

EDIT2:

In risposta ai problemi che stai incontrando per quanto riguarda la navigazione da tastiera, suggerisco che invece di gestire l'evento "CurrentChanged", a gestire il lstLevel Evento .SelectionChanged invece. Sto postando gli aggiornamenti necessari che devi fare qui sotto.Basta copiare e incollare il codice e vedere se funziona correttamente.

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> 

Codice-dietro (costruttore):

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

++, molto perspicace! Ho aggiunto un ListBox separato perché non mi fido del DataGrid, ma non ho provato a testare il ListBox da solo senza il DataGrid. Più sto usando questo controllo, più lo dimostra eccentrico (3 bug già segnalati negli ultimi 2 giorni). Inizierò dal suggerimento e ti faccio sapere. Grazie! – RedGlyph

+1

Adattato alcuni bit (non ha funzionato così com'è, ma abbastanza vicino), una domanda aggiornata con soluzione alternativa. Grazie ancora! – RedGlyph

+0

Felice di aiutare. Sì, ho trovato abbastanza sorprendente il fatto che DataGrid stesse cancellando le SortDescriptions dietro le quinte. Non sono sicuro del perché, ma probabilmente c'è una buona ragione per farlo ... o sì, forse è un bug. =) – ASanch

4

Una soluzione migliore: CollectionViewSource sorting only the first time it is bound to a source

implementare il proprio DataGrid:

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

L'unica cosa che coattiva callback nell'implementazione corrente è cancellare le descrizioni degli ordinamenti. Puoi semplicemente "tagliare" questo codice con metadati sovrascrivendo. Non valido su Silverlight: l'API OverrideMetadata non è pubblica. Anche se non sono sicuro che Silverlight sia affetto da questo bug . Possono essere applicati altri rischi ed effetti collaterali.

+0

Anche se ci sono rapporti che questa soluzione non avrebbe funzionato, sembra che faccia il suo lavoro nel mio codice. – linac

5

Sono stato in grado di risolvere questo problema semplicemente chiamando PropertyChanged sulla proprietà che espone la vista, lasciando che la vista aggiorni (e cancelli l'ordinamento) e quindi aggiungendo le descrizioni di ordinamento.

+0

+ 1. Anche questo sembra funzionare per me. La soluzione più semplice senza problemi di code-behind. Grazie! – Anttu

Problemi correlati