在MVVM中绑定的首选方法,在资源文件中绑定数据模板还是在视图中绑定数据?

时间:2021-01-18 09:56:58

This one has got me stumped as I THOUGHT I looked at everything but I must be missing something. I have went off the traditional MVVM pattern from the MSDN magazine:

这件事让我很为难,因为我以为我什么都看了,但我一定是漏掉了什么。我已经脱离了MSDN杂志中传统的MVVM模式:

http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

while learning MVVM. However I usually copy most of the code and then replace it as I need to but today I wanted to build something from scratch and saw that there may be more to it than I thought. MVVM appeared to not work with bindings when I used the resource dictionary but does with datacontext directly. This question ultimately wants to find other developers suggested use of binding they find.

在学习MVVM。但是,我通常复制大部分代码,然后根据需要替换它,但是今天我想从头开始构建一些东西,并看到它可能比我想象的要多得多。当我使用资源字典时,MVVM似乎不使用绑定,而是直接使用datacontext。这个问题最终想要找到其他开发人员建议使用绑定。

Summary of question is this: Why does the 'DataTemplate' in the Resource Dictionary appear to not work shown below but a direct 'DataContext' method does with a view right away for bindings?

问题的总结是:为什么资源字典中的“DataTemplate”似乎不能工作如下所示,而直接的“DataContext”方法可以立即对视图进行绑定?

Is it because I am doing a mixture of code behind with settings views in the code behind. Or potentially because of something else. It appears my property and it's implementation are set correct in the viewmodel if I set the 'DataContext' directly in the View's XAML, but why not in the Resource Dictionary? I thought the advantage of that method was you could set a bunch of relationships all at once. I am curious if there is some other setting need to be done to get it to work. It is curious in the main example of the MVVM method they use Data Templates yet it appears there is more to setting them up than I am doing to get their 'bindings' to work.

是因为我在后面做的代码与后面代码中的设置视图的混合吗?或者可能是因为别的原因。如果我直接在视图的XAML中设置“DataContext”,那么它就会显示为我的属性,它的实现在viewmodel中是正确的,但是为什么不在资源字典中呢?我认为这个方法的优点是你可以同时建立一系列的关系。我很好奇,是否需要做一些其他的设置来让它工作。在MVVM方法的主要示例中,他们使用了数据模板,这很奇怪,但似乎设置它们的方法比我在让它们的“绑定”工作时做的更多。

WHAT DID NOT WORK FOR ME:

什么对我不起作用:

I attempted to do some very basic stuff in a main window xaml, leaving some of my code out to make it even simpler:

我尝试在一个主窗口xaml中做一些非常基本的事情,留下一些代码使它更简单:

Main Window XAML:

主窗口XAML:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dxc="http://schemas.devexpress.com/winfx/2008/xaml/charts" x:Class="WPFTesting12_2.MainWindow"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <ResourceDictionary Source="Resources.xaml"/>
    </Window.Resources>
    <Grid>
        <DockPanel x:Name="dockpanel">
            <Menu DockPanel.Dock="Top" Height="30">
                <MenuItem Header="Charting">
                    <MenuItem Header="MVVMDataBound" x:Name="mnuDataBoundSeriesMVVMCharting" Click="mnuDataBoundSeriesMVVMCharting_OnClick"/>
                </MenuItem>
            </Menu>
            <TextBlock Height="5" Background="Black" DockPanel.Dock="Top" />
            <DockPanel x:Name="dockchildren" DockPanel.Dock="Bottom"/>
        </DockPanel>
    </Grid>
</Window>

Main Window Code Behind:

主窗口的代码:

public partial class MainWindow : Window
    {

        public MainWindow()
        {
            InitializeComponent();

            this.WindowState = WindowState.Maximized;
        }


        private void mnuDataBoundSeriesMVVMCharting_OnClick(object sender, RoutedEventArgs e)
        {
            View.DataBoundMVVMChart c = new DataBoundMVVMChart();

            dockchildren.Children.Clear();
            dockchildren.Children.Add(c);
        }
    }
}

Resource Dictionary:

资源字典:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:vw="clr-namespace:WPFTesting12_2.View"
                    xmlns:vm="clr-namespace:WPFTesting12_2.ViewModel"
                    >
  <DataTemplate DataType="{x:Type vm:DataBoundMVVMChartViewModel}">
    <vw:DataBoundMVVMChart/>
 </DataTemplate>

<Style TargetType="MenuItem">
        <Setter Property="Background" Value="Wheat"/>
    </Style>
    <Style TargetType="Menu">
        <Setter Property="Background" Value="Wheat"/>
    </Style>

</ResourceDictionary>

View for DataBoundMVVMChart.xaml:

DataBoundMVVMChart.xaml视图:

