Laden...

CollectionViewSource.GetDefaultView oder besser new ListCollectionView?

Erstellt von bb1898 vor 7 Jahren Letzter Beitrag vor 7 Jahren 5.879 Views
B
bb1898 Themenstarter:in
112 Beiträge seit 2008
vor 7 Jahren
CollectionViewSource.GetDefaultView oder besser new ListCollectionView?

Ausgangssituation: ich habe in meinen Daten irgendeine Sorte von Liste, will in einem WPF-Fenster daran binden und habe Gründe, im ViewModel nicht direkt auf die Liste zuzugreifen, sondern auf eine zugehörige CollectionView-Property.

Dann kann ich im Prinzip beides machen:

public class MainViewModel 
{
	public ObservableCollection<Zeugs> _meinKram;
	
	public ListCollectionView MeinKram { get; private set; }
	
	public MainViewModel()
	{
		_meinKram = HoleMeineDaten(irgend, welche, parameter);
		// Variante 1:
		MeinKram = (ListCollectionView)CollectionViewSource.GetDefaultView(
			_meinKram);
		// oder Variante 2:
		// MeinKram = new ListCollectionView(_meinKram);
		// ... alles, was sonst noch nötig ist
	}
	
	private ObservableCollection<Zeugs> HoleMeineDaten(parameter):
	{
		// irgendetwas
	}
	
	// ...
}

Ich finde im Netz und in meinen Lehrbüchern keine Erklärung zur Wahl zwischen beiden Varianten. Variante 1 scheint öfter benutzt zu werden, Variante 2 kommt aber auch vor.

Meine eigenen Experimente haben bisher eine Situation ergeben, in der nur Variante 1 das gewünschte Ergebnis hat - und da verstehe ich gar nicht, warum:

Die Daten enthalten eine Nachschlagetabelle, nennen wir sie Rubriken, und eine Datentabelle, nennen wir sie Dinge. Jedes Ding gehört zu genau einer Rubrik. In der Anwendung werden nur Dinge bearbeitet, hinzugefügt, evtl. gelöscht, die Rubriktabelle bleibt unverändert.

Ein Fenster hat drei Bereiche: Eine Listbox, einen Suchbereich, einen Detailbereich zur Bearbeitung eines Ding-Datensatzes.

Der Suchbereich hat u.a. eine Combobox zur Suche nach allen Dingen einer Rubrik. Der Detailbereich hat auch eine Combobox für Rubriken, hier soll dem einzelnen Satz die richtige Rubrik zugeordnet werden (diese Zuordnung kann sich schon mal ändern und muss jedenfalls für neue Sätze gesetzt werden). Die Listbox dient zur Anzeige des Suchergebnisses und zur Auswahl eines Satzes daraus.

Beide Comboboxen haben die gleiche ListCollectionView-Property des ViewModels als ItemsSource. In MainWindow.xaml sieht das so aus:

Im Suchbereich:

<ComboBox Grid.Row="1" Grid.Column="1" Margin="3"
		  ItemsSource="{Binding AlleRubrikenView}"
		  DisplayMemberPath="RubName"
		  IsSynchronizedWithCurrentItem="True" />

Im Detailbereich (das ist ein Grid, der den aktuellen Ding-Datensatz als eigenen DataContext hat):

<ComboBox Grid.Row="2" Grid.Column="1" Margin="3"
		  ItemsSource="{Binding DataContext.AlleRubrikenView, 
				RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
		  DisplayMemberPath="RubName"
		  SelectedValuePath="RubId"
		  SelectedValue="{Binding RubId}" />

Erzeuge ich AlleRubrikenView nach Variante 1 als DefaultView, dann verhält sich das Programm wie gewünscht, insbesondere sind die ausgewählten Rubriken in beiden Comboboxen unabhängig voneinander. Benutze ich Variante 2 und erzeuge AlleRubrikenView neu, dann ist in Suchbereich und Detailbereich immer die gleiche Rubrik ausgewählt, egal, in welchem Bereich ich eine Auswahl treffe. Das führt natürlich zu falschen Ergebnissen.

Ich würde es verstehen, wenn jede Bindung beider Comboboxen an die gleiche ListCollectionView-Property dazu führen würde, dass auch die Auswahl in beiden synchron verändert würde. Aber warum hängt das davon ab, wie die Property erzeugt wurde?

