2012-06-15 10 views
8

Ich wollte an eine ObservableCollection in XAML binden und auch die Gruppierung dort anwenden. Im Prinzip hat das gut funktioniert.Wie kann ich die CollectionView, die in XAML definiert ist

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

Dann wurde die Datenbindung Ausdruck ItemsSource="{Binding Source={StaticResource ResourceKey=cvs}}" anstelle von ItemsSource="{Binding Path=TestTemplates}".

Zuerst schien alles cool, bis ich die Benutzeroberfläche aus dem Ansichtsmodell aktualisieren wollte. Das Problem ist, dass CollectionViewSource.GetDefaultView(TestTemplates) eine andere Ansicht als die von XAML zurückgegeben hat, wo die Gruppierung angewendet wurde. Daher konnte ich keine Auswahl treffen oder irgendetwas Nützliches damit machen.

Ich könnte es beheben, indem Sie die Liste erneut direkt an die Eigenschaft des Ansichtsmodells binden und die Gruppierung im Code-Behind einrichten. Aber ich bin nicht so glücklich mit dieser Lösung.

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

Ich gehe davon aus, ist der Grund für die bereits given by John Skeet here.

Trotzdem würde ich erwarten, dass es einen Weg geben sollte, um die richtige Sicht zu bekommen. Liege ich falsch?

+0

Sie gehen in die falsche Richtung. Eine VM sollte keine Kenntnis von der Ansicht haben. Wenn Sie die Ansicht aktualisieren möchten, stellen Sie sicher, dass es sich bei der Eigenschaft, an die sie gebunden ist, entweder um eine ObservableCollection oder Ihren Code handelt, der beim Ändern der Auflistung explizit NotifyPropertyChanged ausgelöst hat. –

+0

@PanagiotisKanavos: Das Zeug in der Listenansicht ist tatsächlich * ist * in einer 'ObservableCollection' und die Elemente in der Benutzeroberfläche werden bei einer Änderung der Eigenschaften aktualisiert. Aber die Gruppierung respektiert das nicht. Eine bekannte Problemumgehung besteht darin, das Update zu erzwingen, d.h. 'CollectionViewSource.GetDefaultView (...) .Refresh'. – primfaktor

+0

In .NET 4.5 wird dies mit dem [ICollectionViewLiveShaping] (http://msdn.microsoft.com/en-us/library/system.componentmodel.iclectionviewliveshaping (v = vs.110) .aspx) behoben. – primfaktor

Antwort

1

Einen Weg gefunden, basierend auf J. Lennon's Antwort. Wenn ich mit meinem Befehl etwas übermittele, das Zugriff auf die Ressourcen hat, kann ich dort die CollectionViewSource nachschlagen.

In XAML (CollectionViewResource wie oben):

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

Und in dem VM-Code:

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

Die Execute ist derjenige, der auf die RelayCommand gegeben.

Dies würde die Frage beantworten, aber ich mag es nicht sehr. Meinungen?

+0

Da dies vollständiger ist (und funktioniert) als die Basis von [J. Lennon] (http://stackoverflow.com/users/1128106/j-lennon), ich markiere das (aber er hat die Bounty). – primfaktor

5

Ich neige dazu, nur die von der VM Sammlung Ansicht aussetzen, anstatt haben die Sicht es definieren:

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

public ICollectionView EmployeesView 
{ 
    get { ... } 
} 

Auf diese Weise Ihre VM über die volle Kontrolle hat, was zu der Ansicht, ausgesetzt ist. Es kann beispielsweise die Sortierreihenfolge als Reaktion auf eine Benutzeraktion ändern.

+0

Das ist ein guter Weg, aber es beantwortet die Frage nicht. Ich würde es trotzdem gerne wissen. – primfaktor

+0

Was meinst du mit "aktualisieren Sie die Ansicht" und warum möchten Sie es tun? Wenn die zugrunde liegende Sammlung, die von Ihrer VM bereitgestellt wird, 'INotifyCollectionChanged' implementiert (z. B. mit 'ObservableCollection '), sollte die von Ihrer Ansicht erstellte Sammlungsansicht auf dem neuesten Stand bleiben, damit Sie die" Aktualisierung der Ansicht "erschweren. –

+1

Ich hätte das oben genannte qualifizieren müssen. Solange Sie nur Elemente hinzufügen/entfernen, sollte die Sammlungsansicht sortiert bleiben. Wenn Sie die zugrunde liegende Eigenschaft ändern, nach der Sie sortieren, ist dies ein wichtiger Grund, warum Sie "aktualisieren" müssen. Wenn Sie wirklich möchten, dass in der Ansicht der Lebenslauf und nicht die VM erstellt wird, müssen Sie in Ihrer VM jedes Mal ein Ereignis auslösen, wenn die Ansicht aktualisiert wird. IMHO, es ist einfach sauberer mit der VM alles zu handhaben, da es sowieso Geschäftslogik ist. –

5

Sie könnten das nicht einfach tun?

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

Wenn die Daten verbunden sind, nehme ich an, dass es eine aktualisierte Ansicht haben wird.

+0

Dies wurde auch von einem Kollegen angesprochen. Das Problem ist, dass ich in der VM nicht auf die 'FindResource' eines' FrameworkElement' (separate Assemblys, nur Vs-Referenz-VMs) stoße. – primfaktor

+0

Da Ihre Antwort am nächsten kommt, sollten Sie das Kopfgeld haben. Gib es weise aus. ;-) – primfaktor