使用 DataTemplate 时更改内容会更改 DataContext
Changing content changes DataContext when using DataTemplate
X 问题。
我想暂时放大(让它占据全部可用space)一部分window内容。
Window 布局相当复杂:许多嵌套面板、拆分器、要放大的内容有 10 层深。将 Visibility
更改为拉伸内容是不够的(感谢拆分器)并且看起来非常复杂。
Y问题
我决定将该内容移动到用户控件中并执行类似(伪代码)
if(IsEnlarged)
{
oldContent = window.Content; // store
window.Content = newContent;
}
else
window.Content = oldContent; // restore
没问题。它在测试项目中运行完美......直到我开始使用数据模板。
问题:如果使用数据模板,那么一旦 window.Content = newContent
出现,newContent.DataContext
就会丢失并与 window.DataContext
相同。这将触发各种绑定错误,附加行为突然更改为默认值等。各种不好的东西。
问题:为什么 DataContext
会发生变化?如何prevent/fix这个问题?
这是一个重现(抱歉,不能再短了):
MainWindow.xaml 包含
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewModel}">
<local:UserControl1 />
</DataTemplate>
</Window.Resources>
<Grid Background="Gray">
<ContentControl Content="{Binding ViewModel}" />
</Grid>
MainWindow.cs 包含
public ViewModel ViewModel { get; } = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
UserControl1.xaml 包含
<Button Width="100"
Height="100"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Command="{Binding Command}" />
ViewModel(使用 DelegateCommand)
public class ViewModel
{
public DelegateCommand Command { get; set; }
bool _set;
object _content;
public ViewModel()
{
Command = new DelegateCommand(o =>
{
var button = (Button)o;
var window = Window.GetWindow(button);
_set = !_set;
if (_set)
{
_content = window.Content;
var a = button.DataContext; // a == ViewModel
window.Content = button;
var b = button.DataContext; // b == MainWindow ??? why???
}
else
window.Content = _content;
});
}
}
在var a = ...
上设置断点,启动程序,点击按钮,执行步骤并观察button.DataContext
以及Output
中的绑定错误window.
您一定是在尝试将 DataTemplate
用作 ContentTemplate
用于 ContentControl
。由于 ContentTemplate
对 Content
进行操作,因此它将使用 Content
作为其 DataContext
。您的 Content
包含 ViewModel
.
一旦您的 Button 不再是您的 DataTemplate
的一部分,那么它将使用 MainWindow's
DataContext
.
没有看到您的评论,我假设您希望 UserControl
的 DataContext
保持完整,即使您的 UserControl
不属于 DataTemplate
。
因此,Button
的简单集 DataContext
显式使用 XAML
使用 RelativeSource
。
例如,
<Button Content="{Binding Data}" DataContext="{Binding vm1, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" />
在代码中使用 DataContext
不是个好主意。
好的,这里有一些一般的想法。
如果您将对象 (viewmodel) 绑定到内容控件的内容 属性,则 wpf 使用 DataTemplate 来可视化该对象。如果您没有 DataTemplate,您只会看到 object.ToString()。 DataContext继承是指DataContext被继承给子元素。所以 real 用户控件将从父控件继承 DataContext。这些是您在创建 UserControl 时在 Whosebug 上发现的常见错误 - 它们通常会破坏 DataContext 继承并将 DataContext 设置为自身或新的 DataContext。
X 问题。
我想暂时放大(让它占据全部可用space)一部分window内容。
Window 布局相当复杂:许多嵌套面板、拆分器、要放大的内容有 10 层深。将 Visibility
更改为拉伸内容是不够的(感谢拆分器)并且看起来非常复杂。
Y问题
我决定将该内容移动到用户控件中并执行类似(伪代码)
if(IsEnlarged)
{
oldContent = window.Content; // store
window.Content = newContent;
}
else
window.Content = oldContent; // restore
没问题。它在测试项目中运行完美......直到我开始使用数据模板。
问题:如果使用数据模板,那么一旦 window.Content = newContent
出现,newContent.DataContext
就会丢失并与 window.DataContext
相同。这将触发各种绑定错误,附加行为突然更改为默认值等。各种不好的东西。
问题:为什么 DataContext
会发生变化?如何prevent/fix这个问题?
这是一个重现(抱歉,不能再短了):
MainWindow.xaml 包含
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewModel}">
<local:UserControl1 />
</DataTemplate>
</Window.Resources>
<Grid Background="Gray">
<ContentControl Content="{Binding ViewModel}" />
</Grid>
MainWindow.cs 包含
public ViewModel ViewModel { get; } = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
UserControl1.xaml 包含
<Button Width="100"
Height="100"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Command="{Binding Command}" />
ViewModel(使用 DelegateCommand)
public class ViewModel
{
public DelegateCommand Command { get; set; }
bool _set;
object _content;
public ViewModel()
{
Command = new DelegateCommand(o =>
{
var button = (Button)o;
var window = Window.GetWindow(button);
_set = !_set;
if (_set)
{
_content = window.Content;
var a = button.DataContext; // a == ViewModel
window.Content = button;
var b = button.DataContext; // b == MainWindow ??? why???
}
else
window.Content = _content;
});
}
}
在var a = ...
上设置断点,启动程序,点击按钮,执行步骤并观察button.DataContext
以及Output
中的绑定错误window.
您一定是在尝试将 DataTemplate
用作 ContentTemplate
用于 ContentControl
。由于 ContentTemplate
对 Content
进行操作,因此它将使用 Content
作为其 DataContext
。您的 Content
包含 ViewModel
.
一旦您的 Button 不再是您的 DataTemplate
的一部分,那么它将使用 MainWindow's
DataContext
.
没有看到您的评论,我假设您希望 UserControl
的 DataContext
保持完整,即使您的 UserControl
不属于 DataTemplate
。
因此,Button
的简单集 DataContext
显式使用 XAML
使用 RelativeSource
。
例如,
<Button Content="{Binding Data}" DataContext="{Binding vm1, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" />
在代码中使用 DataContext
不是个好主意。
好的,这里有一些一般的想法。
如果您将对象 (viewmodel) 绑定到内容控件的内容 属性,则 wpf 使用 DataTemplate 来可视化该对象。如果您没有 DataTemplate,您只会看到 object.ToString()。 DataContext继承是指DataContext被继承给子元素。所以 real 用户控件将从父控件继承 DataContext。这些是您在创建 UserControl 时在 Whosebug 上发现的常见错误 - 它们通常会破坏 DataContext 继承并将 DataContext 设置为自身或新的 DataContext。