[英]WPF Binding and Resource Lookup Complexities
我有一个内容控件,如下所示:
<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属性的设置器上运行。 但是,在使用可视树检查器检查可视树时,在初始化MainGridViewModel之后,ContentControl的ContentPresenter不会显示我的DataTemplate的内容。 为什么?
注意,在响应用户交互而再次设置MainGridViewModel之后,我得到了对期望的可视化树的更新。
我想出的唯一解决方法是给DataTemplate ax:Key,并显式设置ContentControl的Content值,而不是依赖于将类型与其DataTemplate匹配的框架并将其应用于我:
ContentControl grid1ContentControl = VisualElementFinder.FindDescendantByName(mwin, "grid1ContentControl") as ContentControl;
grid1ContentControl.SetValue(ContentControl.ContentTemplateProperty, mwin.FindResource("MainGridViewModelKey") as DataTemplate);
我一直遇到同样的问题。 在视觉初始化之后,我对框架如何期望对绑定属性的后续分配有一个理解上的差距。 我接受了Will的建议并开发了一个原型,如下所述。 我感谢任何进一步的想法。
这是我有关该主题的博客条目的链接 。 这是原型项目的直接链接 。
这是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);
}
}
}
我相信我发现了上面无法使用的技术中的错误。 我通过尝试像在代码中一样直接在XAML中设置DataContext来找到它,即直接设置为ViewModel。 当我尝试此操作时,它也不起作用。
例如,将我的MainWindow的DataContext设置为ViewModel不起作用:
<Window DataContext="vm:MainViewModel" ...>
但是,将我的Window的DataContext设置为在更高级别组件的资源中声明的ViewModel(在我的情况下为App.xaml)可以使一切正常运行:
在App.xaml中:
<Application.Resources>
<vm:MainViewModel x:Key="MainViewModel" />
</Application.Resources>
然后,在MainWindow.xaml中:
<Window DataContext="{Binding Source={StaticResource MainViewModel}}" ...>
我认为上面示例中原始代码中的操作类似于将DataContext直接设置为ViewModel,因为即使ContentControl正在创建Binding,我认为必要的元素也必须是ViewModel,这是一种方法或其他,需要添加为资源。 只有这样,才能将ContentControl上的DataContext Binding设置为资源,然后框架将提供ViewModel类型到DataTemplate可视树元素的预期映射。
以上所有信息对于ContentControls很有用。 特别是,我最初提出的问题中的代码解决方案可能是实现所描述内容的最佳且唯一的方法。 但是,对于ItemsControl而言,情况有所不同。 冒着使这篇文章分歧太大的风险,我将这些其他见解包括在我博客的一篇文章中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.