Und vor allem, wo wird das erklärt? In der offiziellen Dokumentation zu ListCollectionView und CollectionViewSource habe ich nichts gefunden, hier im Forum und auf StackOverflow auch nicht. Es ist natürlich immer möglich, dass ich einer Erklärung nicht alles entnommen habe, was drinsteckt. Oder aber falsch gesucht, und da kann mir hier vielleicht jemand mit weiteren Suchbegriffen unter die Arme greifen.

76 Beiträge seit 2006
vor 7 Jahren

Hi bb1898!

Ausgangssituation: ich habe in meinen Daten irgendeine Sorte von Liste, will in einem WPF-Fenster daran binden und habe Gründe, im ViewModel nicht direkt auf die Liste zuzugreifen, sondern auf eine zugehörige CollectionView-Property.

Kannst du etwas zu den Gründen sagen?
In allen Fällen in denen ich mit einer ViewCollection gearbeitet habe, hat mehr Probleme als Vorteile mit sich gebracht. Ich habe dann auch immer einen anderen Weg gefunden meine Probleme zu lösen.

Gruß Patrick

B
bb1898 Themenstarter:in
112 Beiträge seit 2008
vor 7 Jahren

Kannst du etwas zu den Gründen sagen?
In allen Fällen in denen ich mit einer ViewCollection gearbeitet habe, hat mehr Probleme als Vorteile mit sich gebracht. Ich habe dann auch immer einen anderen Weg gefunden meine Probleme zu lösen.

Wichtigster Grund dürften wohl die Fälle sein, in denen die gleiche Liste unterschiedlich sortiert oder gefiltert angezeigt werden soll - also mehrere CollectionViews für eine Collection.

Daneben finde ich sie praktisch für Master-Detail-Darstellungen: beim Wechsel des aktuellen Satzes in der Master-Liste soll die Detail-Liste mit den passenden neuen Sätzen gefüllt werden. Die ViewCollection kennt ihr CurrentItem und hat ein CurrentChanged-Ereignis, die zugrundeliegende Liste hat nichts dergleichen.

Während ich schreibe, kommt mir der Gedanke, dass sich das wahrscheinlich auch direkt im View regeln lässt, wird halt nur die Bindung der Detail-Liste etwas komplizierter. Muss ich mal ausprobieren.

Dein Betrag ist jetzt allerdings der erste, den ich sehe, der vom Arbeiten mit CollectionViews dezidiert abrät, und das mit "mehr Probleme als Vorteile" kann ich aus eigener Kenntnis nicht bestätigen.

Meine ursprüngliche Frage bleibt offen.

T
461 Beiträge seit 2013
vor 7 Jahren

Hey,

also ich hab das gefunden:
ListCollectionView

Im Hinweis ganz unten steht:

When you bind to a data collection, you may want to sort, filter, or group the data. To do that, you use collection views. You can think of a T:System.Windows.Data.CollectionView as the layer on top of the binding source collection that allows you to navigate and display the source collection based on sort, filter, and group queries, all without having to manipulate the underlying source collection itself. If the source collection implements the T:System.Collections.Specialized.INotifyCollectionChanged interface, the changes raised by the E:System.Collections.Specialized.INotifyCollectionChanged.CollectionChanged event are propagated to the views.

All collections have a default T:System.Windows.Data.CollectionView. For all collections implementing T:System.Collections.IList, the T:System.Windows.Data.ListCollectionView object is the default view object. The T:System.Windows.Data.BindingListCollectionView is the collection view class used for collections that implement T:System.ComponentModel.IBindingList. **:::

For more information about collection views, see Data Binding Overview.

Also wird es wohl so sein, daß das Erstellen einer einfachen Instanz wohl nicht automatisch einen 'DefaultView' erzeugt und es nur durch Variante1 umzusetzen ist. (bitte berichtigt mich wenn ich falsch liege)

Hoffe das hilft dir weiter..

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

B
bb1898 Themenstarter:in
112 Beiträge seit 2008
vor 7 Jahren

