WPF ObservableCollection<T> Event Problem?



  • Morgen,

    folgendes Problem liegt vor.
    Ich hab eine Klasse:

    [Serializable()]
        sealed public class Transaktion
        {
            public enum Art
            {
                Sonstige,
                Einnahme,
                Ausgabe
            }
            public string Beschreibung { get; set; }
            public string Datum { get; set; }
            public double Betrag { get; set; }
            public Art TransaktionsArt { get; set; }
    
        }
    

    Dann eine Form, in welcher eine ListView mit Databindings existiert, ungefähr so:

    <ListView ItemsSource="{Binding Transaktionen}">
      <ListView.View>
                        <GridView>
                            <GridViewColumn Header="Datum" DisplayMemberBinding="{Binding Datum}" />
                            <GridViewColumn Header="Beschreibung" DisplayMemberBinding="{Binding Beschreibung}"/>
                            <GridViewColumn Header="Preis" DisplayMemberBinding="{Binding Betrag}"/>
                            <GridViewColumn Header="Art" DisplayMemberBinding="{Binding TransaktionsArt}"/>
                        </GridView>
                    </ListView.View>
                </ListView>
    

    Dann habe ich folgende Konstellation in der Form um die Daten immer aktuell im View zu halten:

    public partial class MainWindow : Window
        {
    
            static ObservableCollection<Transaktion> transaktionen = new ObservableCollection<Transaktion>();
            Serializer<ObservableCollection<Transaktion>> ser = new Serializer<ObservableCollection<Transaktion>>("transaktionen.dat");
    
            public MainWindow()
            {
    
                InitializeComponent();
                transaktionen = ser.Load();
    
            }
            public static ObservableCollection<Transaktion> Transaktionen
            {
                get { return transaktionen; }
            }
    }
    

    So wenn ich nun per Button neue Items in die Collection hinzufüge klappt es auch einwandfrei das die Items sofort im ListView angezeigt werden, wenn ich nun aber die Items über meine Serialisierungsmethode reinlade, werden diese nicht im ListView angezeigt. Ehe diese Frage aufkommt, ja ich bin schon mit dem Debugger durchgegangen und hab geguckt ob überhaupt was in der List drinne ist.Sie wird definitiv korrekt gefüllt.
    Muss ich das Binding nach dem laden nochmal aktuallisieren oder wo ist das Problem?



  • Offensichtlich ist mir jetzt kein Fehler aufgefallen. Was aber aus deinem geposteten Code nicht genau hervor geht:

    Fügst du die deserialisierten Objekte der bestehenden Collection hinzu oder erstellst du eine neue Collection entweder direkt oder der Serializer intern?
    Für mich sieht es so aus, als würde jedesmal eine neue Collection erstellet. Logischerweise hat die BindingEngine in der neuen Collection dann keine Eventhandler mehr drin.



  • O.o schrieb:

    Offensichtlich ist mir jetzt kein Fehler aufgefallen. Was aber aus deinem geposteten Code nicht genau hervor geht:

    Fügst du die deserialisierten Objekte der bestehenden Collection hinzu oder erstellst du eine neue Collection entweder direkt oder der Serializer intern?
    Für mich sieht es so aus, als würde jedesmal eine neue Collection erstellet. Logischerweise hat die BindingEngine in der neuen Collection dann keine Eventhandler mehr drin.

    Hmm ok das kann natürlich eine Idee sein. Der Serializer sieht so aus:

    sealed class Serializer<T>
        {
    //aufgrund der übersicht gekürzt.
    
            public T Load()
            {
                using(this.serializeStream = new FileStream(this.FileName,FileMode.Open))
                {
                    IFormatter f = new BinaryFormatter();
                    T item = (T)f.Deserialize(this.serializeStream);
                    return item;
    
                }
            }
    


  • ich vermute das tatsaechlich beim (T)f.Deserialize ein neues objekt erstellt wird

    public MainWindow()
    {
        InitializeComponent();
    
        var tmpItem = ser.Load();
        foreach (var tmpTtem in tmpItems)
            transaktionen.Add(tmpItem);
    }
    


  • Mr Evil schrieb:

    ich vermute das tatsaechlich beim (T)f.Deserialize ein neues objekt erstellt wird

    public MainWindow()
    {
        InitializeComponent();
    
        var tmpItem = ser.Load();
        foreach (var tmpTtem in tmpItems)
            transaktionen.Add(tmpItem);
    }
    

    Haste Recht, das war der Fehler. Aber schon ein eigenartiges Verhalten.



  • Eigentlich überhaupt nicht eigenartig? Woher sollte die BindingEngine denn wissen, dass du ein neues Objekt erstellt hast? Ich wette wenn du INotifyPropertyChanged für "MainWindow.Transaktionen" implementierst funktioniert auch deine erste Variante. Eleganter wäre es natürlich gleich DependencyProperties zu verwenden.



  • Klingt vielleicht doof, aber wie müsste ich denn das Interface für meine Klasse implementieren?Und was meinst du mit den DependencyProperties?



  • Mit INotifyPropertyChanged:

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
      public MainWindow()
      {
        InitializeComponent();
        Transaktionen = ser.Load();
      }
    
      private static ObservableCollecion<Transaktion> transaktionen;
      public static ObservableCollection<Transaktion> Transaktionen
      {
        get { return transaktionen; }
        private set
        {
          transaktionen = value;
          RaiseProperyChanged("Transaktionen");
        }
      }
    
      private void RaisePropertyChanged(string propertyName)
      {
        if(PropertyChanged != null)
          PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
    }
    

    Da es sich bei Window allerdings sowieso schon um ein DependencyObject handelt kannst du das gleich benutzen:

    public partial class MainWindow : Window
    {
      public MainWindow()
      {
        InitializeComponent();
        Transaktionen = ser.Load();
      }
    
      public static ObservableCollection<Transaktion> Transaktionen
      {
        get { return (ObservableCollection<Transaktion>)GetValue(TransaktionenProperty); }
        private set { SetValue(TransaktionenProperty, value); }
      }
    
      private static readonly DependencyPropertyKey TransaktionenPropertyKey =
            DependencyProperty.RegisterReadOnly("Transaktionen", typeof(ObservableCollection<Transaktion>), typeof(MainWindow), new UIPropertyMetadata(null));
      public static readonly DependencyProperty TransaktionenProperty = TransaktionenPropertyKey.DependencyProperty;
    }
    

    Für das DependencyProperty gibts übrigens im Studio (2008) auch einen schönen vorgefertigten Codeschnipsel. 😉

    Eine dritte Variante, die zu dem auch deutlich weniger Code erfordert wäre es Window.DataContext zu benutzen und das Binding der ItemsSource entsprechend umzubiegen.


Anmelden zum Antworten