none
ListBox-Items aktualisieren nicht beim löschen eines Items aus der mit DataBinding angebundenen Liste RRS feed

  • Frage

  • Hallo,

    Ich habe eine ListBox der ich per DataBinding eine List zugeordnet habe. Wenn ich items hinzufüge funktioniert das einwandfrei. Nur sobald ich aus der List etwas lösche, aktualisiert er das in der ListBox nicht. Ich habe auch nach dem Entfernen ein

    ListBox.Items.Refresh()

    eingefügt. Das funktioniert normalerweise immer (verwende das DataBinding nicht zum ersten Mal). Aber leider hat es diesmal keinen Erfolg. Habe auch das Databinding schon mal gelöscht und neu zugewiesen. Funktioniert aber leider auch nicht. Wenn ich beim Debuggen in die Eigenschaften der ListBox.Items schaue, dann sehe ich bei Count, dass ich 9 (= das gelöscht ist noch dabei) Elemente habe. Ich sehe aber bei ListBox.Items auch die Eigenschaft SourceCollection. Da steht 8 Elemente, also die richtige Anzahl. Ich habe auch schon versucht die SourceCollection auf die ListBox zu DataBinden. Aber irgenwie will nichts funktionieren.

    Nur wenn ich zu der Liste ein neues Element hinzufüge aktualisiert er das DataBinding und das vorm Hinzufügen gelöschte Element ist dann auch weg...

    Hat jemand eine Idee für eine Lösung oder einen Workaround?

    Gruß

    Matthias

    Mittwoch, 11. September 2013 14:31

Antworten

  • Hallo Matthias,

    das hört sich nach einem Problem mit der Updatefunktionalität an.

    Versuch doch mal die Liste als ObservableCollection oder

    System.ComponentModel.ICollectionView icv = System.Windows.Data.CollectionViewSource.GetDefaultView(object);

    eventuell hilft auch noch das Binding mit UpdateSourceTrigger="PropertyChanged" zu versehen.

    Weiterhin solltest Du versuchen bei der ListBox 

    <ListBox IsSynchronizedWithCurrentItem="True"/>

    zu setzen.

    Vielleicht hilft das ja schon ohne großen Programmieraufwand.

    Ansonsten kannst Du noch versuchen an deine Liste das INotifyPropertyChanged anzuhängen und auszuwerten. Dies sollte dann spätestens zum Erfolg führen.

    Grüße

    JP One

    • Als Antwort markiert hias116 Donnerstag, 12. September 2013 09:39
    Donnerstag, 12. September 2013 08:44

