2012-06-15 9 views
8

Ho voluto associare a un ObservableCollection in XAML e anche applicare il raggruppamento lì. In linea di principio, questo ha funzionato bene.Come posso ottenere il CollectionView definito in XAML

<UserControl.Resources> 
    <CollectionViewSource x:Key="cvs" Source="{Binding Path=TestTemplates}"> 
     <CollectionViewSource.SortDescriptions> 
      <scm:SortDescription PropertyName="Title"/> 
     </CollectionViewSource.SortDescriptions> 
     <CollectionViewSource.GroupDescriptions> 
      <PropertyGroupDescription PropertyName="TestCategory"/> 
     </CollectionViewSource.GroupDescriptions> 
    </CollectionViewSource> 
</UserControl.Resources> 

Poi i dati espressione di associazione divenne ItemsSource="{Binding Source={StaticResource ResourceKey=cvs}}" invece di ItemsSource="{Binding Path=TestTemplates}".

All'inizio, tutto sembrava interessante, fino a quando non volevo aggiornare l'interfaccia utente dal modello di visualizzazione. Il problema è che CollectionViewSource.GetDefaultView(TestTemplates) ha restituito una visualizzazione diversa da quella di XAML in cui è stato applicato il raggruppamento. Pertanto, non ho potuto impostare la selezione o fare qualcosa di utile con esso.

È possibile correggerlo legando nuovamente l'elenco direttamente alla proprietà del modello di visualizzazione e impostando il raggruppamento nel code-behind. Ma non sono così felice con questa soluzione.

private void UserControlLoaded(object sender, RoutedEventArgs e) 
{ 
    IEnumerable source = TemplateList.ItemsSource; 
    var cvs = (CollectionView)CollectionViewSource.GetDefaultView(source); 
    if (cvs != null) 
    { 
     cvs.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending)); 
     cvs.GroupDescriptions.Add(new PropertyGroupDescription("TestCategory")); 
    } 
} 

Suppongo che il motivo sia già given by John Skeet here.

Tuttavia, mi aspetto che ci sia un modo per ottenere la vista giusta. Ho sbagliato?

+0

Stai andando sul modo sbagliato. Una VM non dovrebbe avere alcuna conoscenza della vista. Se si desidera aggiornare la vista, assicurarsi che la proprietà a cui si lega sia ObservableCollection o che il proprio codice sollevi NotifyPropertyChanged esplicitamente quando si modifica la raccolta. –

+0

@PanagiotisKanavos: la roba nella visualizzazione elenco in realtà * è * in un 'ObservableCollection' e gli elementi nell'interfaccia utente si aggiornano su una modifica di proprietà. Ma il gruppo non lo rispetta. Una soluzione nota è forzare l'aggiornamento, ad esempio "CollectionViewSource.GetDefaultView (...) .Refresh'. – primfaktor

+0

In .NET 4.5, questo verrà risolto con [ICollectionViewLiveShaping] (http://msdn.microsoft.com/en-us/library/system.componentmodel.icollectionviewliveshaping (v = vs.110) .aspx). – primfaktor

risposta

1

Trovato un modo, in base alla risposta J. Lennon's. Se passo qualcosa che ha accesso alle risorse con il mio comando, allora posso cercare il CollectionViewSource lì.

In XAML (CollectionViewResource come sopra):

<Button Command="{Binding Command}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}">Do it!</Button> 

E nel codice VM:

private void Execute(object parm) 
{ 
    var fe = (FrameworkElement)parm; 
    var cvs = (CollectionViewSource)fe.FindResource("cvs"); 
    cvs.View.Refresh(); 
} 

Il Execute è quella che viene dato al RelayCommand.

Questo risponderebbe alla domanda, ma non mi piace molto. Opinioni?

+0

Poiché questo è più completo (e funzionante) della base di [J. Lennon] (http://stackoverflow.com/users/1128106/j-lennon), contrassegno questo (ma ha ottenuto la taglia). – primfaktor

5

tendo a esporre solo la vista collezione dal VM, piuttosto che avere la vista definirlo:

public ICollection<Employee> Employees 
{ 
    get { ... } 
} 

public ICollectionView EmployeesView 
{ 
    get { ... } 
} 

In questo modo il vostro VM ha il pieno controllo su ciò che viene esposto alla vista. Ad esempio, può modificare l'ordinamento in risposta a un'azione dell'utente.

+0

Questo è un buon modo per farlo, ma non risponde alla domanda. Mi piacerebbe ancora saperlo. – primfaktor

+0

Cosa intendi con "aggiorna la vista" e perché vuoi farlo? Se la collezione sottostante esposta dalla tua VM implementa 'INotifyCollectionChanged' (ad esempio con' ObservableCollection '), la vista raccolta creata dalla tua visualizzazione dovrebbe rimanere aggiornata, risparmiando così il problema di "aggiornare la vista". –

+1

Avrei dovuto qualificarmi come sopra. Finché si aggiungono/rimuovono solo gli elementi, la vista di raccolta dovrebbe rimanere ordinata. Se stai modificando la proprietà sottostante da cui stai ordinando, questa è una ragione valida per cui dovresti "aggiornare". Se vuoi davvero che la vista crei il CV piuttosto che la VM, dovrai generare un evento nella tua VM ogni volta che la vista dovrebbe aggiornarsi. IMHO, è solo più pulito avere la VM gestire tutto, dal momento che è comunque la logica di business. –

5

Non potevi farlo?

var _viewSource = this.FindResource("cvs") as CollectionViewSource; 

Se i dati sono connessi, presumo che avrà una vista aggiornata.

+0

Anche questo è stato sollevato da un collega. Il problema è che nella VM, non riesco ad accedere a 'FindResource' di alcun' FrameworkElement' (assembly separati, solo VM di riferimento di Vs). – primfaktor

+0

Come la tua risposta si avvicina, avrai la taglia. Spenderlo saggiamente ;-) – primfaktor