<UserControl x:Class="WPFTesting12_2.View.DataBoundMVVMChart"
             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" 
             xmlns:local="clr-namespace:WPFTesting12_2.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <ResourceDictionary Source="..\Resources.xaml"/>
    </UserControl.Resources>
    <Grid>
        <DockPanel>
            <Menu DockPanel.Dock="Top">
                <MenuItem Header="TesterContent"/>
            </Menu>
            <Label DockPanel.Dock="Bottom" Width="300" x:Name="label" Height="50" Foreground="Blue" FontSize="24"  Content="{Binding Path=HelloString}"/>
        </DockPanel>
    </Grid>
</UserControl>

ViewModel to bind to the View above:

将ViewModel绑定到上述视图:

namespace WPFTesting12_2.ViewModel
{
    class DataBoundMVVMChartViewModel : INotifyPropertyChanged
    {
        private string _HelloString;

        public string HelloString
        {
            get { return _HelloString; }
            set 
            {
                _HelloString = value;
                RaisePropertyChanged("HelloString");
            }
        }

        public DataBoundMVVMChartViewModel()
        {
            HelloString = "Hello there from the ViewModel";
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
}

Okay now the binding will fail in the view but yet the resources of the color will come in. So I was thinking I did something wrong but code behind will get the property right away. So let's move on:

现在,绑定会在视图中失败,但是颜色的资源会进来。所以我在想我做错了什么但是后面的代码会马上得到这个属性。所以让我们继续:

WHAT DID WORK:

什么工作:

Magicially if I just add these four lines to the view:

如果我把这四行加到视图中

Add this to the declarations in the 'UserControl' segment at top:

将此添加到顶部的“UserControl”部分的声明中:

xmlns:local="clr-namespace:WPFTesting12_2.ViewModel"

Then set a reference to the UserControl's DataContext:

然后设置对UserControl的DataContext的引用:

<UserControl.DataContext>
        <local:DataBoundMVVMChartViewModel/>
    </UserControl.DataContext>

2 个解决方案

#1


27  

Its important to realize when working with WPF that there are two layers: the data layer (DataContext) and the UI layer (the XAML).

在使用WPF时,要认识到有两个层:数据层(DataContext)和UI层(XAML)。

Bindings are used to pull data from the data layer into the View layer.

绑定用于将数据从数据层拉到视图层。

When you write

当你写

<UserControl.DataContext>
    <local:DataBoundMVVMChartViewModel/>
</UserControl.DataContext>

You are telling WPF that it should create a new instance of DataBoundMVVMChartViewModel and use it for the data layer of that UserControl.

您正在告诉WPF,它应该创建DataBoundMVVMChartViewModel的一个新实例,并将其用于该UserControl的数据层。

When you write

当你写

<Label Content="{Binding HelloString}" />

you are telling the Label to look in its data layer (the DataContext) for a property called "HelloString", and use it for the Content property.

您正在告诉标签在其数据层(DataContext)中查找一个名为“HelloString”的属性,并将其用于内容属性。

If the property "HelloString" does not exist in the data layer (which it does not unless you set the DataContext like you did with <UserControl.DataContext>), the binding will fail and nothing gets displayed except for a binding error in your output window.

如果数据层中不存在属性“HelloString”(除非您像使用 那样设置DataContext),那么绑定将失败,除了输出窗口中的绑定错误外,不会显示任何内容。

Your DataTemplate from your ResourceDictionary is something different from the DataContext.

来自ResourceDictionary的DataTemplate与DataContext不同。

In fact, a ResourceDictionary is just what it sounds like - a dictionary of resources that WPF can use in the application when needed. But objects in the Dictionary are not by default part of the application's UI itself. Instead, they need to be referenced in some way to be used.

实际上,ResourceDictionary就是它听起来的样子——WPF在需要时可以在应用程序中使用的资源字典。但是字典中的对象并不是应用程序UI本身的默认部分。相反,需要以某种方式引用它们。

But back to your DataTemplate

但是回到你的数据模板

<DataTemplate DataType="{x:Type vm:DataBoundMVVMChartViewModel}">
    <vw:DataBoundMVVMChart/>
</DataTemplate>

WPF uses DataTemplates to know how to draw specific types of objects. In your case, this DataTemplate is telling WPF that anytime it needs to draw an object of type DataBoundMVVMChartViewModel, it should do so by using a DataBoundMVVMChart.

WPF使用数据处理板来了解如何绘制特定类型的对象。在您的例子中,这个DataTemplate告诉WPF,每当需要绘制DataBoundMVVMChartViewModel类型的对象时,它都应该使用DataBoundMVVMChart。

To insert an object into the UI, a Content property is normally used, such as

要将对象插入到UI中,通常使用内容属性,例如。

MyLabel.Content = new DataBoundMVVMChartViewModel();

or

<ContentControl Content="{Binding SomeDataboundChartViewModelProperty}" />

I actually started out learning MVVM with the exact same article you linked in your question, and had a lot of trouble figuring it out as well, which lead me to doing a little blogging about WPF/MVVM aimed specifically for beginners like me :)

实际上,我开始学习MVVM的时候,和你在你的问题中链接的一篇文章一模一样,我也很难搞清楚,这让我开始写一些关于WPF/MVVM的博客,专门针对像我这样的初学者:)

