【问题标题】:How to update UI change when property changed in MVVM如何在 MVVM 中更改属性时更新 UI 更改
【发布时间】:2017-11-25 22:19:55
【问题描述】:

我正在尝试在 WPF 中完成正确的 MVVM 架构。

我有一个播放器,在“模型”部分有一个布尔属性,表示我们现在是否正在“播放”。

public bool IsPlaying
    {
        get
        {
            return isPlaying;
        }
        set
        {
            isPlaying = value;
            OnPropertyChanged("IsPlaying");
        }
    }

(注意我实现了“INotifyPropertyChanged”接口,所以OnPropertyChanged函数会报告变化)

在我的 ViewModel 中,我有一个名为“ToggleButtonIcon”的 ImageSource 属性:

public ImageSource ToggleButtonIcon
    {
        get
        {
            if (Model.IsPlaying)
                return pauseIcon;
            else
                return playIcon;
        }
    }

我绑定到视图中的“TogglePlayButton”:

<cc:IconButton x:Name="TogglePlayButton" 
               Style="{StaticResource playButton}" 
               ImageSource="{Binding Path=ToggleButtonIcon,
               UpdateSourceTrigger=PropertyChanged}"  
               Command="{Binding Path=TogglePlayCommand}"/>

(这是一个自定义控件,但它正在工作,我检查了) 当然,我希望按钮根据是否正在播放(暂停)和是否未播放(播放)来更改其图像图标。

问题是 ToggleButtonIcon 在更改时没有通知,我无法在 ViewModel 部分实现 INotifyValueChanged,因为 a.我知道这不是 MVVM 架构的一部分,并且 b.我不知道它什么时候会改变,因为它取决于 Model 的 IsPlaying 属性。

我曾考虑将 ToggleButtonIcon 属性放在模型部分,但这不是“业务逻辑”,所以我认为这不是正确的方法。

我还考虑过使用转换器并将IconButton 直接绑定到“IsPlaying”,这可能会起作用,但我在这里读到:How can WPF Converters be used in an MVVM pattern? 你根本不应该在 MVVM 中使用转换器,因为你可以做任何事情在“ViewModel”部分中进行所需的转换。

在 MVVM 架构中实现此目的的最佳方法是什么?

【问题讨论】:

  • 您从哪里得到“这不是您的做法”的想法? ViewModel 通常实现 INotifyPropertyChanged。我会说模型这样做实际上并不常见,但您显然已经这样做了。如果你这样做,那么你将不得不在视图模型中订阅模型的 PropertyChanged 事件并引发适当的更改相关属性。另一种方法是仅绑定到模型对象并使用 DataTrigger 根据 IsPlaying 的状态更改图像源。
  • “你从哪里得到这个想法的......” - 我在 youtube 上看到了一个例子。 “你必须订阅模型的 PropertyChanged 事件......” - 我该怎么做? “...并使用 DataTrigger 根据 IsPlaying 的状态更改图像源” - 这是什么意思,我该怎么做?

标签: c# wpf mvvm


【解决方案1】:

对我来说,IsPlaying 应该在 ViewModel 中,并实现了更改通知,因为它代表了某种应用程序状态。

为了解决这个问题,我建议将 ToggleButtonIcon 从 ViewModel 中取出,并在 IconButton 控件上创建一个 DataTrigger(通过其 Style),它绑定到 IsPlaying 属性(在ViewModel) 并在此基础上更改 ImageSource 属性。

【讨论】:

    【解决方案2】:

    MVVM 的模型应该只包含类实体,这些实体有时可以有一个INotiftPropertyChanged,但通常它们没有。

    您的意图是传达一种状态,并且应该在您的视图模型上。

    我建议您将IsPlaying 的状态放在视图模型 (VM) 上并绑定到它。然后在TogglePlayCommand 的命令中,它将在VM 上设置该属性。

    这样,两个项目都会在对其中任何一个进行更改时正确更新。 如果需要,您仍然可以在模型中新建原始对象,并在 VM 的 IsPlayingSetter 上将类实例属性设置为其值


    看看我的博文Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding。请注意我如何使用 OnPropertyChanged 将更改消息推送到其他操作上,这些都是为了您寻求的灵活性以及让视图模型保持状态而不是模型。

    【讨论】:

    • 所以你建议将 IsPlaying BOTH 放在模型和视图模型中?那么“ViewModel.IsPlaying”会从“Model.IsPlaying”获取/设置它的值吗?
    • 我更愿意在 ViewModel 中保留任何视图操作,但不清楚为什么 OP 在模型上使用它。
    【解决方案3】:

    你应该把 bool 放在实现接口 INotifyPropertyChange 的类上: 举个例子:

    public class Game : INotifyPropertyChanged
    {
        private bool _isPlaying;
    
        public string IsPlaying
        {
            get { return _isPlaying; }
            set {
                _isPlaying = value;
                this.NotifyPropertyChangedBool();    
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void NotifyPropertyChangedBool()
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new 
                               PropertyChangedEventArgs("IsPlaying"));
            }
        }
    

    【讨论】:

    • 我就是这么做的! (在“模型”控制杆中)但我需要绑定一个图像,而不是一个布尔值。
    猜你喜欢
    • 2013-12-02
    • 1970-01-01
    • 2020-02-11
    • 1970-01-01
    • 2015-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多