ListCollectionView vs CollectionViewSource



  • Folgende 2 Szenarien:

    Habe eine ObservableCollection<string> Names. Diese Combobox binde ich an eine Combobox. Und zusätlich melde ich eine Funktion bei CurrentChanged an:
    CollectionViewSource.GetDefaultView(Names).CurrentChanged += NamesViewCurrentChanged

    Wenn ich hier der ObservableCollection was hinzufüge wird auch immer das CurrentChanged ausgelöst.

    Habe eine List<string> Names und erstelle zusätzlich eine ListCollectionView
    NamesCollectionView = new ListCollectionView(Names);

    Und melde ebenfalls eine Funktion bei CurrentChanged an
    NamesCollectionView.CurrentChanged += NamesViewCurrentChanged;

    Wenn ich hier der Liste was hinzufüge wird das CurrentChanged nicht immer ausgelöst.

    Wleche Regel dahinter steckt habe ich noch nicht herausgefunden.

    Kann mir das jemand erklären


  • Administrator

    Meistens liegt es daran, dass man vergisst Selector.IsSynchronizedWithCurrentItem auf True zu setzen.

    Grüssli



  • Aber in dem Fall liegt es nicht dran. Das das ist gesetzt.

    Dass kann man sogar selber ausprobieren.

    Und zwar gibt es eine Inkonsistenz wenn man versucht das auswählen eines Wertes in einer Combobox, Listbox usw. abzubrechen. Der Wert der in der Liste angezeigt stimmt nicht mehr mit dem CurrentItem überein.

    Dazu habe ich im Netz einen Bugfix gefunden:

    http://coderelief.net/2011/11/07/fixing-issynchronizedwithcurrentitem-and-icollectionview-cancel-bug-with-an-attached-property/

    Im Unterschied zu meiner bisherigen Lösung ObservableCollection und CollectionViewSource wurde hier aber eine List und eine ListCollectionView verwendet.

    Lädt man sich hier nun das Beispielprojekt herunter, kann man feststellen, dass das Problem mit dem Canceln gelöst wurde.

    Fügt man aber dynamisch über z.B. einen Button der Liste Werte hinzu wird das ChangedEvent nicht ausgeführt.

    Hier habe ich nun 2 Lösungen gefunden. Entweder rufe ich nach dem Einfügen des Wertes in die Liste ListCollectionView.Refresh() auf oder ich füge den Wert nicht der Liste zu sondern gleich der ListCollectionview.

    Nachteile: ListCollectionView bietet nicht so viele Methoden wie List an geschweige den ObservableCollection ( kein Clear, kein ReplaceRange).

    Oder ich muss bei jeder Aktion Refresh() aufrufen.

    Wieso ist das so? Vieleicht kenn ja auch jemand einen Bugfix für das Cancel Problem für ObservableCollection und CollectionViewSource.


  • Administrator

    Parker schrieb:

    Aber in dem Fall liegt es nicht dran. Das das ist gesetzt.

    Konnte ich nicht wissen, du hast so gut wie keine Details gegeben. Nun beschreibst du immerhin dein Vorgehen, wie es zum Problem kommt.

    Parker schrieb:

    Wieso ist das so?

    Steht doch in dem von dir verlinkten Blogeintrag. Es ist ein Bug. Er wurde sogar an Microsoft gemeldet:
    http://connect.microsoft.com/VisualStudio/feedback/details/614500/wpf-datagrid-not-following-underlying-collectionview-selection

    Sie haben aber wohl aktuell wichtigere Bugs zu beheben.

    Parker schrieb:

    Vieleicht kenn ja auch jemand einen Bugfix für das Cancel Problem für ObservableCollection und CollectionViewSource.

    Eine ObservableCollection ist nichts anderes als eine List . Und über die CollectionViewSource kommst du an die ListCollectionView . Es ist mir allerdings überhaupt schon ein Rätsel, wieso man eine Selektion über CurrentChanging verhindern möchte. Wieso macht man etwas überhaupt schon auswählbar, wenn man es nicht auswählen darf? 😕

    Grüssli



  • Konnte ich nicht wissen, du hast so gut wie keine Details gegeben.

    Wenn IsSynchronizedWithCurrentItem dann wäre es ja in beiden Versionen nicht gesetzt gewesen. Und es hätte in beiden Versionen nicht funktioniert.

    Wieso ist das so?

    ??? Ich habe nicht gefragt wieso der Cancel Bug auftritt. Ich habe gefragt wieso sich, wenn ich der Liste einen Wert hinzufüge das Changed Ereignis nicht auftritt.

    Eine ObservableCollection ist nichts anderes als eine List. Und über die CollectionViewSource kommst du an die ListCollectionView.

    Schön. Der Bugfix geht aber nicht mit ObservableCollection und CollectionViewsource.

    Es ist mir allerdings überhaupt schon ein Rätsel, wieso man eine Selektion über CurrentChanging verhindern möchte. Wieso macht man etwas überhaupt schon auswählbar, wenn man es nicht auswählen darf?

    Da ist sogar ein Beispiel drin in dem verlinkten Eintrag von dir!!!!

    Also ich kann mir da viele Beispiele vorstellen.
    Bei mir habe ich das mit einer Abfrage verbunden gleich wie im Link. Wenn ich zu einem bestimmten Eintrag wechsle wird dadurch eine Aktion durchgeführt. Diese Aktion muss vom User aber bestättigt werden. Wenn er auf abbrechen klickt soll der neue Wert dann auch nicht ausgewählt sein.

    Aber das war ja eigentlich gar nicht meine Frage. Darum habe ich auch nicht so weit ausgeholt. Wiel so wie ich dachte die Sache nur unnötig komplizierter wird.


  • Administrator

    Parker schrieb:

    Konnte ich nicht wissen, du hast so gut wie keine Details gegeben.

    Wenn IsSynchronizedWithCurrentItem dann wäre es ja in beiden Versionen nicht gesetzt gewesen. Und es hätte in beiden Versionen nicht funktioniert.

    Nein. Lies dir die Dokumentation zu Selector.IsSynchronizedWithCurrentItem nochmals durch, vor allem was der Standardwert null genau bedeutet. Zudem ist es oft so, dass die ganz einfachen Dinge vergessen werden. Bevor man somit anfängt wild rumzuspekulieren, sollte man die offensichtlichen Fehler beleuchten. Wenn es mal synchroniziert und mal nicht, dann liegt es eben meistens daran, dass die Property nicht auf True gesetzt wurde.

    Parker schrieb:

    ??? Ich habe nicht gefragt wieso der Cancel Bug auftritt. Ich habe gefragt wieso sich, wenn ich der Liste einen Wert hinzufüge das Changed Ereignis nicht auftritt.

    Deine Frage war recht unpräzis. Und ich muss sagen, dass ich mir jetzt nicht sicher bin, ob ich dich richtig verstehe. Was meinst du mit dem Changed Ereignis? CurrentChanged von der CollectionView ? Oder was anderes?

    Parker schrieb:

    Eine ObservableCollection ist nichts anderes als eine List. Und über die CollectionViewSource kommst du an die ListCollectionView.

    Schön. Der Bugfix geht aber nicht mit ObservableCollection und CollectionViewsource.

    Ob du eine ObservableCollection oder eine List nimmst, ist völlig egal.
    Und mir ist nicht klar, was dein Problem mit der CollectionViewSource in dem Szenario ist. Diese wird ja sogar im Bugfix verwendet. Du musst nicht mal eine ListCollectionView zur Verfügung stellen in deinem ViewModel, damit der Bugfix funktioniert. Es dürfte höchstens einen Fehler im präsentierten Code haben:

    EventHandler itemsSourceChangedHandler = null;
    itemsSourceChangedHandler = delegate
    {
        collectionView = selector.ItemsSource as ICollectionView;
        if (collectionView == null)
            collectionView = CollectionViewSource.GetDefaultView(selector.ItemsSource);
            //                                                           ^^^^^^^^^^^^ fehlt
    };
    

    Parker schrieb:

    Es ist mir allerdings überhaupt schon ein Rätsel, wieso man eine Selektion über CurrentChanging verhindern möchte. Wieso macht man etwas überhaupt schon auswählbar, wenn man es nicht auswählen darf?

    Da ist sogar ein Beispiel drin in dem verlinkten Eintrag von dir!!!!

    Das ist nur ein theoretisches Beispiel, welches mir aber nicht erklärt, wieso man dies überhaupt machen würde. Dieses Beispiel sagt nur aus: "Man möchte die Auswahl verhindern". Aber ich frage ja explizit nach dem "Warum man dies machen möchte". Das ist dort nicht erklärt.

    Parker schrieb:

    Also ich kann mir da viele Beispiele vorstellen.
    Bei mir habe ich das mit einer Abfrage verbunden gleich wie im Link. Wenn ich zu einem bestimmten Eintrag wechsle wird dadurch eine Aktion durchgeführt. Diese Aktion muss vom User aber bestättigt werden. Wenn er auf abbrechen klickt soll der neue Wert dann auch nicht ausgewählt sein.

    Das ist erneut eine nichts-sagende Erklärung. Das ist ein Beispiel ohne Bezug zur Realität. Dass eine Aktion bei einer Selektierung ausgeführt wird, kann ich verstehen, dass habe ich auch. Aber dass die Selektion rückgängig gemacht werden muss, wenn der User die Aktion abbricht, empfinde ich als wahnsinnig unintuitiv. Und ich kann mir kein sinnvolles Beispiel vorstellen, wo man dies nicht anders besser lösen könnte.

    Parker schrieb:

    Aber das war ja eigentlich gar nicht meine Frage. Darum habe ich auch nicht so weit ausgeholt. Wiel so wie ich dachte die Sache nur unnötig komplizierter wird.

    Es ist manchmal noch interessant, wenn man die Hintergründe etwas beleuchtet. Vielleicht gibt es andere Lösungsansätze. Wie ich bereit schrieb, scheint mir das wahnsinnig unintuitiv für den User zu sein.

    Übrigens, es gäbe noch eine weitere einfache Lösung:
    Statt in CurrentChanging die Aktion auszulösen und allenfalls Cancel auf True zu setzen, könnte man dies auch in CurrentChanged machen. Irgendwo allerdings immer die letzte Selektion zwischenspeichern. Falls der User die Aktion abbricht, ruft man ein MoveCurrentTo auf der ICollectionView auf, mit dem zuvor selektierten Wert. Ist nicht ganz das Gleiche, dürfte aber in vielen Fällen problemlos funktionieren.

    Grüssli



  • 1. Zu: IsSynchronizedWithCurrentItem:
    Ok in Ordnung. Also ist in beiden gesetzt, Thema erledigt.

    2. Zu: Deine Frage war recht unpräzise.
    Ok entschuldigung dafür. Und ja CurrentChanged von der CollectionView.

    3. Zu: "Ob du eine ObservableCollection oder eine List nimmst, ist völlig egal".
    Wie gesagt es hat halt nicht funktioniert. Hatte das Beispielprojekt von der angegebenen Seite mal von einer List auf eine ObservableCollection umgebaut. Dann hat der Bugfix nicht mehr getan.

    4. Zu den nichts sagenden Beipielen. Mag sein dass es für die ein nichts sagendes Beipiel ist. In meinem Projekt ist es aber sinnvoll. Möchte hier aber nicht das ganze Projekt erklären.

    Vieleicht ein anderes Beipiel. Wenn man Werte hat in einer Liste die je nach Berechtigung ausgewählt werden dürfen oder nicht macht so was für mich auch einen Sinn.

    5. Zu deiner einfacheren Lösung.

    Ist nicht ganz das Gleiche, dürfte aber in vielen Fällen problemlos funktionieren.

    Funktioniert aber in meinem Fall nicht problemlos. Was mehrere Gründe hat.


  • Administrator

    Parker schrieb:

    2. Zu: Deine Frage war recht unpräzise.
    Ok entschuldigung dafür. Und ja CurrentChanged von der CollectionView.

    Wieso erwartest du, dass das CurrentChanged Event aufgerufen wird, wenn du Werte der Liste hinzufügst? Oder meinst du, dass wenn man diese neuen Werte selektiert, dass das Event nicht ausgelöst wird? Kannst du aufzeigen, wass du am Beispielprojekt abgeändert hast?

    Parker schrieb:

    3. Zu: "Ob du eine ObservableCollection oder eine List nimmst, ist völlig egal".
    Wie gesagt es hat halt nicht funktioniert. Hatte das Beispielprojekt von der angegebenen Seite mal von einer List auf eine ObservableCollection umgebaut. Dann hat der Bugfix nicht mehr getan.

    Und gegen was hast du gebunden? Gegen die List oder die CollectionView ? Falls du direkt gegen die List gebunden hast, hast du mal probiert meine Korrektur einzubauen?

    Parker schrieb:

    Vieleicht ein anderes Beipiel. Wenn man Werte hat in einer Liste die je nach Berechtigung ausgewählt werden dürfen oder nicht macht so was für mich auch einen Sinn.

    Das ist der typische Fall von:
    Wieso sind diese Elemente dann überhaupt selektierbar oder wieso werden diese überhaupt angezeigt?

    Grüssli



  • Wir machen es so das die item Container dann inaktiv sind, der Text ist dadurch ausgegraut und das Popup bleibt offen.
    Dadurch ist es deutlich das man es nicht auswählen kann, und Code wird absolut gar nichts ungewollt geändert



  • Wieso erwartest du, dass das CurrentChanged Event aufgerufen wird, wenn du Werte der Liste hinzufügst?

    Jo eigentlich hatte ich das nicht erwartet. Das ist so passiert :-(. Das wollte ich eigentlich gar nicht.
    Ich habe mir nun ein einfaches Beipiel gemacht. Und da wird das Event aber nicht beim Hinzufügen eines Wertes ausgelöst. Irgendwas funkt da in meinem richtigen Projekt noch dazwischen.

    Kannst du aufzeigen, wass du am Beispielprojekt abgeändert hast?

    Statt einer ListCollectionView selbst zu erzeugen, verwende ich die, die automatisch erzeugt wurde.
    CollectionViewSource.GetDefaultView(SomeItems).CurrentChanging += SomeItemsCollectionView_CurrentChanging;
    und die Liste SomeItems ist nun eine ObservableCollection
    public ObservableCollection<string> SomeItems
    und gegen die habe ich nun gebunden.

    Und gegen was hast du gebunden? Gegen die List oder die CollectionView? Falls du direkt gegen die List gebunden hast, hast du mal probiert meine Korrektur einzubauen?

    Wenn ich deine Korrektur einbaue geht es nun auch für die ObservableCollection mit der CollectionViewSource. Ohne deine Korrektur funktioniert es nur für die List mit der ListCollectionView. (Wieso das? Dann gibts ja doch einen Unterschied)

    Das ist der typische Fall von:
    Wieso sind diese Elemente dann überhaupt selektierbar oder wieso werden diese überhaupt angezeigt?

    Ok dummes Beispiel.

    Also noch eines und zwar das letzte 🙂 und eines aus der Realität. Bei WindowsXP kann man bei der Anzeige das Design auswählen Beispielsweise "Windows XP". Verändert man nun dieses Design steht in der Combobox Design: "Windows XP (geändert)". Wechsle ich nun dieses Design auf "Windows - klassisch" wird automatisch "Windows XP (geändert)" aus der Liste gelöscht.

    In meinem Projekt ist das nun so ähnlich. Und zwar möchte ich aber nun wenn ich das Design wechlse und das alte ist nicht gespeichert die Abfrage kommt:

    Designwechsel: Das Aktuelle Design ist nicht gespeichert. Abbrechen / Design verwerfen / Design speichern.

    Und wenn ich nun auf abbrechen klicke ist doch es selbstverständlich dass ich zurückkehre und das alte Design ist immer noch ausgewählt. Oder?



  • Dann selektiere doch einfach zurück - wo ist das Problem



  • Ganz einfach. Weil es nicht geht!

    Füge das mal ein bei CurrentChanged oder auch bei CurrentChanging es ändert sich nichts.

    CollectionViewSource.GetDefaultView(Names).MoveCurrentTo(m_oldName);

    Ich denke dass die anderen auch schon auf die Idee gekommen sind. Sonst hätten sie nicht so aufwendige Bugfixes geschrieben.



  • Ist m_oldName auch die selbe Referenz? Wenn nicht dann such das Element neu aus der Liste.

    Oder du machst es noch einfacher, erstell dir ein normales Property und binde das SelectedItem dagegen.
    - Du brauchst dann kein Event abonnieren sondern einfach in den Setter rein
    - Das setzen funktioniert immer (sofern selbe referenz wie das Item in der Liste), machen wir ständig so
    (PropertyChanged nicht vergessen)



  • SelectedItem habe ich gebunden gegen ein Property. Problem: Wenn das Property geändert werden andere ViewModels benachrichtigt und führen ebenfalls Aktionen aus. Die müsste ich dann auch rückgängig machen.



  • Dann benachrichtige die anderen ViewModels erst wenn alles vorherige erfolgreich war...


Anmelden zum Antworten