C# MVVM INotifyPropertyChanged与基类PropertyChange冲突

C# MVVM INotifyPropertyChanged与基类PropertyChange冲突,c#,wpf,binding,mvvm,inotifypropertychanged,C#,Wpf,Binding,Mvvm,Inotifypropertychanged,我对MVVM比较陌生,我试图了解INotifyPropertyChanged接口是如何工作的,以及如何在我的模型中实现它。我决定采取的方法是在我的每个业务对象类中实现它该方法的问题是,当我将视图绑定到基类中的属性时,该基类中的PropertyChanged事件从未初始化(为null),因此当我的模型更改时,视图不会刷新该元素的数据。我能够用下面的例子重现这个问题 我有一个人基类: public class Person : INotifyPropertyChanged {

我对MVVM比较陌生,我试图了解INotifyPropertyChanged接口是如何工作的,以及如何在我的模型中实现它。我决定采取的方法是在我的每个业务对象类中实现它
该方法的问题是,当我将视图绑定到基类中的属性时,该基类中的PropertyChanged事件从未初始化(为null),因此当我的模型更改时,视图不会刷新该元素的数据。我能够用下面的例子重现这个问题

我有一个人基类:

 public class Person : INotifyPropertyChanged
    {
        #region INotifyProperty

        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        public String Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
        private String _name;
    }
 public class Employee : Person,INotifyPropertyChanged
    {
        #region INotifyProperty

        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion


        public String EmployeeID
        {
            get
            {
                return _employeeId;
            }
            set
            {
                _employeeId = value;
                RaisePropertyChanged("EmployeeID");
            }
        }
        private String _employeeId;
    }
我有一个从Person基类继承的Employee类:

 public class Person : INotifyPropertyChanged
    {
        #region INotifyProperty

        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        public String Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
        private String _name;
    }
 public class Employee : Person,INotifyPropertyChanged
    {
        #region INotifyProperty

        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion


        public String EmployeeID
        {
            get
            {
                return _employeeId;
            }
            set
            {
                _employeeId = value;
                RaisePropertyChanged("EmployeeID");
            }
        }
        private String _employeeId;
    }
这里是我的视图模型:

 public class ViewModel : ViewModelBase<ViewModel>
    {
        private Employee _employee;

        public ViewModel()
        {
            ChangeModelCommand = new RelayCommand(param=>this.ChangeModel() , param=>this.CanChangeModel);
            Employee = new Employee()
                         {
                             Name = "BOB",EmployeeID = "1234"
                         };
        }

        public ICommand ChangeModelCommand { get; set; }


        public Employee Employee
        {
            get
            {
                return _employee;
            }
            set
            {
                this._employee = value;
                NotifyPropertyChanged(m=>m.Employee);
            }
        }

        public void ChangeModel()
        {
            MessageBox.Show("CHANGING MODEL");
            this.Employee.Name = "MIKE";
            this.Employee.EmployeeID = "5678";
        }
        public bool CanChangeModel
        {
            get{ return true;}
        }
    }
public类ViewModel:ViewModelBase
{
私人雇员(u雇员),;
公共视图模型()
{
ChangeModelCommand=newrelaycommand(param=>this.ChangeModel(),param=>this.CanChangeModel);
雇员=新雇员()
{
Name=“BOB”,EmployeeID=“1234”
};
}
公共ICommand ChangeModelCommand{get;set;}
公务员
{
得到
{
返回(u)员工;;
}
设置
{
这。_employee=价值;
NotifyPropertyChanged(m=>m.Employee);
}
}
公共模型()
{
MessageBox.Show(“更改模型”);
this.Employee.Name=“MIKE”;
this.Employee.EmployeeID=“5678”;
}
公共布尔变换模型
{
获取{return true;}
}
}
最后,我的观点是:

<Window.Resources>
    <MVVM_NotificationTest:ViewModel x:Key="Model"></MVVM_NotificationTest:ViewModel>
