MVVM中属性更改如何更新UI变化

4

我正在尝试在WPF中实现正确的MVVM架构。

我有一个播放器,在"Model"部分有一个布尔属性,用于表示我们当前是否正在"播放"。

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属性放在Model部分,但那不是“业务逻辑”,所以我认为那不是正确的方式。

我也考虑过使用转换器并直接将IconButton绑定到“IsPlaying”,这可能有效,但我在这里读到:如何在MVVM模式中使用WPF转换器?,你根本不应该在MVVM中使用转换器,因为可以在“ViewModel”部分进行任何转换。

在MVVM架构中完成这项任务的最佳方法是什么?


1
你从哪里得到了“这不是正确的做法”的想法?ViewModel通常实现INotifyPropertyChanged。我会说,对于模型来说实际上很少这样做,但你显然已经这样做了。如果你选择这种方式,那么你将不得不在ViewModel中订阅模型的PropertyChanged事件,并引发相应的更改依赖属性。另一种方法是只绑定到模型对象,并使用DataTrigger根据IsPlaying的状态更改图像源。 - Mike Zboray
“你是从哪里得到这个想法的?” - 我在YouTube上看到了一个例子。 “你需要订阅模型的PropertyChanged事件…” - 我该怎么做呢? “…并使用DataTrigger根据IsPlaying的状态更改图像源” - 这是什么意思,我该如何做到? - Shachar Har-Shuv
3个回答

2

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

为了解决这个问题,我建议将 ToggleButtonIcon 从 ViewModel 中剥离,并在 IconButton 控件上创建一个 DataTrigger(通过其 Style),该触发器绑定到 ViewModel 上的 IsPlaying 属性,并根据其更改 ImageSource 属性。


0
你应该在实现INotifyPropertyChange接口的类上使用bool: 这里是一个例子:
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"));
        }
    }

这就是我所做的!(在“模型”级别上)但我需要绑定一个图像,而不是布尔值。 - Shachar Har-Shuv

0

MVVM模型应仅包含类实体,这些实体偶尔可以具有INotiftPropertyChanged,但通常不会。

您的意图是要传达一个状态,该状态应该在您的视图模型中。

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

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


请看我的博客文章Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding。请注意我如何使用OnPropertyChanged在其他操作中推送更改消息,这可以为您寻求的灵活性以及让视图模型保持状态而不是模型提供帮助。

所以你建议将IsPlaying同时放在模型和视图模型中? 这样,"ViewModel.IsPlaying"的值将从"Model.IsPlaying"获取/设置? - Shachar Har-Shuv
我更喜欢将任何视图操作放在ViewModel中,但不清楚为什么OP在Model上使用它。 - ΩmegaMan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接