如何使用 DataContext 属性在 XAML 中的窗口上设置 ViewModel?

Posted

技术标签:

【中文标题】如何使用 DataContext 属性在 XAML 中的窗口上设置 ViewModel?【英文标题】:How do I set a ViewModel on a window in XAML using DataContext property? 【发布时间】:2011-06-03 04:34:42 【问题描述】:

这个问题几乎说明了一切。

我有一个窗口,并尝试使用 ViewModel 的完整命名空间设置 DataContext,但我似乎做错了什么。

<Window x:Class="BuildAssistantUI.BuildAssistantWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="BuildAssistantUI.ViewModels.MainViewModel">

【问题讨论】:

在 Mike Nakis 之后,我试图手动创建 ViewModel 并订阅其中的事件,却发现框架正在创建另一个 ViewModel。因此,我订阅的 viewModel 不是附加到视图的那个。 这是否意味着除了自己实例化视图模型之外,您还以其他方式指定了视图模型的类型?需要构造函数参数的视图模型的第二个优点是框架要么无法实例化它们,要么必须为这些参数传递默认值,在这种情况下,您可以轻松检测框架的实例化。 XAML 设计器可能还需要能够实例化视图模型,但是这个设计器对我从来没有任何用处,(它只会导致问题,)所以我不使用它,所以我个人不关心那个用例。 DataContext="Binding Source=x:Type BuildAssistantUI.ViewModels.MainViewModel" 我们可以像这样使用 x:Type 吗?.. 但是,它不起作用。 【参考方案1】:

试试这个。

<Window x:Class="BuildAssistantUI.BuildAssistantWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:VM="clr-namespace:BuildAssistantUI.ViewModels">
    <Window.DataContext>
        <VM:MainViewModel />
    </Window.DataContext>
</Window>

【讨论】:

我最喜欢这个选项。如果 VM 仅用于 MainWindow,看起来更干净。 有没有办法使用Window 元素上的属性设置数据上下文,例如DataContext="VM:MainWindowViewModel" 这才是正道! 我并不完全理解为什么一种方法比另一种更好。此外,与我看到一些人使用“动态资源”的方式相比,我并没有完全看到这两种方式的区别。这是什么? @Oliver 你必须实现MarkupExtension,从来没有在虚拟机上做过,但你可以用转换器来确保只有一个转换器实例存在,并使用@987654325直接从xaml调用它@,暗示 xmlns:converters 指向转换器命名空间。 public abstract class BaseValueConverter&lt;T&gt; : MarkupExtension, IValueConverter where T : class, new() private static T _converter; public override object ProvideValue(IServiceProvider serviceProvider) return _converter ?? (_converter = new T()); 【参考方案2】:

除了其他人提供的解决方案(很好且正确)之外,还有一种方法可以在 XAML 中指定 ViewModel,但仍将特定的 ViewModel 与 View 分开。当您想编写独立的测试用例时,将它们分开很有用。

在 App.xaml 中:

<Application
    x:Class="BuildAssistantUI.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:BuildAssistantUI.ViewModels"
    StartupUri="MainWindow.xaml"
    >
    <Application.Resources>
        <local:MainViewModel x:Key="MainViewModel" />
    </Application.Resources>
</Application>

在 MainWindow.xaml 中:

<Window x:Class="BuildAssistantUI.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="StaticResource MainViewModel"
    />

【讨论】:

哦哇...谢谢。我已经将此标记为已回答,但非常感谢您的补充。会用的。 @Nicholas:另一个答案非常适合这个问题,所以我同意你的决定 请注意,这种方法对 MainWindow 的每个实例都使用相同的 ViewModel 实例。如果窗口是单实例,这很好,但如果您显示窗口的多个实例,例如在 MDI 或选项卡式应用程序的情况下,则不是。 实际上 Josh 的答案更好,因为它为您提供了 DataContext 上的类型安全性。因此,您可以直接绑定到 DataContext 而不必担心打错某些属性名称/路径。 &lt;TextBlock &gt; &lt;TextBlock.DataContext&gt; &lt;local:BaseViewModel/&gt; &lt;/TextBlock.DataContext&gt; &lt;/TextBlock&gt; 这很好用。但是,我想要这样的内联&lt;TextBlock DataContext="Binding Souce=x:Type local:BaseViewModel" /&gt;【参考方案3】:

您需要实例化 MainViewModel 并将其设置为 datacontext。在您的声明中,它只是将其视为字符串值。

     <Window x:Class="BuildAssistantUI.BuildAssistantWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:BuildAssistantUI.ViewModels">
      <Window.DataContext>
        <local:MainViewModel/>
      </Window.DataContext>

【讨论】:

谢谢,我认为它正在这样做。【参考方案4】:

还有这种指定viewmodel的方式:

using Wpf = System.Windows;

public partial class App : Wpf.Application //your skeleton app already has this.

    protected override void OnStartup( Wpf.StartupEventArgs e ) //you need to add this.
    
        base.OnStartup( e );
        MainWindow = new MainView();
        MainWindow.DataContext = new MainViewModel( e.Args );
        MainWindow.Show();
    

之前提出的所有解决方案都要求MainViewModel 必须有一个无参数的构造函数。

Microsoft 的印象是可以使用无参数构造函数来构建系统。如果您也有这种印象,请继续使用其他一些解决方案。

对于那些知道构造函数必须有参数,因此对象的实例化不能交给魔术框架的人来说,指定视图模型的正确方法是我上面展示的方法。

咆哮>

【讨论】:

如果我有一个带有 MainView 和 MainViewModel 的 &lt;UserControl&gt; 怎么办 - 这里我没有 OnStartup 事件,这实际上是我现在正在研究的... @PandaWood 我不关注你。您的UserControl(我们称之为MyUserControl)是应用程序的根视觉对象,还是位于窗口内?在这两种情况下,MyUserControl 一个视图,因此不清楚其他 MainView 是如何融入图片的。 如果MyUserControl 应该存在于另一个视图中(例如,在MyMainView 中),那么MyMainViewModel 将有一个MyUserControlViewModel 成员属性,所以在MainWindowView 中你只需说&lt;MyUserControl DataContext="Binding MyUserControlViewModel" /&gt; 请注意,MyUserControlViewModel 存在于 MyMainViewModel 中,因此 MyMainViewModel 会对其进行实例化,因此它可以将所需的任何构造函数参数传递给它。【参考方案5】:

您可能想试试Catel。它允许您定义一个 DataWindow 类(而不是 Window),该类会自动为您创建视图模型。这样,您可以像在原始帖子中那样使用 ViewModel 的声明,并且仍将创建视图模型并将其设置为 DataContext。

有关示例,请参阅 this article。

【讨论】:

以上是关于如何使用 DataContext 属性在 XAML 中的窗口上设置 ViewModel?的主要内容,如果未能解决你的问题,请参考以下文章

XAML - 绑定到 DataContext 并使用转换器?

在此示例中,在 XAML 中使用 DataContext 和代码背后有啥区别?

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

为啥在 XAML 中绑定 MainWindow 数据上下文与使用 this.datacontext=this 在代码隐藏中绑定的行为不同?

在 WPF 中的 XAML 中设置 DataContext

如何使用不在代码后面的 XAML 绑定 ListView ItemsSource。?