If you're interested, I have a few blog articles about WPF/MVVM that may help you understand the technology better.

如果您感兴趣,我有一些关于WPF/MVVM的博客文章,可以帮助您更好地理解该技术。

As for your actual question:

关于你的实际问题:

Preferred method for binding in MVVM, Data Template in Resources file or just DataContext in View itself?

在MVVM中绑定的首选方法,在资源文件中绑定数据模板还是在视图中绑定数据?

I prefer using a DataTemplate in the .Resources somewhere, as then your UI is not specifically tied with one specific DataContext.

我更喜欢在. resources中的某处使用DataTemplate,因为这样您的UI就不会与特定的DataContext绑定。

This is especially important when creating re-usable controls with MVVM. For example, if you have a CalculatorUserControl, and you assigned it a DataContext in the control itself such as with <UserControl.DataContext>, then you could never use that CalculatorUserControl with any other DataContext other than the one that is created when the control is created.

在使用MVVM创建可重用控件时,这一点尤为重要。例如,如果您有一个CalculatorUserControl,并且在控件本身中为它分配了一个DataContext,例如with ,那么您永远不能将CalculatorUserControl与任何其他DataContext(创建控件时创建的文本)一起使用。 。datacontext>

So typically I set the DataContext for the entire application once at startup, and use DataTemplates to tell WPF how to draw the different ViewModels or Models in my application.

因此,我通常在启动时为整个应用程序设置DataContext,并使用datatemplate告诉WPF如何在应用程序中绘制不同的视图模型或模型。

My entire application exists in the data layer, and the XAML is merely a user-friendly interface to interact with the data layer. (If you want to see a code sample, check out my Simple MVVM Example post)

我的整个应用程序都存在于数据层中,而XAML只是一个与数据层交互的用户友好的界面。(如果您想查看代码示例,请查看我简单的MVVM示例文章)

#2


2  

If you want to use implicit DataTemplates you have to use the view-model-first approach, you however add a view to your DockPanel without a view-model as its context. If you simply add the respective view-model (to a ContentControl or ItemsControl), the view will be created automatically from the DataTemplate with the view-model as its DataContext.

如果您想使用隐式的DataTemplates,您必须使用视图-模型-第一方法,但是您需要向DockPanel添加一个视图,而不使用视图-模型作为其上下文。如果您只是将各自的视图模型(添加到ContentControl或ItemsControl中),那么将从DataTemplate中自动创建视图,视图模型作为其DataContext。

e.g.

如。

<ItemsControl Name="ic"/>

ic.Items.Add(new DataBoundMVVMChartViewModel());

I personally prefer this over view-first (e.g. adding a view which then assigns its own DataContext), because you usually want references to the view-models, the view is only secondary and manipulated indirectly by interacting with the view-model.

我个人更喜欢这一点(例如添加一个视图,然后指定它自己的DataContext),因为您通常需要对视图模型的引用,视图只是次要的,并且通过与视图模型交互来间接操作。

#1


27  

Its important to realize when working with WPF that there are two layers: the data layer (DataContext) and the UI layer (the XAML).

在使用WPF时,要认识到有两个层:数据层(DataContext)和UI层(XAML)。

Bindings are used to pull data from the data layer into the View layer.

绑定用于将数据从数据层拉到视图层。

When you write

当你写

<UserControl.DataContext>
    <local:DataBoundMVVMChartViewModel/>
</UserControl.DataContext>

You are telling WPF that it should create a new instance of DataBoundMVVMChartViewModel and use it for the data layer of that UserControl.

您正在告诉WPF,它应该创建DataBoundMVVMChartViewModel的一个新实例,并将其用于该UserControl的数据层。

When you write

当你写

<Label Content="{Binding HelloString}" />

you are telling the Label to look in its data layer (the DataContext) for a property called "HelloString", and use it for the Content property.

您正在告诉标签在其数据层(DataContext)中查找一个名为“HelloString”的属性,并将其用于内容属性。

If the property "HelloString" does not exist in the data layer (which it does not unless you set the DataContext like you did with <UserControl.DataContext>), the binding will fail and nothing gets displayed except for a binding error in your output window.

如果数据层中不存在属性“HelloString”(除非您像使用 那样设置DataContext),那么绑定将失败,除了输出窗口中的绑定错误外,不会显示任何内容。

Your DataTemplate from your ResourceDictionary is something different from the DataContext.

