【问题标题】:MVVM INotifyPropertyChanged conflict with base class PropertyChangeMVVM INotifyPropertyChanged 与基类 PropertyChange 冲突
【发布时间】:2012-02-21 15:36:44
【问题描述】:

我对 MVVM 比较陌生,我正在尝试了解 INotifyPropertyChanged 接口的工作原理以及如何在我的模型中实现它。我决定采用的方法是在我的每个业务对象类中实现它。
这种方法的问题在于,当我将视图绑定到基类中的属性时,该基类中的 PropertyChanged 事件永远不会被初始化(为空),因此当我的视图不会刷新该元素的数据时模型变化。我能够通过下面的示例重现该问题。

我有一个 Person Base 类:

 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;
    }

我有一个 Employee 类继承自我的 Person Base 类:

 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;}
        }
    }

最后是我的观点:

<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 事件在我的基类中始终为空(人)。我还注意到,当我从 Employee 类中删除整个 #INotifyProperty 区域时,一切正常,因为它使用了 Base Type 事件和方法。

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

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

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

【问题讨论】:

    标签: c# wpf binding mvvm inotifypropertychanged


    【解决方案1】:

    只需将 RaisePropertyChanged 设为受保护并将其移至基类即可。目前你会有很多不必要的重复。

    类似这样的:

    protected virtual void RaisePropertyChanged(string propertyName);
    

    许多 MVVM 框架为您提供了此功能。例如,PRISM 有一个NotificationObject ViewModel 基类。

    【讨论】:

    • 正是我的想法:p
    • 嗨@RichardOD,我刚试过,但似乎没有用。它仅在我将 PropertyChanged 偶数声明和 RaisePropertyChanged 方法移至基类时才有效...
    • 问题是我有很多动态生成的类,所以 INPC 实现被大量添加到每个类中。如果这是解决方案,那么我需要有一个更复杂的逻辑来消除从基类继承的实现:(
    • 是的,这就是他们俩的归属。我也会以线程安全的方式查看触发事件 - stackoverflow.com/questions/1868877/…
    • 你的问题是你在你的类中实现了 INotifyPropertyChanged,而你应该只从 ModelBase 继承,它已经实现了这个接口并包含一个 NotifyPropertyChanged 方法来引发 PropertyChanged 事件并编组调用 UI线。您无需在类中实现接口 - 只需从每个属性设置器调用 NotifyPropertyChanged,使用 lambda 表达式指定属性名称。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      我还将更改 raise 属性更改方法以使用反射而不是传入硬编码字符串。我看到你是在你的视图模型中做到的,但不是在你的模型中(大多数错误往往会发生)。

      【讨论】:

      • 这是一个很好的观察@tsells。原因是我使用了来自 Tony Sneed 的 Simple MVVM Framework 中的 ViewModelBase,它以这种方式实现了 INPC。我的模型类必须以类似的方式实现它,以避免像你说的那样出现拼写错误。
      • 澄清一下,Simple MVVM Toolkit 有一个实现 INotifyPropertyChanged 的​​基类,并有一个 NotifyPropertyChanged 方法,它接受属性名称的 lambda 表达式——而不是字符串——这使得它是类型安全的。该方法还编组对 UI 线程的调用。
      • 请查看 NotifyPropertyWeaver。让你免于痛苦;)code.google.com/p/notifypropertyweaver
      • 感谢 Martin,我刚刚查看了 NotifyPropertyWeaver,它似乎确实为您节省了很多工作。我想知道它如何与深层类层次结构(继承)一起使用,我不确定这是否会导致我在继承时出现相同的 PropertyChanged 事件断开连接问题,出于好奇,我可能会尝试一下。
      猜你喜欢
      • 1970-01-01
      • 2011-03-11
      • 2020-11-26
      • 1970-01-01
      • 2015-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多