这个让我难住了,因为我认为我看了所有的东西,但我一定遗漏了什么。我已经摆脱了 MSDN 杂志中的传统 MVVM 模式:
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
同时学习MVVM。然而,我通常会复制大部分代码,然后根据需要替换它,但今天我想从头开始构建一些东西,发现它可能比我想象的要多。当我使用资源字典但直接使用数据上下文时,MVVM 似乎无法使用绑定(bind)。这个问题最终想找其他开发者建议使用他们找到的绑定(bind)。
问题的总结是这样的:为什么资源字典中的“DataTemplate”似乎不起作用,如下所示,但直接的“DataContext”方法会立即对 View 进行绑定(bind)?
是不是因为我混合使用代码隐藏和代码隐藏设置 View 。或者可能是因为其他原因。如果我直接在 View 的 XAML 中设置“DataContext”,那么我的属性及其实现在 View 模型中设置正确,但为什么不在资源字典中设置呢?我认为这种方法的优点是你可以一次设置一堆关系。我很好奇是否需要进行其他设置才能使其正常工作。在他们使用数据模板的 MVVM 方法的主要示例中很好奇,但似乎设置它们比我正在做的要使它们的“绑定(bind)”起作用要多得多。
什么对我不起作用:
我试图在主窗口 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>
主窗口代码隐藏:
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);
}
}
}
资源字典:
<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>
DataBoundMVVMChart.xaml View :
<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 绑定(bind)到上面的 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));
}
}
}
好的,现在绑定(bind)将在 View 中失败,但颜色资源会进来。所以我想我做错了什么,但后面的代码会立即获得该属性。那么让我们继续:
什么起作用了:
神奇的是,如果我只是将这四行添加到 View 中:
将此添加到顶部“UserControl”段中的声明:
xmlns:local="clr-namespace:WPFTesting12_2.ViewModel"
然后设置对 UserControl 的 DataContext 的引用:
<UserControl.DataContext>
<local:DataBoundMVVMChartViewModel/>
</UserControl.DataContext>
最佳答案
在使用 WPF 时必须认识到有两层:数据层 (DataContext
) 和 UI 层(XAML)。
绑定(bind)用于将数据从数据层提取到 View 层。
当你写作时
<UserControl.DataContext>
<local:DataBoundMVVMChartViewModel/>
</UserControl.DataContext>
您告诉 WPF 它应该创建 DataBoundMVVMChartViewModel
的新实例并将其用于该 UserControl 的数据层。
当你写的时候
<Label Content="{Binding HelloString}" />
您要告诉 Label 在其数据层(DataContext
)中查找名为“HelloString”的属性,并将其用于 Content
。属性(property)。
如果数据层中不存在属性“HelloString”(除非您像设置 DataContext
那样设置 <UserControl.DataContext>
,否则它不存在),绑定(bind)将失败,除了绑定(bind)错误外,不会显示任何内容在你的输出窗口中。
你的 DataTemplate
来自你的 ResourceDictionary
与 DataContext
不同.
事实上,一个ResourceDictionary
就像它听起来的那样 - WPF 可以在需要时在应用程序中使用的资源字典。但是 Dictionary 中的对象在默认情况下并不是应用程序 UI 本身的一部分。相反,它们需要以某种方式被引用才能使用。
但是回到你的DataTemplate
<DataTemplate DataType="{x:Type vm:DataBoundMVVMChartViewModel}">
<vw:DataBoundMVVMChart/>
</DataTemplate>
WPF 使用数据模板来了解如何绘制特定类型的对象。在你的情况下,这个 DataTemplate
告诉 WPF 任何时候它需要绘制类型为 DataBoundMVVMChartViewModel
的对象, 它应该通过使用 DataBoundMVVMChart
来实现.
要将一个对象插入到 UI 中,一个 Content
通常使用属性,例如
MyLabel.Content = new DataBoundMVVMChartViewModel();
或
<ContentControl Content="{Binding SomeDataboundChartViewModelProperty}" />
我实际上是从完全相同的 article 开始学习 MVVM 的你链接了你的问题,但也很难弄清楚,这让我写了一些关于 WPF/MVVM 的博客,专门针对像我这样的初学者:)
如果您有兴趣,我有几篇关于 WPF/MVVM 的博客文章,可以帮助您更好地理解这项技术。
至于你的实际问题:
Preferred method for binding in MVVM, Data Template in Resources file or just DataContext in View itself?
我更喜欢使用 DataTemplate
在.Resources
某处,因为那时您的 UI 没有专门与一个特定的 DataContext
相关联.
这在使用 MVVM 创建可重用控件时尤为重要。例如,如果您有一个 CalculatorUserControl
, 你给它分配了一个 DataContext
在控件本身中,例如 <UserControl.DataContext>
, 那么你永远不能使用那个 CalculatorUserControl
与任何其他 DataContext
与创建控件时创建的控件不同。
所以通常我设置 DataContext
在启动时为整个应用程序使用一次,并使用 DataTemplates
告诉 WPF 如何绘制不同的 ViewModels
或 Models
在我的申请中。
我的整个应用程序都存在于数据层中,而 XAML 只是一个与数据层交互的用户友好界面。 (如果您想查看代码示例,请查看我的 Simple MVVM Example 帖子)
关于c# - 在 MVVM 中绑定(bind)的首选方法,资源文件中的数据模板或只是 View 本身中的 DataContext?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16446747/