</Window.Resources>
<Grid DataContext="{StaticResource Model}">
<StackPanel>
    <Label Content="Employee Name"/>
    <TextBox Text="{Binding     Path=Employee.Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
    <Label Content="Employee ID"/>
    <TextBox Text="{Binding Path=Employee.EmployeeID,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
    <Button Content="Change Model" Height="30" Width="100" Margin="5" Command="{Binding Path=ChangeModelCommand}"/>
</StackPanel>
</Grid>

在本例中,我在VM构造函数中初始化Employee VM属性,然后使用命令修改EmployeeID(来自Employee类)和Name(来自Person类)。但是,视图中唯一更新的UI元素是EmployeeID,而不是名称(我希望Bob更新为Mike)。

调试时,我发现PropertyChanged事件在我的基类(Person)中始终为null。我还注意到,当我从Employee类中删除整个#inotifProperty区域时,一切正常,因为它使用了基类型事件和方法。

我的问题是,我当前的所有模型类都显式实现了inotifProperty更改。它们都定义了一个PropertyChanged事件并实现了RaisePropertyChanged方法,这显然会影响我在MVVM应用程序中的绑定。

最后,我想澄清的是,我不希望在ViewModel中包装模型属性,而依赖于VM INPC机制。我希望使用已经存在的模型INPC实现,而不必根据是否从基类型继承而有条件地删除INPC实现。


总之,我想知道在我的深层层次模型中实现INPC的最佳方法是什么,这样继承就不会像我们在本例中看到的那样破坏PropertyEvent传播,这样我的独立类也可以自给自足。任何想法或建议都将不胜感激:)

只需将RaisePropertyChanged设置为受保护,并将其移动到基类中即可。目前,您将有许多不必要的重复

大概是这样的:

protected virtual void RaisePropertyChanged(string propertyName);

许多MVVM框架都为您提供了这一点。例如,PRISM有一个ViewModel基类。

您应该只实现
INPC
一次,您可以在子类中使用相同的提升方法。

我还将提升属性更改方法更改为使用反射,而不是传递硬编码字符串。我看到您在视图模型中这样做了,但在模型中没有这样做(大多数错误都会发生在模型中)

正是我所想的:pHi@RichardOD,我只是尝试了一下,但似乎不起作用。只有当我将PropertyChanged偶数声明和RaisePropertyChanged方法都移动到基类时,它才起作用……问题是,我动态生成了很多类,因此INPC实现被大量添加到每个类中。如果这是解决方案,那么我需要一个更复杂的逻辑来消除从基类继承的实现:(是的-这就是它们都属于的地方。我也会以线程安全的方式来看待触发事件-您的问题是您在类中实现了INotifyPropertyChanged,而您应该只从ModelBase继承,ModelBase已经实现了这个接口,并且包含了一个NotifyPropertyChanged方法来引发PropertyChanged事件,并且通过UI线程封送调用。您无需在类中实现接口-只需从每个属性setter调用NotifyPropertyChanged,使用lambda表达式指定属性名称。这是一个很好的观察@tsells。原因是我使用的是Tony Sneed提供的简单MVVM框架中的ViewModelBase我的模型类必须以类似的方式实现它,以避免像您所说的打字错误。为了澄清这一点,Simple MVVM Toolkit有一个基类,它实现INotifyPropertyChanged,并有一个NotifyPropertyChanged方法,该方法接受属性名称的lambda表达式,而不是字符串,这使得它是类型安全的。该方法还封送对UI线程的调用。请同时查看NotifyPropertyWeaver。为您省去了很多麻烦;)谢谢Martin,我刚刚查看了NotifyPropertyWeaver,它似乎确实为您省去了很多工作。我想知道它将如何处理深层类层次结构(继承),我不确定这是否会在继承时导致相同的PropertyChanged事件断开连接问题,出于好奇,我可能会尝试一下。