【问题标题】:Should INotifyPropertyChanged also be implemented on model level?INotifyPropertyChanged 是否也应该在模型级别实现?
【发布时间】:2018-06-18 11:42:00
【问题描述】:

假设我有一个名为 MyViewModel 的视图模型,它的实现方式如下:

public class MyViewModel : BindableBase
{
    // BindableBase implements INotifyPropertyChanged
    // through the method SetProperty() which works fine.       
    private string _viewModelProperty
    public string ViewModelProperty 
    {
        get { return _viewModelProperty; }
        set { SetProperty(ref _viewModelProperty, value); } 
    }

    private NestedObjectModel _nestedObject;
    public NestedObjectModel NestedObject
    {
        get { return _nestedObject; }
        set { SetProperty(ref _nestedObject, value); }
    }
}

如您所见,它包含两个属性。一个字符串和一个对象(不涉及字符串是否也是对象的语义)。对象(或模型)也有一些属性,但这些不是直接用INotifyPropertyChanged 作为视图模型实现的。它可能看起来像这样:

public class NestedObjectModel
{
    public string Text { get; set; }
    public double Number { get; set; }
}

我想做的是在视图模型中使用INotifyPropertyChanged,以通知模型何时更改(即,如果TextNumber 被视图模型中的方法更改)。它可能看起来像这样:

// This method is located in the view model
public void ChangeTextInNestedObject(string newText)
{
    NestedObject.Text = newText;
    OnPropertyChanged(nameof(NestedObject));
}

问题是,我无法让它工作。该值已更改,但 XAML 未达到更改。我还尝试添加属性特定通知:

OnPropertyChanged(nameof(NestedObject.Text));

不能这样吗?我真的必须在模型级别实现INotifyPropertyChanged 吗?

【问题讨论】:

  • 如果模型属性直接在 XAML 中绑定,那么是的,您应该实现 INotifyPropertyChanged。另一种绑定方式(不实现INotifyPropertyChanged)是 --- 在 ViewModel 中有一个包装器属性,它是对象实际属性的包装器。
  • 感谢@user1672994。我直接绑定到 XAML 中的属性(例如NestedObject.Text),并且我不希望使用包装器,因为它会使视图模型变得一团糟。我只是希望可以对整个NestedObject 进行一般性通知,因此不必在模型级别实现INotifyPropertyChanged
  • 我认为模型可以使用 INotifyPropertyChanged
  • 您可以通过 NestedObject = new NestedObjectModel { Text = newText, Number = NestedObject != null ? NestedObject.Number : 0d }; 之类的方式避免在模型中实现 INotifyPropertyChanged 的​​必要性。但是,模型类中的 INotifyPropertyChanged 没有任何问题。
  • 您是否尝试过在您的ChangeTextInNestedObject() 方法中创建一个新的NestedObject 实例,而不仅仅是更新现有的? (IIRC 那么 WPF 倾向于通过引用缓存对象,只有在实际对象实例/引用发生变化时才更新后续/依赖绑定)

标签: c# wpf mvvm inotifypropertychanged


【解决方案1】:

它应该工作。 WPF 总是对PropertyChanged 事件作出反应。但是,您的 MVVM 框架可能会比较 OnPropertyChanged 方法中的新旧值,如果它们相同,则不会引发事件。也许有一些参数会强制引发事件,如果没有,您可以自己引发 PropertyChanged 事件。

查看视图模型的代码:

using System.ComponentModel;
using WpfApp1.Model;

namespace WpfApp1.Model
{
    public class NestedObjectModel
    {
        public string Text { get; set; }
        public double Number { get; set; }
    }
}

namespace WpfApp1.ViewModels
{
    class MyViewModel: INotifyPropertyChanged
    {
        private string _viewModelProperty;
        private NestedObjectModel _nestedObject;
        public event PropertyChangedEventHandler PropertyChanged;

        public string ViewModelProperty
        {
            get { return _viewModelProperty; }
            set {
                _viewModelProperty = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ViewModelProperty"));
                ChangeTextInNestedObject(value);
            }
    }

    public NestedObjectModel NestedObject
    {
        get { return _nestedObject; }
        set {
            _nestedObject = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("NestedObject"));
        }
    }

    public MyViewModel()
    {
        _nestedObject = new NestedObjectModel() { Text = "TExt", Number = 321.1 };
    }

    public void ChangeTextInNestedObject(string newText)
    {
        _nestedObject.Text = newText;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("NestedObject"));
    }
    }
}

还有 MainWindow.xaml:

<Window x:Class="WpfApp1.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:vm="clr-namespace:WpfApp1.ViewModels"
    xmlns:local="clr-namespace:WpfApp1"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
    <vm:MyViewModel />
</Window.DataContext>

<StackPanel Orientation="Vertical">
    <TextBox Text="{Binding ViewModelProperty, UpdateSourceTrigger=PropertyChanged}" />

    <Label Content="{Binding NestedObject.Text}" />
    <Label Content="{Binding NestedObject.Number}" />
</StackPanel>        

当您更改TextBox 中的文本时,第一个标签的内容也会更改。

HTH,汤姆

【讨论】:

    猜你喜欢
    • 2011-10-18
    • 2015-01-20
    • 2010-10-20
    • 2020-07-25
    • 2014-09-30
    • 2014-07-30
    • 1970-01-01
    • 2017-03-09
    • 1970-01-01
    相关资源
    最近更新 更多