WPF绑定不更新视图(2020)

时间:2020-06-29 18:13:42

标签: c# wpf xaml binding datacontext

我是C#和XAML的新手,我正在尝试学习如何在MVVM设计模式下使用数据上下文和绑定。我写了一个非常简单的程序。该程序允许用户创建一个新人员,并在MainWindow中显示该人员的详细信息。要创建一个新的人,用户从MainWindow的菜单中选择“添加新人”。会弹出一个窗口(PersonView),用户可以在其中输入此人的姓名和年龄。当用户在PersonView窗口上单击OK时,将触发一个事件,该事件将Person的数据复制到MainWindow中的属性中。一旦通过PersonView窗口复制了数据,就会关闭。

问题在于此人的详细信息未在主窗口中更新。使用调试器,我可以看到我的PropertyChanged事件触发了,并且MainWindow中的事件处理程序正在运行。我已经将MainWindow的DataContext(在c#中)设置为等于MainWindow中包含要显示的数据的属性。我已经在MainWindow中为XAML中的Name和Age TextBlocks设置了绑定。

我已经读过thisthis,但仍然无法正常工作。我想念什么?

代码:

Person.cs:

{
    public class Person
    {
        #region Constructors
        public Person()
        {
            Name = "";
            Age = 0;
        }
        public Person(string name, int age)
        {
            Name = name;
            Age = age;
        }

        #endregion

        #region Properties
        public string Name { get; set; }
        public int Age { get; set; }

        #endregion

        #region Methods
        #endregion
    }
}

PersonViewModel.cs

using System.ComponentModel;

namespace MVVM_Basics.ViewModel
{
    public class PersonViewModel : INotifyPropertyChanged
    {
        #region Constructors
        public PersonViewModel()
        {
            _person.Name = "";
            _person.Age = 0;
        }
        public PersonViewModel(string name, int age)
        {
            _person.Name = name;
            _person.Age = age;
        }

        #endregion

        #region Properties

        Person _person = new Person();
        public Person  Person 
        { get { return _person; }
            set
            {
                _person = value;
                OnPropertyChanged(new PropertyChangedEventArgs("Person"));
            }
        }

        #region Events and EventHandlers
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            PropertyChangedEventHandler handler = PropertyChanged; //Associate event handler with event
            handler?.Invoke(this, e); //invoke the delegate
        }

        #endregion


        #endregion

        #region Methods


        #endregion
    }
}

PersonView.xaml.cs

using System;
using System.Windows;

namespace MVVM_Basics.View
{
    /// <summary>
    /// Interaction logic for PersonView.xaml
    /// </summary>
    public partial class PersonView : Window
    {
        #region Constructors
        public PersonView()
        {
            InitializeComponent();
        }
        #endregion

        #region Properties
        public PersonViewModel pvm { get; set; }
        #endregion

        #region Events and EventHandlers
        public event EventHandler NewPersonCreated;

        protected void OnNewPersonCreated(EventArgs e)
        {
            EventHandler handler = NewPersonCreated; //Link handler to event
            handler?.Invoke(this, e); //Invoke handler
        }

        private void OkButton_Click(object sender, RoutedEventArgs e)
        {
            pvm = new PersonViewModel(this.NameTextBox.Text, Convert.ToInt32(this.AgeTextBox.Text));
            OnNewPersonCreated(new EventArgs()); //Trigger the event
            this.Close();
        }

        private void CancelEventHandler(object sender, RoutedEventArgs e)
        {
            //When the cancel button is clicked...
            this.Close();
        }
        #endregion


        #region Methods


        #endregion
    }
}

PersonView.xaml

        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:MVVM_Basics.View"
        mc:Ignorable="d"
        Title="PersonView" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" Text="Name" Margin="30, 30"/>
        <TextBox x:Name="NameTextBox" Grid.Row="0" Grid.Column="1" Margin="30, 30"/>

        <TextBlock Grid.Row="1" Grid.Column="0" Text="Age" Margin="30, 30"/>
        <TextBox x:Name="AgeTextBox" Grid.Row="1" Grid.Column="1" Margin="30, 30"/>

        <Button x:Name="OkButton" Content="Ok" Grid.Row="2" Grid.Column="0" Click="OkButton_Click" Margin="30, 30"/>
        <Button x:Name="CancelButton" Content="Cancel" Grid.Row="2" Grid.Column="1" Click="CancelEventHandler" Margin="30, 30"/>
    </Grid>
</Window>

MainWindow.xaml.cs

using MVVM_Basics.ViewModel;
using System;
using System.Windows;

namespace MVVM_Basics
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Constructors
        public MainWindow()
        {
            InitializeComponent();
            pvm.Person.Name = "Name from constructor";
            this.DataContext = pvm; //Set Window's data context
        }
        #endregion

        #region Properties
        public PersonViewModel pvm { get; set; } = new PersonViewModel();

        #endregion

        #region Events and EventHandlers
        private void AddNewPerson(object sender, RoutedEventArgs e)
        {
            //When user clicks on File>New person...
            PersonView pv = new PersonView();
            pv.NewPersonCreated += Pv_NewPersonCreated; //Subscribe event handler to event
            pv.Show();
        }

        private void Pv_NewPersonCreated(object sender, EventArgs e)
        {
            //This delegate is called when PersonView creates a new PersonViewModel object
            PersonView pv = (PersonView)sender;
            pvm = pv.pvm; //Copy the PersonViewModel object from PersonView to MainWindow
            //NameTextBlock.Text = pvm.Person.Name; //This works, but I want to use bindings, not this hack
            pv.NewPersonCreated -= Pv_NewPersonCreated; //Unsubscribe event handler from event, going to close PersonView shortly
        }
        #endregion

        #region Methods
        #endregion
    }
}

MainWindow.xaml

        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:MVVM_Basics"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="400" >

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="25"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Menu Grid.Row="0">
            <MenuItem Header="_File">
                <MenuItem Header="Add _new person" Click="AddNewPerson"/>
            </MenuItem>
        </Menu>
        
        <Grid Grid.Row="1" Grid.Column="1" >
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto"/>
                <ColumnDefinition Width="auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            
            <!--Subgrid Row 0-->
            <TextBlock Text="Name:" Grid.Row="0" Grid.Column="0"/>
            <TextBlock x:Name="NameTextBlock" Text="{Binding Person.Name}"  Grid.Row="0" Grid.Column="1" MinWidth="50"  />

            <!--Subgrid Row 1-->
            <TextBlock Text="Age:" Grid.Row="1" Grid.Column="0"/>
            <TextBlock x:Name="AgeTextBlock" Text="{Binding Person.Age}" Grid.Row="1" Grid.Column="1"/>
        </Grid>
    </Grid>
</Window>

0 个答案:

没有答案