来自ResourceDictionary的DataTemplate与DataContext不同。

In fact, a ResourceDictionary is just what it sounds like - a dictionary of resources that WPF can use in the application when needed. But objects in the Dictionary are not by default part of the application's UI itself. Instead, they need to be referenced in some way to be used.

实际上,ResourceDictionary就是它听起来的样子——WPF在需要时可以在应用程序中使用的资源字典。但是字典中的对象并不是应用程序UI本身的默认部分。相反,需要以某种方式引用它们。

But back to your DataTemplate

但是回到你的数据模板

<DataTemplate DataType="{x:Type vm:DataBoundMVVMChartViewModel}">
    <vw:DataBoundMVVMChart/>
</DataTemplate>

WPF uses DataTemplates to know how to draw specific types of objects. In your case, this DataTemplate is telling WPF that anytime it needs to draw an object of type DataBoundMVVMChartViewModel, it should do so by using a DataBoundMVVMChart.

WPF使用数据处理板来了解如何绘制特定类型的对象。在您的例子中,这个DataTemplate告诉WPF,每当需要绘制DataBoundMVVMChartViewModel类型的对象时,它都应该使用DataBoundMVVMChart。

To insert an object into the UI, a Content property is normally used, such as

要将对象插入到UI中,通常使用内容属性,例如。

MyLabel.Content = new DataBoundMVVMChartViewModel();

or

<ContentControl Content="{Binding SomeDataboundChartViewModelProperty}" />

I actually started out learning MVVM with the exact same article you linked in your question, and had a lot of trouble figuring it out as well, which lead me to doing a little blogging about WPF/MVVM aimed specifically for beginners like me :)

实际上,我开始学习MVVM的时候,和你在你的问题中链接的一篇文章一模一样,我也很难搞清楚,这让我开始写一些关于WPF/MVVM的博客,专门针对像我这样的初学者:)

If you're interested, I have a few blog articles about WPF/MVVM that may help you understand the technology better.

如果您感兴趣,我有一些关于WPF/MVVM的博客文章,可以帮助您更好地理解该技术。

As for your actual question:

关于你的实际问题:

Preferred method for binding in MVVM, Data Template in Resources file or just DataContext in View itself?

在MVVM中绑定的首选方法,在资源文件中绑定数据模板还是在视图中绑定数据?

I prefer using a DataTemplate in the .Resources somewhere, as then your UI is not specifically tied with one specific DataContext.

我更喜欢在. resources中的某处使用DataTemplate,因为这样您的UI就不会与特定的DataContext绑定。

This is especially important when creating re-usable controls with MVVM. For example, if you have a CalculatorUserControl, and you assigned it a DataContext in the control itself such as with <UserControl.DataContext>, then you could never use that CalculatorUserControl with any other DataContext other than the one that is created when the control is created.

在使用MVVM创建可重用控件时,这一点尤为重要。例如,如果您有一个CalculatorUserControl,并且在控件本身中为它分配了一个DataContext,例如with ,那么您永远不能将CalculatorUserControl与任何其他DataContext(创建控件时创建的文本)一起使用。 。datacontext>

So typically I set the DataContext for the entire application once at startup, and use DataTemplates to tell WPF how to draw the different ViewModels or Models in my application.

因此,我通常在启动时为整个应用程序设置DataContext,并使用datatemplate告诉WPF如何在应用程序中绘制不同的视图模型或模型。

My entire application exists in the data layer, and the XAML is merely a user-friendly interface to interact with the data layer. (If you want to see a code sample, check out my Simple MVVM Example post)

我的整个应用程序都存在于数据层中,而XAML只是一个与数据层交互的用户友好的界面。(如果您想查看代码示例,请查看我简单的MVVM示例文章)

#2


2  

If you want to use implicit DataTemplates you have to use the view-model-first approach, you however add a view to your DockPanel without a view-model as its context. If you simply add the respective view-model (to a ContentControl or ItemsControl), the view will be created automatically from the DataTemplate with the view-model as its DataContext.

如果您想使用隐式的DataTemplates,您必须使用视图-模型-第一方法,但是您需要向DockPanel添加一个视图,而不使用视图-模型作为其上下文。如果您只是将各自的视图模型(添加到ContentControl或ItemsControl中),那么将从DataTemplate中自动创建视图,视图模型作为其DataContext。

e.g.

如。

<ItemsControl Name="ic"/>

ic.Items.Add(new DataBoundMVVMChartViewModel());

I personally prefer this over view-first (e.g. adding a view which then assigns its own DataContext), because you usually want references to the view-models, the view is only secondary and manipulated indirectly by interacting with the view-model.

我个人更喜欢这一点(例如添加一个视图,然后指定它自己的DataContext),因为您通常需要对视图模型的引用,视图只是次要的,并且通过与视图模型交互来间接操作。