WPF MVVM 代码背后的最佳实践

问题描述 投票:0回答:3

我是一名使用 MVVM 模式通过 WPF 学习 C# 的学生。最近,我一直在研究我的应用程序的艺术(自定义启动屏幕),当我不希望它关闭时,它不应该关闭。 我一直在网上搜索一种无需后台代码即可完成此操作的好方法。不幸的是,几天后我仍然没有找到令人满意的方法。 然后我想到了一种自己做这件事的方法,只需在我的视图构造函数中添加一行代码即可。它仍然使我的代码可测试,并将代码与视图解耦。问题是,有没有更好的方法来做我想做的事情:

我的 ViewModel 界面

public interface IPreventCloseViewModel
{
    bool PreventClose { get; set; }
}

视图的扩展

public static class PreventCloseViewModelExtension
{
    /// <summary>
    /// Use this extension method in the constructor of the view.
    /// </summary>
    /// <param name="element"></param>
    public static void PreventCloseViewModel(this Window element)
    {
        var dataContext = element.DataContext as IDisposable;
        if (dataContext is IPreventCloseViewModel)
        {
            element.Closing += delegate(object sender, CancelEventArgs args)
                                   {
                                       if (dataContext is IPreventCloseViewModel)
                                       {
                                           args.Cancel = (dataContext as IPreventCloseViewModel).PreventClose;
                                       }
                                   };
        }
    }
}

视图的隐藏代码

public partial class SplashScreen
{
    public SplashScreen()
    {
        InitializeComponent();
        this.PreventCloseViewModel();
    }
}
c# wpf mvvm code-behind
3个回答
16
投票

MVVM并不意味着你不能使用Code-Behind。

MVVM 意味着您的应用程序逻辑不应与 UI 元素绑定。

您可以很好地处理后面代码中的事件(例如

Window.Closing
),并“发送消息”或执行 ViewModel 中的方法来对此做出反应。

在这里,您不会通过将事件处理程序放在代码后面来破坏 MVVM。如果您将确定应用程序是否可以关闭的逻辑放在代码后面,那么您将破坏 MVVM。这是应用程序逻辑的责任,应用程序逻辑存在于 ViewModel 中,而不是 View 中。


4
投票

我通常有一个通用的

Shell
类,它是
Window
的子类,并执行以下操作:

public Shell()
{
    InitializeComponent();
    this.Closing += (s,e) =>
    {
        var canClose = Content as ICanClose;
        if (canClose != null)
            e.Cancel = !canClose.CanClose;
    }
}

这样,无论您放入哪种视图模型,只要它实现了要考虑的接口即可。

看不出将逻辑具体化有什么意义,就 MVVM 模式而言就很好了。


0
投票

Fede 的答案绝对正确,但我认为有一种更好、更 MVVM 友好的方法来做到这一点,只需稍微扩展你的

Window
类即可:

    #region bool PreventClose dependency property
    public static readonly DependencyProperty PreventCloseProperty = DependencyProperty.Register(
        "PreventClose",
        typeof(bool),
        typeof(MainWindow),
        new PropertyMetadata(false));
    public bool PreventClose
    {
        get
        {
            return (bool)GetValue(PreventCloseProperty);
        }
        set
        {
            SetValue(PreventCloseProperty, value);
        }
    }
    #endregion

    protected override void OnClosing(CancelEventArgs e)
    {
        if (this.PreventClose)
        {
            e.Cancel = true;
            return;
        }
        base.OnClosing(e);
    }

现在,您的视图代码不需要了解有关数据上下文或视图模型类型的任何信息,您只需将 XAML 中的新

PreventClose
属性绑定到视图模型上的某个相应属性即可。

这实际上是为了准确定义“代码隐藏”的含义。我讨厌语义参数(尤其是在这个网站上),但太多人将代码隐藏定义为视图层中的任何 C# 代码。事实并非如此——或者至少,这不是 MVVM 不喜欢的代码隐藏类型。 我们要避免的那种隐藏代码是视图层中与视图模型混合在一起的 C# 代码,或者实现属于抽象视图模型的逻辑。在您的示例中,窗口(或者更确切地说是扩展方法)需要知道视图模型的类型以及有关它的其他细节才能工作。 MVVM 所追求的视图和视图模型之间的清晰分离就这样被破坏了。

在我的示例中,我们以完全与应用程序无关的方式扩展

Window

。您可以将这个子类

Window
放入 WPF 帮助程序库中,以便在您的职业生涯的其余部分中使用。由于它是一个可绑定属性,因此您可以通过简单的绑定来连接视图模型和视图。
这与视图是否应该包含 C# 代码无关。重要的是 C# 代码的作用。只要您将其保持特定于 UI,并尽量不要让它接触视图模型(除非尽可能通过绑定),您就可以忠实于该模式并实现其全部好处。

© www.soinside.com 2019 - 2024. All rights reserved.