Den Abschnitt aus der Dokumentation kenne ich schon, aber der besagt ja eigentlich nur, dass es die Default View immer gibt und wie man sie kriegt. Im Prinzip kann man aber beides tun: auf die automatisch erzeugte Default View-Komponente zugreifen und mit der arbeiten oder statt dessen mit einer eigens erzeugten CollectionView-Komponente arbeiten; der muss man natürlich die richtige Collection im Konstruktor übergeben. Oder hinterher die SourceCollection-Property setzen. Wenn ich mich nicht ganz falsch erinnere, wird die Default CollectionView nicht erzeugt, wenn man selbst eine erzeugt (so dass "es gibt sie immer" doch nicht ganz stimmt).

Wenn man mehrere Sortierungen oder Filter haben will, dann geht m.W. nur die Variante mit entsprechend vielen selbst erzeugten CollectionViews. Es finden sich andererseits auch Beispiele, in denen die Default View der ItemsSource einer Listbox o.ä. benutzt wird:


ListCollectionView view = CollectionViewSource.GetDefaultView(lstProducts.ItemsSource) as
    ListCollectionView;

Kommt aus einem nicht mehr ganz taufrischen WPF-Lehrbuch. Hat aber natürlich mit MVVM schon gar nichts zu tun. Bei so einem Verfahren ist die GetDefaultView-Variante mindestens naheliegend, wenn nicht zwingend, aber die Konstruktion ist eh nicht schön.

T
461 Beiträge seit 2013
vor 7 Jahren

🤔 Ok aber war das nicht deine Frage, warum Variante 2 nicht funktioniert? ?(

Bzw. müßte man wissen, was der Unterschied der Konstruktion zwischen der direkten Klassenerstellung und des statischen Konstruktors ist.. Da bin ich jetzt aber auch überfragt! 😁

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

B
bb1898 Themenstarter:in
112 Beiträge seit 2008
vor 7 Jahren

In einer bestimmten Situation, beschrieben in meinem ersten Beitrag, hat Variante 2 nicht funktioniert. In anderen Situationen schon. M.E. gehört der Unterschied der beiden Konstruktionen in der Dokumentation erklärt, aber ich finde nichts.

3.003 Beiträge seit 2006
vor 7 Jahren

Bei solchen Fragen lohnt ein Blick in den Framework-Quellcode. GetDefaultView initialisiert das DataBinding, im Gegensatz zum einfachen Konstruktor.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

R
74 Beiträge seit 2006
vor 7 Jahren

interessante Frage, ob es nur an der Bindung liegt ?

DefaultCollectionView nachverfolgt scheint intern einen eigenen Zugriff
auf die Collection zu realisieren bzw. eine eigene Liste vorzuhalten mit
Referenzen auf die Einträge der Orginal-Collection.

base.Add(new WeakRefKey(collection), cr); //cr == CollectionRecord

Somit sind/wären unabhängige Auswahlen möglich.

Im Gegensatz zu (new ListCollectionView), die eine IList bekommt
und nur [index] zur Verfügung steht für die Zugriffe. Hinter IList verbirgt sich
ein Object für alle mit new ListCollectionView erzeugten Ansichten und
das eine Object hat einen [index];

In anderen von bb1898 genannten Situationen kann es einfach nur sein, dass
unterschiedliche Object(Collections) erzeugt wurden und mit new ListCollectionView
gearbeitet wurde. Dann sind unterschiedliche Auswahlen
auf jeden Fall gleichzeitig möglich.

B
bb1898 Themenstarter:in
112 Beiträge seit 2008
vor 7 Jahren

Bei solchen Fragen lohnt ein Blick in den
>
. GetDefaultView initialisiert das DataBinding, im Gegensatz zum einfachen Konstruktor.

Danke für den Hinweis, habe ich mir gleich mal lesegezeichnet (gelesezeichnet?).

Daneben finde ich sie praktisch für Master-Detail-Darstellungen: beim Wechsel des aktuellen Satzes in der Master-Liste soll die Detail-Liste mit den passenden neuen Sätzen gefüllt werden. Die ViewCollection kennt ihr CurrentItem und hat ein CurrentChanged-Ereignis, die zugrundeliegende Liste hat nichts dergleichen.

Während ich schreibe, kommt mir der Gedanke, dass sich das wahrscheinlich auch direkt im View regeln lässt, wird halt nur die Bindung der Detail-Liste etwas komplizierter. Muss ich mal ausprobieren.

Erfolgreich ausprobiert und ist in der Summe für ein halbwegs gewöhnliches Master-Detail-Szenario sogar eher kürzer, weil die diversen CurrentChanged-Handler entfallen.