CollectionViewSource WPF
-
Hallo zusammen.
Eine Frage zur CollectionViewSource in WPF
Habe in meinem ViewModel eine CollectionViewSource. Deren Source verweist auf eine ObservableCollection<MyType> die auch im ViewModel enthalten ist.
Auf meiner View binde ich nun eine Listbox an diese CollectionViewSource.
Profiles heißt die CollectionViewSource
<ListBox Height="200" DisplayMemberPath="Name" ItemsSource="{Binding Profiles.View}" />
Nun habe ich noch eine ListView auf meiner View. Hier würde ich ebenfallas gerne auf die CollectionViewSource verweisen. Allerdings auf eine Liste die wiederum in meiner darunterliegenden Klasse also MyType enhalten ist. Also soll in der ListView die Liste des aktuell angewählten Objekts der Listbox angezeigt werden. Machines heißt die Liste in MyType.
Habe das mal so getestet.
<ListView Height="100" DisplayMemberPath="Machines" ItemsSource="{Binding Profiles.View}"> <ListView.View> <GridView> <GridViewColumn Width="150" DisplayMemberBinding="{Binding Name}" Header="Name" /> <GridViewColumn Width="50" DisplayMemberBinding="{Binding Type}" Header="Typ" /> </GridView> </ListView.View> </ListView>
Nun wird aber in der Listview das selbe angezeigt wie in der Listbox.
Was mache ich falsch?
-
Mach ein Binding auf das "SelectedItem" in deiner ListBox und dann setz den ItemsSource auf die Collection in deiner Klasse
-
Dein Binding ist ja beides Mal auf die gleiche Collection. Du kannst das aber ganz einfach lösen. Füge dem zweiten Binding noch ein / an. Damit bindest du immer das
CurrentItem
derView
.Also:
<ListBox Height="200" DisplayMemberPath="Name" ItemsSource="{Binding Profiles.View}" IsSynchronizedWithCurrentItem="True" /> <ListView Height="100" DisplayMemberPath="Machines" ItemsSource="{Binding Profiles.View/}" > <ListView.View> <GridView> <GridViewColumn Width="150" DisplayMemberBinding="{Binding Name}" Header="Name" /> <GridViewColumn Width="50" DisplayMemberBinding="{Binding Type}" Header="Typ" /> </GridView> </ListView.View> </ListView>
Das
IsSynchronizedWithCurrentItem="True"
ist nur zur Sicherheit, sollte auch ohne gehen.Grüssli
-
Hallo Dravere
Habe deinen Code getestet. Leider wird nun in der Listview gar nichts mehr angezeigt.
Habe hier eine funktionierende Lösung:
<ListBox Height="200" DisplayMemberPath="Name" ItemsSource="{Binding Profiles.View}" /> <ListView Height="100" DataContext="{Binding Profiles.View}" ItemsSource="{Binding Machines}"> <ListView.View> <GridView> <GridViewColumn Width="150" DisplayMemberBinding="{Binding Name}" Header="Name" /> <GridViewColumn Width="50" DisplayMemberBinding="{Binding Type}" Header="Typ" /> <GridViewColumn Width="50" DisplayMemberBinding="{Binding Build}" Header="Bauart" /> <GridViewColumn Width="50" DisplayMemberBinding="{Binding No}" Header="Nummer" /> </GridView> </ListView.View> </ListView> </StackPanel>
Bin hier über den Datencontext gegangen. Ob das auch noch anders geht und wann man Datacontext überhaupt verwenden soll weiß ich nicht genau.
-
@braunbär,
Ah, jetzt verstehe ich überhaupt erst, was du ursprünglich erreichen wolltest. Daher diesesDisplayMemberPath
imListView
. Hatte mich noch über dieses Property gewundert, welches vonListView
ja gar nicht berücksichtig wird. Du willst also in derListView
auf das PropertyMachines
des selektierten Objektes derListBox
binden?Dann musst du nicht ein / hinzufügen, sondern ein /Machines
<ListBox Height="200" DisplayMemberPath="Name" ItemsSource="{Binding Profiles.View}" IsSynchronizedWithCurrentItem="True" /> <ListView Height="100" ItemsSource="{Binding Profiles.View/Machines}" > <ListView.View> <GridView> <GridViewColumn Width="150" DisplayMemberBinding="{Binding Name}" Header="Name" /> <GridViewColumn Width="50" DisplayMemberBinding="{Binding Type}" Header="Typ" /> </GridView> </ListView.View> </ListView>
Siehe dazu: http://msdn.microsoft.com/en-us/library/ms752347.aspx#current_record_pointers
Grüssli
-
Na das ist mal interessant. Das wußte ich bisher auch noch nicht. Dank dir.
-
braunbär schrieb:
Na das ist mal interessant. Das wußte ich bisher auch noch nicht.
Ich habe es auch erst relativ spät mitbekommen. Irgendwie stand das nie in einem Tutorial oder ähnliches und ich treffe es auch heute noch selten an, dabei ist es äusserst praktisch.
Ähnlich wie auch diese nette Funktion:
http://msdn.microsoft.com/en-us/library/system.windows.data.collectionviewsource.getdefaultview.aspxObwohl man eine
ObservableCollection
an die View übergibt, kommt man trotzdem an dasICollectionView
ran und somit auch an das selektierte Item, bzw. den entsprechenden Events. Man kann auch das selektierte Item damit setzen (MoveCurrentToXXX
). Das ganze lässt sich dann auch prima in Unit Tests integrieren.Grüssli
-
Hallo nochmal
Da hätte ich gleich noch eine Frage zum CurrentItem einer CollectionViewSource.
Habe hier ein seltsames Verhalten.Im XAML funktioniert das mit dem CurrentItem, allerdings habe ich im Code ein Problem. Greife ich auf das CurrentItem in der View zu erhalte ich egal was in der ListView ausgewählt ist das erste Objekt in der List. Das allerdings nur beim ersten Mal.
Wähle ich dann erneut einen Eintrag aus der ListView aus steht in CurrentItem das richtige drin.
Was läuft da schief.
-
Das mit dem GetDefaultView habe ich auch nicht ganz verstanden.
Wenn ich doch das CurrentItem will gehe ich doch einfach über .View.CurrentItem
Wie wende ich GetDefaultView() an. Und was übergebe ich als Parameter. Das ist für mich noch verwirrend.
-
1. Sorry nicht ListView sondern ListBox
2. Ok das mit dem GetDefaultView ist mir nun klar
Aber das Problem besteht auch hier in CurrentItem steht nicht drin was in der Liste ausgewählt wurde.
-
Hast du
IsSynchronizedWithCurrentItem
aufTrue
gesetzt?Grüssli
-
Ja habe ich.
Habe nun mal ein ganz einfaches Projekt gemacht.
Eine ObservableCollection mit String. Diese habe ich der Source einer CollectionViewSource zugewiesen. Diese wiederum an eine ListView gebunden.
Über einen Button lese ich auch hier nun das CurrentItem aus. Aber auch hier wird beim ersten mal immer der erste Eintrag angezeigt, egal was ausgewählt ist.
Auch in der Liste wird nach Abfrage des CurrentItem der erste Wert selektiert.
Verstehe ich nicht. Wie kann eine Abfrage eines Objekts den das Objekt ändern.
-
braunbär schrieb:
Eine ObservableCollection mit String. Diese habe ich der Source einer CollectionViewSource zugewiesen. Diese wiederum an eine ListView gebunden.
Hast du auch mal die
CollectionViewSource
ausgelassen? Wozu brauchst du diese überhaupt und wo erstellst du sie? XAML oder Code?Kannst du dein kurzes Beispiel hier präsentieren? Dann können wir ebenfalls damit testen. Oder sehen allenfalls gleich den Fehler
Grüssli
-
So das ganz einfache Beispiel
public partial class MainWindow : Window { public ObservableCollection<string> InternalHouses { get; set; } public CollectionViewSource Houses { get; set; } public MainWindow() { InitializeComponent(); DataContext = this; InternalHouses = new ObservableCollection<string>(); InternalHouses.Add("house1"); InternalHouses.Add("house2"); InternalHouses.Add("house3"); Houses = new CollectionViewSource { Source = InternalHouses }; } private void Button_Click(object sender, RoutedEventArgs e) { var house = (string)Houses.View.CurrentItem; } }
<StackPanel> <Button Click="Button_Click">Actual House</Button> <Label>Houses:</Label> <ListView Height="200" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Houses.View}" /> </StackPanel>
In der ListView ist nun das erste Objekt ausgewählt. Wähle ich nun ein anderes Objekt aus. z.B. "House 2" und klicke dann den Button steht in CurrentItem "House 1" und in der ListView wird auch "House 1" wieder ausgewählt. Wähle ich nun nochmal "House 2" aus und klicke den Button steht in CurrentItem auch "House 2" drin
-
So funktioniert es:
public partial class MainWindow : Window { public ObservableCollection<string> Houses { get; set; } public MainWindow() { InitializeComponent(); DataContext = this; Houses = new ObservableCollection<string>(); Houses.Add("house1"); Houses.Add("house2"); Houses.Add("house3"); } private void Button_Click(object sender, RoutedEventArgs e) { var house = (string)CollectionViewSource.GetDefaultView(this.Houses).CurrentItem; } }
Ich bin mir aktuell nur noch nicht ganz klar, wieso dem so ist, und werde mal danach suchen. Allerdings habe ich selber noch nie eine
CollectionViewSource
direkt im Code erstellt. Ich sehe keinerlei Vorteil darin. Überlass dies doch WPF.Grüssli
-
Also das ganze kommt von hier:
Wenn ich in meinem Code das CurrentItem, direkt nachdem ich die CollectionViewsource erstellt habe, einmal auslese. Dann funktioniert das nachher mit dem CurrentItem im Buttonhandler auch.
Oder ich kann es auch mit MoveCurrentTo erstmal setzen dann funktioniert es auch.Meiner Meinung ist das ein Bug.
-
braunbär schrieb:
Also das ganze kommt von hier:
http://www.xamlplayground.org/post/2009/07/18/Use-CollectionViewSource-effectively-in-MVVM-applications.aspxIch würde mal sagen, dem Mann war nicht bekannt, dass es die Funktion
CollectionViewSource.GetDefaultView
gibt. WPF erstellt automatisch eineCollectionViewSource
, wenn man eine Liste an ein Control bindet. Es wird nie z.B. direkt dieOberservableCollection
angebunden.Daher kann man die
ObservableCollection
binden und falls man an die dazugehörigeICollectionView
kommen möchte, kann manCollectionViewSource.GetDefaultView
aufrufen.Grüssli
-
Achso die CollectionViewSource wird automatisch erstellt. Dachte ich muss die im XAML selber erstellen. Ja dann macht mir auch GetDefaultView sehr viel Sinn. Vielen Dank.
Das andere ist meiner Meinung aber trotzdem ein bug