WPF, nie dziala bindowanie danych przy zmianie elementow konteneru

0

Person.cs

 namespace MyWPFProject
{
    public class Person
    {
        public string Name { get; set; }
        public string Surname { get; set; }
        public int Age { get; set; }
    }
}

RelayCommand.cs

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace MyWPFProject
{
    public class RelayCommand : ICommand
    {
        private readonly Func<Boolean> _canExecute;
        private readonly Action _execute;

        public RelayCommand(Action execute)
            : this(execute, null)
        {
        }

        public RelayCommand(Action execute, Func<Boolean> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            _execute = execute;
            _canExecute = canExecute;
        }

        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (_canExecute != null)
                    CommandManager.RequerySuggested += value;
            }
            remove
            {
                if (_canExecute != null)
                    CommandManager.RequerySuggested -= value;
            }
        }

        public Boolean CanExecute(Object parameter)
        {
            return _canExecute == null ? true : _canExecute();
        }

        public void Execute(Object parameter)
        {
            _execute();
        }
    }
}

ViewModel.cs

 using System.ComponentModel;
using System.Windows.Input;

namespace MyWPFProject
{
    public class ViewModel : INotifyPropertyChanged
    {
        public ViewModel()
        {
            ListOfPeople = new System.Collections.ObjectModel.ObservableCollection<Person>();
            ListOfPeople.Add(new Person() { Name = "Michael", Surname = "Jordan", Age = 22 });
            ListOfPeople.Add(new Person() { Name = "Jan", Surname = "Kowalski", Age = 21 });
            ListOfPeople.Add(new Person() { Name = "Anna", Surname = "Nowak", Age = 20 });
        }

        private void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if(handler!=null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public string Name
        {
            get
            {
                return person.Name;
            }
            set
            {
                person.Name = value;
                OnPropertyChanged("Name");
            }
        }

        public string Surname
        {
            get
            {
                return person.Surname;
            }
            set
            {
                person.Surname = value;
                OnPropertyChanged("Surname");
            }
        }
        public int Age
        {
            get
            {
                return person.Age;
            }
            set
            {
                person.Age = value;
                OnPropertyChanged("Age");
            }
        }

        private void ChangeDataExecute()
        {
            //Ponizsze linijki nie dzialaja a debuger przez nie przechodzi jak wcisniemy buttona
            ListOfPeople[1].Name = "Monika";
            ListOfPeople[1].Surname = "Strucka";
            ListOfPeople[1].Age = 24;

            //Ponisza linijka jak sie odkomentuje to dziala
            //ListOfPeople.Add(new Person() { Name = "Krzysztof", Surname = "Wozniak", Age = 20 });
        }

        public ICommand ChangeData { get { return new RelayCommand(ChangeDataExecute); } }

        public System.Collections.ObjectModel.ObservableCollection<Person> ListOfPeople
        {
            get
            {
                return listOfPeople;
            }
            set
            {
                listOfPeople = value;
                OnPropertyChanged("ListOfPeople");
            }
        }

        private System.Collections.ObjectModel.ObservableCollection<Person> listOfPeople;

        private readonly Person person;

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

MainWindow.xaml

<Window x:Class="MyWPFProject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MyWPFProject"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <Grid>
        <StackPanel>
            <ListView Name="listView" ItemsSource="{Binding Path=ListOfPeople, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}"/>
                        <GridViewColumn Header="Surname" DisplayMemberBinding="{Binding Path=Surname}"/>
                        <GridViewColumn Header="Age" DisplayMemberBinding="{Binding Path=Age}"/>
                    </GridView>
                </ListView.View>
            </ListView>
            <Button FontSize="20" MinHeight="30" Margin="180,20,180,0" Content="Zmień" Command="{Binding ChangeData}"/>
        </StackPanel>
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Windows;

namespace MyWPFProject
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}
 

ed175e7dc1.png

No więc mam problem z bindowaniem danych. Otoż kiedy naciskam buttona to program mi uruchamia funkcję ChangeData() w ViewModelu (sprawdzalem debuggerem) i zmienia element nr 1 obiektu listOfPeople

System.Collections.ObjectModel.ObservableCollection<Person> listOfPeople; 

.
Wcześniej tu miałem

List<Person>

, ale wyczytalem w tutorialach na necie, ze ten długaśny typ ma zaimplementowane bindowanie elementów tego kontenera. Bo właśnie w tym tkwi problem. Program wyświetla ładną listę osób. Jak nacisnę buttona to zmieniam element nr 1 tej listy. I to powinno się w View zaktualizować, ale tak nie się nie dzieje. Co innego jak np. w funkcji ChangeData wykonam jakieś operacje na liście (a nie na jej elementach). Wtedy ładnie aktualizuje listę. Ale jak zmienię pojedynczy element to nic się nie aktualizuje. W czym może być problem, bo już dużo kombinowałem?

2

Ponizsze linijki nie dzialaja a debuger przez nie przechodzi jak wcisniemy buttona.

Nieprawda, przechodzi.

  • Użyj System.Collections.ObjectModel;, a nie będziesz musiał pisać łańcuszka przed ObservableCollection.
  • INotifyPropertyChanged powinien w Twoim przypadku być implementowany przez model, gdyż zmienne modelu podlegają zmianie.
  • private Person person; jest bez sensu. Nawet nie inicjalizujesz tego obiektu w konstruktorze. To jest zawsze null.
  • Utwórz klasę, która dziedziczy po INotifyPropertyChanged i implementuje OnPropertyChanged. Niech po tej klasie dziedziczą inne klasy, które zmieniają własności. Nie będziesz musiał za każdym razem definiować metody implementując interfejs, bo załatwi to za Ciebie ta właśnie klasa.
  • Używaj nameof(object) zamiast magic stringów.

Reasumując:

  • Viewmodel:
using System.ComponentModel;
using System.Windows.Input;
using System.Collections.ObjectModel;

namespace _4programmersWPF {
    class Viewmodel : INotifyPropertyChanged {

        public Viewmodel() {
            ListOfPeople = new ObservableCollection<Person>();
            ListOfPeople.Add(new Person() { Name = "Michael", Surname = "Jordan", Age = 22 });
            ListOfPeople.Add(new Person() { Name = "Jan", Surname = "Kowalski", Age = 21 });
            ListOfPeople.Add(new Person() { Name = "Anna", Surname = "Nowak", Age = 20 });
        }

        private ObservableCollection<Person> listOfPeople;
        public ObservableCollection<Person> ListOfPeople {
            get {
                return listOfPeople;
            }
            set {
                listOfPeople = value;
                OnPropertyChanged(nameof(ListOfPeople));
            }
        }

        public ICommand ChangeData {
            get {
                return new RelayCommand(() => {
                    ListOfPeople[1].Name = "Monika";
                    ListOfPeople[1].Surname = "Strucka";
                    ListOfPeople[1].Age = 24;
                });
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName) {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        } 
    }
}
  • Model:
using System.ComponentModel;

namespace _4programmersWPF {
    public class Person : INotifyPropertyChanged {

        private string name;
        public string Name {
            get { return this.name; }
            set {
                this.name = value;
                this.OnPropertyChanged(nameof(Name));
            }
        }

        private string surname;
        public string Surname {
            get { return this.surname; }
            set {
                this.surname = value;
                this.OnPropertyChanged(nameof(Surname));
            }
        }

        private int age;
        public int Age {
            get { return this.age; }
            set {
                this.age = value;
                this.OnPropertyChanged(nameof(Age));
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName) {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

I będzie gites. :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1