MVVM中绑定的首选方法,Resources文件中的数据模板或View中的DataContext本身?

dja*_*azz 9 c# data-binding wpf xaml mvvm

这个让我难过,因为我想到我看了一切但我必须遗漏一些东西.我已经从MSDN杂志中删除了传统的MVVM模式:

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

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

问题摘要是这样的:为什么资源字典中的'DataTemplate'看起来不能在下面显示,但直接'DataContext'方法会立即查看绑定视图?

是因为我在后面的代码中使用设置视图混合了代码.或者可能是因为别的东西.如果我直接在View的XAML中设置'DataContext',那么看起来我的属性和它的实现在viewmodel中设置正确,但为什么不在资源字典中呢?我认为这种方法的优点是你可以同时设置一堆关系.我很好奇是否需要进行其他设置才能使其正常工作.他们使用数据模板的MVVM方法的主要示例很奇怪,但似乎设置它们比我正在做的更多,以使它们的"绑定"起作用.

什么不适合我:

我试图在主窗口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>
Run Code Online (Sandbox Code Playgroud)

主窗口代码背后:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

资源词典:

<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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

ViewModel绑定到上面的View:

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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

好了,现在绑定将在视图中失败,但是颜色的资源会进来.所以我认为我做错了什么,但后面的代码将立即获得该属性.让我们继续:

什么工作:

如果我只是将这四行添加到视图中,那就太神奇了:

将其添加到顶部"UserControl"段中的声明:

xmlns:local="clr-namespace:WPFTesting12_2.ViewModel"
Run Code Online (Sandbox Code Playgroud)

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

<UserControl.DataContext>
        <local:DataBoundMVVMChartViewModel/>
    </UserControl.DataContext>
Run Code Online (Sandbox Code Playgroud)

Rac*_*hel 29

在使用WPF时,重要的是要有两层:数据层(DataContext)和UI层(XAML).

绑定用于将数据从数据层提取到View层.

当你写作

<UserControl.DataContext>
    <local:DataBoundMVVMChartViewModel/>
</UserControl.DataContext>
Run Code Online (Sandbox Code Playgroud)

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

当你写作

<Label Content="{Binding HelloString}" />
Run Code Online (Sandbox Code Playgroud)

您告诉Label在其数据层(DataContext)中查找名为"HelloString"的属性,并将其用于该Content属性.

如果属性"HelloString"并不在数据层中存在(它不会,除非你设置DataContext像你这样用<UserControl.DataContext>),绑定操作将失败,并没有显示,除了在输出窗口绑定错误得到.

你的DataTemplate与你ResourceDictionary的不同DataContext.

实际上,ResourceDictionary它就像听起来一样 - WPF可以在需要时在应用程序中使用的资源字典.但是,Dictionary中的对象默认情况下不是应用程序UI本身的一部分.相反,它们需要以某种方式被引用才能使用.

但回到你的身上 DataTemplate

<DataTemplate DataType="{x:Type vm:DataBoundMVVMChartViewModel}">
    <vw:DataBoundMVVMChart/>
</DataTemplate>
Run Code Online (Sandbox Code Playgroud)

WPF使用DataTemplates了解如何绘制特定类型的对象.在你的情况下,这DataTemplate告诉WPF,只要它需要绘制一个类型的对象DataBoundMVVMChartViewModel,它应该通过使用一个DataBoundMVVMChart.

要将对象插入UI,Content通常使用属性,例如

MyLabel.Content = new DataBoundMVVMChartViewModel();
Run Code Online (Sandbox Code Playgroud)

要么

<ContentControl Content="{Binding SomeDataboundChartViewModelProperty}" />
Run Code Online (Sandbox Code Playgroud)

我实际上开始用你在问题中链接的完全相同的文章学习MVVM ,并且在弄清楚它时遇到了很多麻烦,这让我做了一些关于WPF/MVVM的博客,专门针对像我这样的初学者:)

如果你有兴趣,我有一些关于WPF/MVVM的博客文章可以帮助你更好地理解这项技术.

至于你的实际问题:

MVVM中绑定的首选方法,Resources文件中的数据模板或View中的DataContext本身?

我更喜欢DataTemplate.Resources某个地方使用a ,因为那时你的UI并没有特别地与一个特定的绑定DataContext.

在使用MVVM创建可重用控件时,这一点尤其重要.举例来说,如果你有一个CalculatorUserControl,你赋予它DataContext本身,如与控制<UserControl.DataContext>,那么你永远无法使用CalculatorUserControl任何其他DataContext比创建控件时创建的一个其他.

因此,我通常DataContext在启动时为整个应用程序设置一次,并用于DataTemplates告诉WPF如何绘制不同的ViewModelsModels在我的应用程序中.

我的整个应用程序都存在于数据层中,而XAML只是一个与数据层交互的用户友好界面.(如果你想看一个代码示例,请查看我的Simple MVVM示例帖子)