none
DataGrid Aktualisierungs-Problem bei veränderter ItemsSource RRS feed

  • Frage

  • Hallo,

    ich verwende in meinem Projekt ein DataGrid, dass Personendaten zu einer in einer ComboBox ausgewählten Person anzeigen soll.

     <DataGrid ItemsSource="{Binding ElementName=SelectedPerson, Path=Activities}">
    
     <ComboBox ItemsSource="{Binding Source={x:Static data:Person.Persons}}" SelectedItem="{Binding SelectedPerson, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

    Das Steuerelement implementiert INotifyPropertyChanged und ruft bei einer Änderung von SelectedPerson das entsprechende Event auf:

    public event PropertyChangedEventHandler PropertyChanged;
    
    public Worker SelectedPerson
           {
                get
                {
                    return _selectedPerson;
                }
                set
                {
                    _selectedPerson = value;
                    OnPropertyChanged("SelectedPerson");
                }
            }
    
    
    private void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
    
    

    Bei Person.Persons und Person.Activities handelt es sich um ObservableCollections

    public static ObservableCollection<Worker> Workers
            {
                get;
                set;
            }
    
    public ObservableCollection<Activity> Activities
            {
                get
                {
                    return _activities;
                }
                private set
                {
                    _activities = value;
                    OnPropertyChanged("Activities");
                }
            }

    Die ComboBox funktioniert nun einwandfrei und aktualisiert auch SelectedPerson, allerdings bleiben die Einträge im DataGrid immer gleich. Wie kann ich dieses Problem beheben und das DataGrid aktualisieren?

    Dienstag, 15. April 2014 12:48

Antworten

  • Hi,
    ich habe Deine Codeschnipsel mal getestet und es funktioniert bei mir problemlos. Da wird bei Dir noch irgendwo ein Fehler im nicht gezeigten Code sein. Hier meine Demo:

    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"
            xmlns:local="clr-namespace:WpfApplication1">
      <Window.Resources>
        <local:ViewModel x:Key="vm"/>
      </Window.Resources>
      <StackPanel DataContext="{Binding Source={StaticResource vm}}">
        <ComboBox Name="cb" ItemsSource="{Binding Persons}"
                  DisplayMemberPath="Name"
                  SelectedItem="{Binding SelectedPerson, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <DataGrid ItemsSource="{Binding Activities}"/>
      </StackPanel>
    </Window>

    Dazu der ViewModel:

    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Windows.Data;
    
    namespace WpfApplication1
    {
      class ViewModel : INotifyPropertyChanged
      {
        Random rnd = new Random();
        public ViewModel()
        {
          for (int i = 1; i < 9; i++)
          {
            Person p = new Person() { Name = "Person " + i.ToString(), 
              Activities = new ObservableCollection<Activity>() };
            for (int k = 1; k < rnd.Next(3, 9); k++)
            {
              p.Activities.Add(new Activity() 
              { Bezeichnung = "Aktivität " + k.ToString() + " der Person " + i.ToString(), 
                Wert = (decimal)rnd.NextDouble() });
            }
            Personenliste.Add(p);
          }
          cvs1.Source = Personenliste;
        }
        ObservableCollection<Person> Personenliste = new ObservableCollection<Person>();
        CollectionViewSource cvs1 = new CollectionViewSource();
        public ICollectionView Persons
        {
          get
          {
            return cvs1.View;
          }
        }
        private Person _selectedPerson = null;
        public Person SelectedPerson
        {
          get
          {
            return this._selectedPerson;
          }
          set
          {
            if (value != this._selectedPerson)
            {
               this._selectedPerson=value;
              OnPropertyChanged("Activities");
            }
          }
        }
            CollectionViewSource cvs2 = new CollectionViewSource();
        public ICollectionView Activities
        {
          get
          {
            if (SelectedPerson != null) cvs2.Source = SelectedPerson.Activities;
            return cvs2.View;
          }
        }
        void OnPropertyChanged(string propname)
        {
          if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propname));
        }
        public event PropertyChangedEventHandler PropertyChanged;
      }
      class Person
      {
        public string Name { get; set; }
        public ObservableCollection<Activity> Activities { get; set; }
      }
      class Activity
      {
        public string Bezeichnung { get; set; }
        public decimal Wert { get; set; }
      }
    }
    

    --
    Peter
    • Als Antwort vorgeschlagen Peter Fleischer Donnerstag, 17. April 2014 07:34
    • Als Antwort markiert Moduel Dienstag, 22. April 2014 15:13
    Donnerstag, 17. April 2014 07:19

