如何在不创建 ViewModel 对象的情况下指定 DataContext (ViewModel) 类型以在 XAML 编辑器中进行设计时绑定检查? [复制]

Posted

技术标签:

【中文标题】如何在不创建 ViewModel 对象的情况下指定 DataContext (ViewModel) 类型以在 XAML 编辑器中进行设计时绑定检查? [复制]【英文标题】:How do I specify DataContext (ViewModel) type to get design-time binding checking in XAML editor without creating a ViewModel object? [duplicate] 【发布时间】:2015-06-06 14:15:09 【问题描述】:

我可以像这样指定 DataContext:

<Window ... >
    <Window.DataContext>
        <MainViewModel />
    </Window.DataContext>
    ...
</Window>

在这种情况下,WPF 将创建一个 MainViewModel 类型的对象并将其分配给窗口的 DataContext 属性(这发生在 Window 的 InitializeComponent() 方法中)。

但是如果我的ViewModel 没有默认构造函数怎么办。或者如果我想初始化 ViewModel 并在执行 Window.InitializeComponent() 之后将其分配给 DataContext(在 Window 的构造函数内或从实例化窗口的同一代码中) - 在这种情况下,WPF 创建一个 ViewModel(在 InitializeComponent() 内),分配它到窗口的 DataContext 然后我用另一个 ViewModel 实例覆盖它(我担心这里不必要的对象实例化)。

我希望能够仅指定 ViewModel 的类型,因此如果我在 Binding 中拼错了属性名称(或重命名属性之后),我会收到设计时警告,或者我可以转到通过单击(在 XAML 中)Binding PropertyName 内的属性名称进行声明。

感谢您宝贵的时间。

【问题讨论】:

【参考方案1】:

如果您自己动手做 MVVM,那就是棘手的部分。

你的选择,基本上:

使用依赖注入

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

但这有一些缺点。

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

带导航服务的 ViewModel 优先

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

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

ViewModelLocator / 附加属性/行为

有了这个,您将创建一个附加属性,您可以将其设置为true(即自动装配)或 ViewModel 类型(以更好地控制实例化的 ViewModel),然后查找并解析 ViewModel 并分配它。

它基本上提供了上述所有优点以及实例化表单视图。

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

话虽如此,最好使用成熟的 MVVM 框架来完成这部分接线(即使您决定不使用基类,例如 BindableBase 或在所述框架中调用的任何内容)。

关于 ViewModels 的设计时 ViewModel/自动完成:

您可以使用 Blend 的 Design-Time 属性来执行此操作。您需要先添加 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 和您的实际 ViewModel,这并不像听起来那么糟糕。这使您的 UI 设计师有机会使用它,即使真正的 ViewModel 尚未完成/实现。

【讨论】:

曾,谢谢您的回复。使用“使用依赖注入”选项将不会进行设计时绑定检查,即在 XAML 编辑器中,我将收到“由于未知数据上下文而无法解析符号 XXX”警告。此外,我将无法单击“Binding PropertyName”中的属性名称并自动跳转到属性声明。 Tseng,我在您的答案中添加了一个示例(带有 DesignInstance 标记扩展)。请有人批准(我还没有足够的“权力”)。另外,我不必引用 Blend 程序集,我必须添加 mc:Ignorable="d" 才能使其编译。 DesignInstance 标记扩展解决了我的问题。谢谢你。标记为答案。 感谢您的解决方案。 IsDesignTimeCreatable=True

以上是关于如何在不创建 ViewModel 对象的情况下指定 DataContext (ViewModel) 类型以在 XAML 编辑器中进行设计时绑定检查? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 如何在不使用 List 的情况下从 @ObservedObject ViewModel 获取 Firebase 数据

如何在不指定键值的情况下获取 JSON 对象的最后一个元素?

如何在不明确指定主键的情况下使用 Dapper Extensions 将对象插入 PostGreSql?

如何在不跟踪索引的情况下将元素附加到列表?

StringBuffer 如何在不创建两个对象的情况下实现 append 功能?

java - 如何在不创建对象的情况下找出文件和目录的大小?