如何使用DataTemplate设置DataContext

4

我听说在MVVM中为视图设置DataContext的最佳方法是使用DataTemplate。因此,我尝试使用DataTemplate将MainWindow的DataContext设置为MainWindowViewModel的实例。

但是我还没有弄清楚如何做到这一点。

我尝试过将ResourceDictionary放置在不同的位置(在App.xaml中,在Window.Resources标签中...)

我已经尝试搜索了,但没有结果。以下是我的代码(它不起作用,但是在这里)

App.xaml:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary1.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

Dictionary1.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:local="clr-namespace:DataTemplateTesting" >

    <DataTemplate DataType="{x:Type local:MainViewModel}">
        <local:MainWindow/>
    </DataTemplate>

</ResourceDictionary>

MainViewModel.cs

namespace DataTemplateTesting
{
    public class MainViewModel
    {
        public MainViewModel() { }
    }
}

我所做的唯一一件事是为MainWindow添加了一个DataContextChanged事件处理程序,以便我可以看到它是否触发... 但事实并非如此。
有什么想法如何解决这个问题吗?
编辑: 虽然这里没有任何不能生成的内容,但这是MainWindow代码。
MainWindow.xaml
<Window x:Class="DataTemplateTesting.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DataTemplateTesting"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        DataContextChanged="Window_DataContextChanged"        >
    <Grid>

    </Grid>
</Window>

MainWindow.xaml.cs

namespace DataTemplateTesting
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            int i = 10; //This line exists solely to put a debug stop on.
        }
    }
}

你能添加 MainWindow 的代码吗? - Yusuf Tarık Günaydın
这里没有任何 MainWindow 的代码。我所做的唯一一件事就是添加了一个 DataContextChanged 的事件处理程序,以便我能够看到它。理想情况下,不应该有任何 MainWindow 的代码。 - Tom Padilla
有一个 MainWindow 控件,对吧?那么,一定有一些 xaml 代码来定义它。 - Yusuf Tarık Günaydın
给你们了...我猜是这样的。同样的自动生成代码加上一个事件处理程序。 - Tom Padilla
1个回答

9
<DataTemplate DataType="{x:Type local:MainViewModel}">
    <local:MainWindow/>
</DataTemplate>

首先,这个语句并不是指“创建一个MainWindow并将其DataContext设置为MainViewModel”。它实际上意味着每当您看到一个MainViewModel,只需将MainWindow放入可视树中。
其次,您不能将Window类添加为另一个Visual的子级。如果您尝试,您将会得到一个异常信息:“窗口必须是树的根。不能将窗口作为Visual的子级添加。”
正确的做法是这样的:
<Window x:Class="DataTemplateTesting.MainWindow"
    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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:DataTemplateTesting"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <ContentControl>
        <ContentControl.Content>
            <local:MainViewModel/>
        </ContentControl>
    </ContentControl>
</Grid>

在资源字典中定义这个:

<DataTemplate DataType="{x:Type local:MainViewModel}">
    <local:SomeUserControl/>
</DataTemplate>

并创建用户控件:

<UserControl x:Class="DataTemplateTesting.SomeUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <TextBox/>
    </Grid>
</UserControl>

如果您的内容动态变化或者您正在定义视图的ItemTemplate,那么这将会非常有用。否则,只需手动设置WindowDataContext即可。

<Window x:Class="DataTemplateTesting.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DataTemplateTesting"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <local:MainViewModel>
    </Window.DataContext>

    <Grid>

    </Grid>


经过一些实验,我意识到这种方法的酷炫之处。你可以将 <ContentControl Content="{Binding ????}"> 绑定到你的视图模型的属性上。有很多控制,而且在视图模型中没有视图“垃圾”。 - Tom Padilla
2
这个语句“首先,这个语句并不意味着‘创建一个MainWindow并将其DataContext设置为MainViewModel’。实际上,它的意思是每当你看到一个MainViewModel,就把MainWindow放在可视树中。”并不完全正确,因为它实际上会将DataContext设置为触发数据模板的MainViewModel实例。我无法找出它是在哪里完成的,但它确实完成了。 - ViRuSTriNiTy
1
酷炫,是的。我们应该这样做吗?不是的。您应该从另一个地方实例化您的视图模型,以便可以将其注入依赖项。如果您从XAML中实例化视图模型,则会遇到测试视图模型的问题,可能还有很多new()和调用静态项的问题,这些都应该避免。 - Alexandru Dicu

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