【发布时间】:2012-04-16 12:35:31
【问题描述】:
我有一个内容控件如下:
<ContentControl x:Name="grid1ContentControl" Content="{Binding MainGridViewModel}" />
MainGridViewModel 是 MainGridViewModelType 类型的属性。
我还有一个 DataTemplate 如下:
<DataTemplate DataType="{x:Type App:MainGridViewModelType}">...
我初始化(即设置)MainGridViewModel 属性并引发 NotifyPropertyChanged 事件。
我的期望是,此时应该通知框架我设置了 MainGridViewModel,并且作为 ContentControl 上的绑定的结果,将与 MainGridViewModel 属性类型(即 MainGridViewModelType)匹配的 DataTemplate 内容添加到可视化树在 ContentControl 所在的位置。
确实,我可以看到我的 RaisePropertyChanged() 方法在 MainGridViewModel 属性的设置器上运行。但是,当使用可视化树检查器检查可视化树时,ContentControl 的 ContentPresenter 不会在 MainGridViewModel 初始化后显示我的 DataTemplate 的内容。为什么?
注意,在再次设置 MainGridViewModel 以响应用户交互后,我得到了我预期的可视化树的更新。
我想出的唯一解决方法是给 DataTemplate 一个 x:Key,并显式设置 ContentControl 的 Content 值,不依赖于将类型与其 DataTemplate 匹配的框架,并应用给我的:
ContentControl grid1ContentControl = VisualElementFinder.FindDescendantByName(mwin, "grid1ContentControl") as ContentControl;
grid1ContentControl.SetValue(ContentControl.ContentTemplateProperty, mwin.FindResource("MainGridViewModelKey") as DataTemplate);
我一直遇到同样的问题。我对框架在视觉对象初始化后如何对绑定属性的后续分配做出反应的理解存在差距。我采纳了 Will 的建议并开发了一个原型,如下所述。我很感激任何进一步的想法。
这是一个关于这个主题的link to my blog entry。这是direct link to the prototype project。
这是为进一步讨论提供上下文的 MainWindow 代码。在原型项目中,NewTabControlViaContentControlCommand 有效,而 NewTabControlCommand 无效。
MainWindow.xaml:
<Window x:Class="DynamicTabControlSimpleProto.View.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:vm="clr-namespace:DynamicTabControlSimpleProto.ViewModel"
xmlns:vw="clr-namespace:DynamicTabControlSimpleProto.View"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
mc:Ignorable="d"
Height="300"
Width="500"
Title="Dynamic TabControl Prototype"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:TabControlViewModel}">
<vw:TabControlUserControl />
</DataTemplate>
<DataTemplate x:Key="tabControlDataTemplate">
<vw:TabControlUserControl />
</DataTemplate>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="4" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Name="stackPanel" Grid.Column="0">
<Button
Content="NewTabControl"
Command="{Binding NewTabControlCommand}"
CommandParameter="{Binding Path=DataContext}" />
<Button
Content="NewTabControlViaContentControl"
Command="{Binding NewTabControlViaContentControlCommand}"
CommandParameter="{Binding ElementName=tabControlContentControl}" />
</StackPanel>
<DockPanel
Name="dockPanel"
Grid.Column="2">
<Border
BorderBrush="Blue"
BorderThickness="5">
<ContentControl x:Name="tabControlContentControl" DataContext="{Binding TabControlViewModel, diag:PresentationTraceSources.TraceLevel=High}" />
</Border>
</DockPanel>
</Grid>
</Window>
MainWindowViewModel.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Windows.Controls;
using DynamicTabControlSimpleProto.View;
using System.Windows;
namespace DynamicTabControlSimpleProto.ViewModel
{
public class MainViewModel : ViewModelBase
{
public string Welcome
{
get
{
return "Welcome to MVVM Light";
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
NewTabControlCommand = new RelayCommand<object>(obj =>
{
this.NewTabControl(obj);
});
NewTabControlViaContentControlCommand =
new RelayCommand<ContentControl>(tabControlContentControl =>
{
this.NewTabControlViaContentControl(tabControlContentControl);
});
}
public TabControlViewModel TabControlViewModel
{
get
{
return _tabControlViewModel;
}
private set
{
_tabControlViewModel = value;
RaisePropertyChanged("TabControlViewModel");
}
}
public RelayCommand<object> NewTabControlCommand
{
get;
private set;
}
private TabControlViewModel _tabControlViewModel = null;
void NewTabControl(object obj)
{
TabControlViewModel = new TabControlViewModel();
}
public RelayCommand<ContentControl> NewTabControlViaContentControlCommand
{
get;
set;
}
void NewTabControlViaContentControl(ContentControl tabContentControl)
{
TabControlViewModel = new TabControlViewModel();
MainWindow mwin = Application.Current.MainWindow as MainWindow;
tabContentControl.SetValue(ContentControl.ContentTemplateProperty, mwin.FindResource("tabControlDataTemplate") as DataTemplate);
}
}
}
【问题讨论】:
-
为什么?可能是数百个原因之一。 MainGridViewModel 是在 ContentControl 的 DataContext 上设置的实例的属性吗?在构造函数中调用 InitializeComponent 之前,您是否正在执行所有这些操作?等等等等。您需要创建尽可能简单、可编译的原型来满足您的需求,并查看是否可以重现您的问题。然后在这里使用它(有问题的代码,请不要链接)来说明您的问题。但很可能您会自己解决问题。
-
@Will:是的,MainGridViewModel 是实例的属性,它是 ContentControl 的 DataContext。在调用 InitializeComponent 之后设置 MainGridViewModel。框架第一次没有“应用数据模板”; MainGridViewModel 的后续设置具有预期的效果。我将尝试创建一个简单的复制品;似乎我需要重新触发绑定,或者,也许添加一个新绑定以获取 ContentControl 以将类型链接的 DataTemplate 添加到其 ContentPresenter,就像直接设置 ContentControl.ContentTemplateProperty 一样。
-
你所说的让我相信它应该有效。已经做过很多次了。我认为您的原型会澄清您的问题。
-
@Will:嗨,Will,我采纳了你的建议并制作了一个原型。它真的很简单,并且说明了不符合我预期的技术以及确实有效的技术。我在我的原始帖子中包含了指向编译的 VS2010 项目的链接,并添加了代码和简短描述,以便为讨论提供上下文。感谢您提供的任何其他见解。谢谢。
-
酷。我明天去看看。
标签: wpf xaml data-binding mvvm