Alle Antworten

  • Hallo Matthias,

    das hört sich nach einem Problem mit der Updatefunktionalität an.

    Versuch doch mal die Liste als ObservableCollection oder

    System.ComponentModel.ICollectionView icv = System.Windows.Data.CollectionViewSource.GetDefaultView(object);

    eventuell hilft auch noch das Binding mit UpdateSourceTrigger="PropertyChanged" zu versehen.

    Weiterhin solltest Du versuchen bei der ListBox 

    <ListBox IsSynchronizedWithCurrentItem="True"/>

    zu setzen.

    Vielleicht hilft das ja schon ohne großen Programmieraufwand.

    Ansonsten kannst Du noch versuchen an deine Liste das INotifyPropertyChanged anzuhängen und auszuwerten. Dies sollte dann spätestens zum Erfolg führen.

    Grüße

    JP One

    • Als Antwort markiert hias116 Donnerstag, 12. September 2013 09:39
    Donnerstag, 12. September 2013 08:44
  • Hi,
    unklar ist, was Du wirklich machst. Nachfolgend eine kleine Demo, wie es funktioniert:

    XAML:

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
      <StackPanel>
        <Button Name="btnAdd" Content="Hinzufügen" Click="btnAdd_Click"/>
        <Button Name="btnRemove" Content="Entfernen" Click="btnRemove_Click"/>
        <ListBox ItemsSource="{Binding View}"/>
      </StackPanel>
    </Window>
    

    Und der CodeBehind dazu:

    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WpfApplication1
    {
      /// <summary>
      /// Interaction logic for MainWindow.xaml
      /// </summary>
      public partial class MainWindow : Window
      {
        public MainWindow()
        {
          InitializeComponent();
          this.DataContext = this;
        }
    
        CollectionViewSource cvs = new CollectionViewSource();
        ObservableCollection<string> liste = new ObservableCollection<string>();
        int nummer = 0;
        Random rnd = new Random();
    
        public ICollectionView View
        {
          get
          {
            if (cvs.View == null) cvs.Source = liste;
            return cvs.View;
          }
        }
    
        private void btnAdd_Click(object sender, RoutedEventArgs e)
        {
          liste.Add("Zeile " + nummer.ToString());
          nummer++;
        }
    
        private void btnRemove_Click(object sender, RoutedEventArgs e)
        {
          if (liste.Count() < 1) return;
          liste.RemoveAt(rnd.Next(0,liste.Count()));
        }
      }
    }
    

    --
    Peter
    Donnerstag, 12. September 2013 09:25
  • In Ergänzung von JP One und Peter

    Hallo Mathias,

    da du keinen Code gepostet hast, muss man davon ausgehen dass du an eine Liste gebunden hast, die nicht das INotifyCollectionChanged Interface implementiert. Dadurch nimmt dein Binding nicht an dem automatischen Benachrichtigungssystem der WPF-Engine teil. Die WPF-Engine erstellt zwar um jede Auflistung/Collection die du an eine Items-Auflistung (wie in deiner ListBox) bindest/übergibst, automatisch einen Wrapper(View) implementiert aber nicht automatisch das INotifyCollectionChanged Interface, was auch einen guten Grund hat.

     WPF benutzt also grundsätzlich "nie" direkt die zugrunde liegende Auflistung die du übergeben hast, zumindest nicht solange du keine Auflistungen übergibst die das ICollectionView Interface implementieren. Intern benutzt die WPF-Engine derzeit vier Typen von Wrappern/Views: ListCollectionView, BindingListCollectionView, EnumerableCollectionView und CompositeCollectionView. Allen gemeinsam ist, dass alle das ICollectionView Interface implementieren aber keine dieser Views das INotifyCollectionChanged Interface implementiert.

    Die WPF-Engine entscheidet selbstständig, auf der Basis der Interfaces die in deiner übergebenen Auflistung implementiert werden, welche View die richtige ist. In anderen Worten: Wenn deine übergebene Auflistung das IList-Interface implementiert, dann wird eine ListCollectionView als Wrapper benutzt, wenn deine Auflistung das IBindingList-Interface implementiert, dann wird eine BindingListCollectionView als Wrapper benutzt und wenn deine Auflistung nichts anderes als IEnumerable implementiert dann wird die EnumerableCollectionView als Wrapper benutzt. Zur CompositeCollectionView schreibe ich mal nichts, denn das würde den Rahmen hier bei weitem sprengen. Allen Views ist jedoch gemeinsam, dass sie Filtering, Sorting, Grouping und das Tracking unterstützen. Auf das Tracking komme ich gleich, denn das wird wichtig um deine zugrunde liegende Auflistung auf Stand zu halten.

    Deine Items-Auflistung an die du in deiner ListBox gebunden hast ist eine ItemCollection. ItemCollection ist eine hybrid Auflistung, hybrid deswegen weil sie intern zwei Collection's verwaltet, zum einen die Items in deiner ListBox und zum anderen die von der WPF-Engine generierte CollectionView/Wrapper (die deine Daten/Auflistung repräsentiert). Zur ItemCollection gäbe es noch mehr zu sagen, aber das würde auch den Rahmen hier sprengen... alleine schon weil man an eine ItemCollection ein Binding machen kann, aber man kann auch direkt im XAML Items hinzufügen, das eine schließt das andere jedoch aus ;-). Allerdings ist diese Hybrid-Auflistung meiner Meinung nach ein wirklich gute Idee vom WPF-Team gewesen, denn diese Art der Implementierung ermöglicht es, dass Dinge wie "IsSynchronizedWithCurrentItem" funktionieren oder man denke dabei nur mal an Multi-Threading.

    Nun zum Tracking, da ja deine zugrunde liegende Auflistung auf Stand gehalten werden muss (Add, Remove) liegt der Gedanke nahe, dass der Benachrichtigungsmechanismus ob Einträge hinzugefügt oder entfernt wurden direkt in der zugrunde liegenden Auflistung implementiert sein sollte. Es wäre Unsinn gewesen, wenn das WPF-Team diesen Mechanismus in den CollectionViews implementiert hätte, schon alleine deswegen weil man den dort nicht immer benötigt. Daher muss deine übergebene Auflistung diesen Benachrichtigungsmechanismus implementieren und das tut sie indem du das Interface INotifyCollectionChanged implementierst oder etwas fertiges wie ObservableCollection benutzt. Letzteres ist natürlich zu bevorzugen ;-). Etwas vereinfacht ausgedrückt... schaut nun die WPF-Engine ob diese Interface implementiert wurde und wenn ja, dann werden die CollectionViews über Delegates damit verknüpft. Ergo... ohne INotifyCollectionChanged geht gar nichts ;-) zumindest nicht ohne viel viel Code zu implementieren der das dann übernehmen würde. Übrigens hat "INotifyCollectionChanged" nichts mit "INotifyPropertyChanged" zu tun, letzteres sollte, sofern du Änderungen an deinen Daten vornehmen willst und an dem eleganten Benachrichtigungsmechanismus der WPF-Engine teilnehmen willst. direkt auf Daten/Model-Ebene implementiert sein.

    Zusammenfassend... du kannst also gleich an eine CollectionView binden, die du als Property deiner Items Auflistung (in der ListBox) übergibst, da die WPF-Engine dies so oder so machen würde. Dazu benutzt du am besten ICollectionView als Typ an deiner Property, denn dadurch erhältst du etwas mehr Flexibilität, kannst aber auch direkt eine passende CollectionView übergeben. Am einfachsten kannst du das, wie von JP One und Peter schon vorgeschlagen über CollectionViewSource.GetDefaultView... der du eine ObservableCollection übergibst, implementieren.

    Grundsätzlich kann man sagen, dass CollectionView's unglaublich Leistungsfähig sind und man sehr viel damit machen kann, durch die Möglichkeiten die durch SynchronizedWithCurrentItem geboten werden sind diese Auflistungen auch sehr einfach in Anwendungen zu benutzen die nach dem MVVM-Pattern implementiert werden.

    So, genug getextet, ich hoffe ich konnte etwas helfen...

    Viele Grüße

    Carl


    Carl-Christian Schaffert Bahnhofsweg 2 82008 Unterhaching mobile: 0152-33 72 79 49 email: carl-christian.schaffert@dotnet-dev-expert.de skype: carl.christian.schaffert



    Sonntag, 15. September 2013 09:29