简体   繁体   中英

WPF Binding to property list not updating

Help. I've been searching with google and stackoverflow searchbar for a week and didn't found my answers.

So I have a class named Student and the properties is string Name, string Address, DoB DateofBirth, and List Score. And a ViewModel class for the Student class.

public partial class StudentWindow : Window
{
    public class DoB
    {
        public int Day { get; set; }
        public int Month { get; set; }
        public int Year { get; set; }
        public DoB(int day, int month, int year)
        {
            Day = day;
            Month = month;
            Year = year;
        }
        public override string ToString()
        {
            return Day + "/" + Month + "/" + Year;
        }
    }
    public class Student
    {
        public string Name { get; set; }
        public string Address { get; set; }
        public DoB DateOfBirth { get; set; }
        public List<int> Score { get; set; }
    }
    public class ViewModel : INotifyPropertyChanged
    {
        private Student entry;
        public event PropertyChangedEventHandler PropertyChanged;
        public void Notify(string Property)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(Property));
        }
        public string Name
        {
            get { return entry.Name; }
            set { entry.Name = value; Notify("Name"); }
        }
        public string Address
        {
            get { return entry.Address; }
            set { entry.Address = value; Notify("Address"); }
        }
        public string DateOfBirth
        {
            get { return entry.DateOfBirth.ToString(); }
            set
            {
                var p = value.Split('/');
                int d = 1, m = 1, y = 1;
                bool pass = int.TryParse(p[0], out d) && int.TryParse(p[1], out m) && int.TryParse(p[2], out y);
                if (pass)
                {
                    entry.DateOfBirth = new DoB(d, m, y);
                    Notify("DateOfBirth");
                }
                else
                    throw new InvalidCastException();
            }
        }
        public List<string> Score
        {
            get { return entry.Score.Select(sc => "" + sc).ToList(); }
            set { entry.Score = value.Select(va => int.Parse(va)).ToList(); Notify("Score"); }
        }
        public ViewModel(Student entry)
        {
            this.entry = entry;
        }
    }
    public ObservableCollection<ViewModel> entry { get; set; }
    public StudentWindow()
    {
        InitializeComponent();

        entry = new ObservableCollection<ViewModel>();
        entry.Add(new ViewModel(new Student() { Name = "First People", Address = "Earth", DateOfBirth = new DoB(13, 11, 1996), Score = new List<int>(new int[] { 100, 90, 100, 90 }) }));
        entry.Add(new ViewModel(new Student() { Name = "Second People", Address = "Moon", DateOfBirth = new DoB(13, 11, 1995), Score = new List<int>(new int[] { 90, 80, 100, 100 }) }));

        DataContext = this;
    }
}

And the XAML is

<Grid>
    <DataGrid x:Name="dataGridStudent" ItemsSource="{Binding entry}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="210" Width="572"/>
    <DataGrid x:Name="dataGridStudentScore" ItemsSource="{Binding ElementName=dataGrid, Path=SelectedItem.Score}" HorizontalAlignment="Left" Margin="10,242,0,0" VerticalAlignment="Top" Height="218" Width="572" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        </DataGrid.Columns>
    </DataGrid>
</Grid>

Everything works fine for dataGridStudent.

But dataGridStudentScore is only displaying the values and unable to edit the value. I have to set the Binding Path=. , otherwise the Path or Xpath exception will thrown.

Please help with any solutions you have. I'm newbie here, please do tell if something is wrong with my approach. Thanks in advance.

Ignoring the confusing understanding on the MVVM architecture. The reason student score not editable is that you try to bind a value type variable to DataGridTextColumn. I'm not sure about the background implementation of DependencyProperty binding but I would imagine there is no reference to bind to in order to perform the update as it's just a value type variable.

I'm not an expect in wpf too but I would implement that same thing in the following.

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class Score : ViewModelBase
{
    public int Value { get; set; }
}

public class Student : ViewModelBase
{
    public string Name { get; private set; }
    public string Address { get; private set; }
    public DateTime Dob { get; private set; }

    public ObservableCollection<Score> Scores { get; set; }

    public Student(string name, string address, DateTime dob)
    {
        Name = name;
        Address = address;
        Dob = dob;

        Scores = new ObservableCollection<Score>();
    }
} 

public class StudentViewModel : ViewModelBase
{
    public ObservableCollection<Student> Students { get; private set; }

    public StudentViewModel()
    {
        Students = new ObservableCollection<Student>
        {
            new Student("Student A", "A Address", DateTime.Now)
            {
                Scores = new ObservableCollection<Score>
                {
                    new Score { Value = 80 }, 
                    new Score { Value = 85 }, 
                    new Score { Value = 90 }, 
                }
            },
            new Student("Student B", "B Address", DateTime.Now)
            {
                Scores = new ObservableCollection<Score>
                {
                    new Score { Value = 70 }, 
                    new Score { Value = 75 }, 
                    new Score { Value = 60 }, 
                }
            }
        };
    }

    private Student _selectedStudent;        
    public Student SelectedStudent
    {
        get { return _selectedStudent; }
        set 
        {
            _selectedStudent = value;
            OnPropertyChanged("SelectedStudentScores");
        }
    }

    public ObservableCollection<Score> SelectedStudentScores
    {
        get
        {
            if (_selectedStudent == null) return null;
            return _selectedStudent.Scores;
        }
    }

    public Score SelectedScore { get; set; }
}

<Window x:Class="StudentScoreWpfProj.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:studentScoreWpfProj="clr-namespace:StudentScoreWpfProj"
    mc:Ignorable="d"
    d:DataContext="{d:DesignInstance Type=studentScoreWpfProj:StudentViewModel,IsDesignTimeCreatable=True}"        
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <DataGrid ItemsSource="{Binding Students}" AutoGenerateColumns="False"
              SelectedItem="{Binding SelectedStudent}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Name, Mode=OneWay}" />
            <DataGridTextColumn Header="Address" Binding="{Binding Address, Mode=OneWay}" />
            <DataGridTextColumn Header="Birth" Binding="{Binding Dob, Mode=OneWay, StringFormat={}{0:MM/dd/yyyy}}" />
        </DataGrid.Columns>
    </DataGrid>

    <DataGrid Grid.Row="1" ItemsSource="{Binding SelectedStudentScores}"
              SelectedItem="{Binding SelectedScore}">
    </DataGrid>
</Grid>

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new StudentViewModel();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM