C# WPF MVVM窗口加载绑定

4

我的代码后台看起来像这样...

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainWindowViewModel();
    }
}

我的视图模型看起来像这样...

class MainWindowViewModel : INotifyPropertyChanged
{
    public MainWindowViewModel()
    {
        bool flag = Application.Current.MainWindow.IsInitialized;

        if (flag)
        {
            // Do something...
        }

    }

我猜我的问题是...这符合MVVM设计模式吗?唯一的另一种方法是如何在WPF中加载窗口时触发命令
我不知道为什么,但我不想使用mvvm-light或任何其他样板代码。

一切都好。不用担心 :) - dev hedgehog
5
你发布的链接没有使用MVVM Light——如果你正在使用MVVM,最终会想要使用System.Windows.Interactivity,因为它可以解决很多问题。请注意,这句话已经被翻译成了中文。 - Reed Copsey
1
如果你要使用Blend,那么@ReedCopsey给出了很好的建议。此外,还应该研究Prism/MEF中发现的CompositePresentationEvent类或类似的东西,这些东西可以代理应用程序级别的消息。你可以在不使用完整的Prism范例的情况下使用它们。 - Rob Perkins
@RobPerkins 我建议你使用 System.Windows.Interactivity.dll,即使你不使用 Blend :) 它充满了对 MVVM(行为等)非常有用的工具。 - Reed Copsey
5个回答

4
从ViewModel访问UI组件违反了MVVM模式。 Application.Current.MainWindow.IsInitialized也违反了此模式。
自定义行为更符合MVVM。因此,我建议采用您在问题中提到的链接方法。
访问UI组件会破坏ViewModel的可测试性。你如何为你的ViewModel类编写测试用例?当你尝试通过单元测试来测试它时,Application.Current将是null并且它会引发空引用异常。
MVVM的主要动机之一是将UI逻辑与业务逻辑分离,以便可以单独测试业务逻辑而不必担心其使用者(即视图)。

1
虽然通常认为具有一些装载/卸载逻辑是一种模式违规,但在某些用例中是必要的。例如,视图模型可能需要订阅某些事件。如果在卸载时没有取消订阅,则根据订阅的性质,它可能无法被垃圾回收。
真正违反模式的是从视图模型中访问视图状态,例如操作控件。视图模型的作用是向视图公开数据,并管理装载/卸载行为是这个契约的一部分。了解何时加载视图模型意味着知道何时公开该数据。
虽然视图模型不应关心视图的状态,但必须知道如何为在视图中展示的数据做准备。更重要的是,视图模型是模型和视图之间的一层,使它们分离。换句话说:“模型”意味着逻辑,那么“视图模型”意味着获取数据以显示的逻辑。这也涉及知道何时获取数据/使其可用等。
你可能想看一下这篇博客文章,它提供了一种方便的方法使视图模型知道自己已被加载。从MVVM的纯度来说,它并不是100%正确的,因为它把FrameworkElement传回到视图模型中,但是假设我们忽略这个参数。
下面的示例代码基于上述博客文章,但具有更纯净的签名。你可以在你的类上实现IViewModel接口:
public interface IViewModel : INotifyPropertyChanged
{
    void Load();
    void Unload();
}

然后使用附加属性指示视图在加载或卸载时调用适当的方法:

ViewModelBehavior.LoadUnload="True"

请注意最后一行在XAML中的位置 - 视图是强制执行某种行为的,而不是相反。

1
在MVVM中,没有"纯粹"的方法可以做到这一点,而不需要样板代码。通常情况下,在您的VM中不应该根据View的情况进行工作 - 只是概念就违反了MVVM,因为您的ViewModel正在尝试响应View并且事情应该总是反过来流动。
在实际场景中,ViewModel不应关心View的状态 - 它除了通过数据绑定呈现数据之外什么也不应该做。
大多数情况下,当人们尝试这样做时,是为了避免预先加载数据。这通常可以通过将要加载的数据推送并直接在ViewModel中启动后台线程来更好地处理,然后在完成时更新VM中的属性。 C#5的async/await语言特性可以用于大大简化此过程。

我很感激大家对此的贡献。MVVM只是一种设计模式,但考虑到它在商业应用中的广泛使用,希望它能更多一些。我没有问题绑定数据,使用INotifyPropertyChanged和ICommand,但像这样的东西有太多的解释空间... - David Russell
1
@DavidRussell 说到底,几乎每个人都同意VM不应该知道View的任何信息-即:http://reedcopsey.com/blog/wp-content/uploads/2010/01/MVVM.png如何连接其余部分取决于具体情况,不同的框架都有自己的想法。一旦你掌握了基本知识,框架确实会非常有帮助(即使你编写自己的框架),但是像“EventToCommand”、“behaviors”和其他事件触发器等东西对于“真正”的应用程序不违反MVVM是至关重要的。 - Reed Copsey

0

你目前所做的是正确的,这也是其他框架在幕后实际上所做的。

既然你提到了MVVM-Light,我建议你可以看一下caliburn micro。它有非常好的框架来符合MVVM模式。Caliburn micro使得将绑定与控件上的事件连接起来变得容易。只需查看其文档,它仍被认为是MVVMy..


我对MVVM还比较新。很遗憾,所有人都是用代码后台教授WPF,但实际上应该只使用mvvm进行教学。这基本上是我在窗口加载时触发某些操作的技巧。 - David Russell
也许我在追求纯粹,但是如果可能的话,我不喜欢在我的代码中使用第三方框架。我会尝试使用 Caliburn Micro……看看它是否能够说服我……谢谢。 - David Russell
1
@DavidRussell 关于“教授WPF” - 你可能想看看我关于它的系列文章 http://reedcopsey.com/series/windows-forms-to-mvvm/ - Reed Copsey
1
我不同意你的说法,认为这种做法是直接违反了MVVM模式... - Reed Copsey
@ReedCopsey,我指的是第一个源代码是正确的,而不是第二个。我完全不同意你从ViewModel中访问Application.Current的做法。 - 123 456 789 0

0

特别是因为MVVM主要用于保证易于维护和可测试的代码,您应该记住,如果您在单元测试中使用MainViewModel,则Application.Current将为空。因此,这段代码将在您的测试中导致NullPointerException

如果您想确保某些内容已初始化,您应该考虑使用Initialized事件。但是,您在调用InitializeComponent之后创建ViewModel - 我会简单地跳过检查。


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