Alle Antworten

  • Hi,
    nimm für die Anzeige im Grid eine ICollectionView, die aus einer CollectionViewSource.View genommen wird, deren Source die ObservableCollection ist. Da brauchst Du nur eine ReadOnly-Property ohne NotifyPropertyChanged.

    --
    Peter


    Dienstag, 15. April 2014 12:59
  • Ich habe jetzt eine ICollectionView-Property erstellt, die ich im Konstruktor initialisiere

    public ICollectionView Activities
            {
                get
                {
                    return _activities;
                }
            }
    
    
    CollectionViewSource src = new CollectionViewSource();
                src.Source = SelectedPerson.Activities;
                _activities = src.View;
                InitializeComponent();

    Alles wird richtig im DataGrid angezeigt, allerdings kann wird auch hier bei einer Veränderung der Auswahl in der ComboBox nichts aktualisiert.

    Mittwoch, 16. April 2014 11:00
  • Hi,
    und wie teilst Du der Oberfläche mit, dass sie die Anzeige zu aktualisieren hat?

    --
    Peter

    Mittwoch, 16. April 2014 11:18
  • Hallo,

    das wäre jetzt meine nächste Frage.

    Mittwoch, 16. April 2014 12:35
  • Hi,
    wenn Deine Eigenschaft "Activities" für die Bindung genutzt wird, muss der Verursacher im Programm, der will, dass die an "Activities" gebundenen Steuerelemente ihre Anzeige aktulaisieren, ein NotofyPropertyChanged Ereignis auslösen. Das könnte beispielsweise so aussehen:

    public Worker SelectedPerson
      {
        get
          {
            return _selectedPerson;
          }
        set
          {
            _selectedPerson = value;
            OnPropertyChanged("SelectedPerson");
            OnPropertyChanged("Activities");
          }
        }
    

    --
    Peter
    Mittwoch, 16. April 2014 13:03
  • Hallo,
    Ich habe SelectedPerson jetzt auch ein OnPropertyChanged für die Activities-Property gegeben. Beim Wechsel der Personen durch die ComboBox wird das DataGrid aber immer noch nicht aktualisiert.
    Mittwoch, 16. April 2014 13:29
  • Hi,
    wird denn der Setter der SelectedPerson auch beim Klick in der Liste der Combobox angesprungen? Mit der Auswahl eines Listeneintrages wird nichts in der Sicht verändert, die im Grid angezeigt wird. Ich verstehe nicht, was Du damit erreichen willst? Wenn eine bei der Person eingebettete Datenmenge nach dem Klick anzuzeigen ist, so musst Du auch die Sicht entsprechend aktualisieren. Die Quelle für Deine CollectionViewSource wird aber nicht geändert.

    --
    Peter

     

    Mittwoch, 16. April 2014 14:38
  • Hallo,

    ich habe den Setter jetzt so angepasst, das die Activities immer aktualisiert werden. Dieser wird auch angesprungen, das DataGrid ändert sich aber leider immer noch nicht.

     public Worker SelectedPerson
            {
                get
                {
                    return _selectedPerson;
                }
                set
                {
                    _selectedPerson = value;
    
                    CollectionViewSource src = new CollectionViewSource();
                    src.Source = _selectedPerson.Activities;
                    _activities = src.View;
    
                    OnPropertyChanged("SelectedPerson");
                    OnPropertyChanged("Activities");
                }
            }


    Mittwoch, 16. April 2014 15:01
  • Hi,
    ich habe Deine Codeschnipsel mal getestet und es funktioniert bei mir problemlos. Da wird bei Dir noch irgendwo ein Fehler im nicht gezeigten Code sein. Hier meine Demo:

    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"
            xmlns:local="clr-namespace:WpfApplication1">
      <Window.Resources>
        <local:ViewModel x:Key="vm"/>
      </Window.Resources>
      <StackPanel DataContext="{Binding Source={StaticResource vm}}">
        <ComboBox Name="cb" ItemsSource="{Binding Persons}"
                  DisplayMemberPath="Name"
                  SelectedItem="{Binding SelectedPerson, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <DataGrid ItemsSource="{Binding Activities}"/>
      </StackPanel>
    </Window>

    Dazu der ViewModel:

    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Windows.Data;
    
    namespace WpfApplication1
    {
      class ViewModel : INotifyPropertyChanged
      {
        Random rnd = new Random();
        public ViewModel()
        {
          for (int i = 1; i < 9; i++)
          {
            Person p = new Person() { Name = "Person " + i.ToString(), 
              Activities = new ObservableCollection<Activity>() };
            for (int k = 1; k < rnd.Next(3, 9); k++)
            {
              p.Activities.Add(new Activity() 
              { Bezeichnung = "Aktivität " + k.ToString() + " der Person " + i.ToString(), 
                Wert = (decimal)rnd.NextDouble() });
            }
            Personenliste.Add(p);
          }
          cvs1.Source = Personenliste;
        }
        ObservableCollection<Person> Personenliste = new ObservableCollection<Person>();
        CollectionViewSource cvs1 = new CollectionViewSource();
        public ICollectionView Persons
        {
          get
          {
            return cvs1.View;
          }
        }
        private Person _selectedPerson = null;
        public Person SelectedPerson
        {
          get
          {
            return this._selectedPerson;
          }
          set
          {
            if (value != this._selectedPerson)
            {
               this._selectedPerson=value;
              OnPropertyChanged("Activities");
            }
          }
        }
            CollectionViewSource cvs2 = new CollectionViewSource();
        public ICollectionView Activities
        {
          get
          {
            if (SelectedPerson != null) cvs2.Source = SelectedPerson.Activities;
            return cvs2.View;
          }
        }
        void OnPropertyChanged(string propname)
        {
          if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propname));
        }
        public event PropertyChangedEventHandler PropertyChanged;
      }
      class Person
      {
        public string Name { get; set; }
        public ObservableCollection<Activity> Activities { get; set; }
      }
      class Activity
      {
        public string Bezeichnung { get; set; }
        public decimal Wert { get; set; }
      }
    }
    

    --
    Peter
    • Als Antwort vorgeschlagen Peter Fleischer Donnerstag, 17. April 2014 07:34
    • Als Antwort markiert Moduel Dienstag, 22. April 2014 15:13
    Donnerstag, 17. April 2014 07:19
  • Hallo,

    Danke, ich habe jetzt mein Control nochmal auf deinem Codebeispiel neu aufgebaut und es funktioniert. Hatte wohl einen anderen Fehler.


    • Bearbeitet Moduel Dienstag, 22. April 2014 16:41
    Dienstag, 22. April 2014 15:15