如何在XAML编辑器中指定DataContext(ViewModel)类型,以进行设计时绑定检查而无需创建ViewModel对象?

54

我可以像这样指定DataContext:

<Window ... >
    <Window.DataContext>
        <MainViewModel />
    </Window.DataContext>
    ...
</Window>
在这种情况下,WPF将创建一个类型为MainViewModel的对象,并将其分配给窗口的DataContext属性(这发生在Window的InitializeComponent()方法内部)。
但是,如果我的ViewModel没有默认构造函数,或者我想在Window.InitializeComponent()被执行之后(在Window的构造函数中或从实例化窗口的相同代码中)初始化ViewModel并将其赋值给DataContext,那该怎么办呢?在这种情况下,WPF会创建一个ViewModel(在InitializeComponent()内部),将其分配给窗口的DataContext,然后我会用另一个ViewModel的实例来覆盖它(我担心这里会产生不必要的对象实例化)。
我希望能够仅指定ViewModel的类型,这样如果我在{Binding}内错拼属性名称(或重命名属性后),我可以在设计时得到警告,或者通过单击{Binding PropertyName}内的属性名称(在XAML中)转到声明。
1个回答

63

如果您选择自己实现MVVM,那就是棘手的部分。

基本上有几种选择:

使用依赖注入

您可以在Page/Window构造函数中注入ViewModel,并在其中分配它。

但这有一些缺点:

  • 更难使用设计时视图模型
  • 无法再从XAML中实例化视图

首先使用ViewModel并使用导航服务

您将解决ViewModels并通过导航服务进行所有导航。在您的ViewModels中,您传递一个INavigationService。您可以通过使用ViewModel类型导航到视图。在其中通过依赖注入实例化ViewModel,然后实例化视图(基于命名约定或通过DI配置)

这比较好,但仍然无法在XAML中实例化视图。最大的优点是,它允许您轻松地将参数传递给ViewModel(使ViewModels实现带NavigatedTo方法的INavigationAware属性,在实例化和传递参数后调用该方法)。

ViewModelLocator / 附加属性/行为

使用此方法,您可以创建一个附加属性,将其设置为true(即自动连接)或ViewModel类型(以对实例化的ViewModel具有更多控制权),然后查找和解析ViewModel并分配它。

基本上,它提供了以上所有优点以及从View实例化。

最后一个基本上是微软的MVVM框架“Prism”所做的(通过导航服务navigationService.Navigate("MyPage", myParameterForViewModel),从XAML中通过自动连接实例化和分配DataContext(在XAML中:prism:ViewModelLocator.AutoWireViewModel="True")。

也就是说,最好使用成熟的MVVM框架来处理你的部分工作(即使你决定不使用基础类,如BindableBase或其他在该框架中称为的类)。

至于设计时ViewModel/自动完成ViewModel:

您可以使用Blend的设计时属性来完成这个任务。首先需要添加Blend程序集引用。然后您可以将xmlns:d="http://schemas.microsoft.com/expression/blend/2008"命名空间添加到您的页面/视图中。

然后,您可以通过d:DataContext="{d:DesignInstance my:DesignTimeViewModel, IsDesignTimeCreatable=True}将其绑定到页面中。注意在DataContext之前加上 d:,这很重要。此DataContext仅在设计器(Visual Studio XAML设计器或Blend中)中使用。这是为了防止干扰常规的DataContext(没有前缀)。

例如:

<Window x:Class="WpfApplication1.Window2"
        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:myApp="clr-namespace:WpfApplication1"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance myApp:Window2ViewModel, IsDesignTimeCreatable=True}">
    <Grid>
        <TextBlock Text ="{Binding Test}"/>
    </Grid>
</Window>

如果您在ViewModel中使用接口,那么创建设计实例就非常快速,只需要让Visual Studio实现所有接口属性并为其赋一些默认值(例如属性的示例数据,以便验证绑定是否正确)。

这需要您创建单独的设计时ViewModel和实际的ViewModel,但这并没有听起来那么糟糕。这使您的UI设计师有机会使用它,即使真正的ViewModel尚未完成/实现。


曾先生,感谢您的回复。 使用“依赖注入”选项时,将不会进行设计时绑定检查,即在XAML编辑器中,我将收到“由于未知的DataContext而无法解析符号XXX”的警告。此外,我将无法单击“{Binding PropertyName}”内的属性名称并自动跳转到属性声明。 - WpfNewbie
2
曾先生,我在您的答案中添加了一个示例(使用DesignInstance标记扩展)。请有权限的人审核一下(我还没有足够的“权限”)。另外,我不需要引用Blend程序集,并且必须添加mc:Ignorable =“d”才能编译通过。 - WpfNewbie
2
DesignInstance标记扩展解决了我的问题。谢谢。已标记为答案。 - WpfNewbie
感谢您提供的解决方案。IsDesignTimeCreatable=True。 - Ugur

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