使用DataTemplate时,更改内容会更改DataContext

时间:2016-02-25 14:14:06

标签: wpf mvvm datacontext

X问题。

我想暂时放大(让它占用整个可用空间)窗口内容的一部分。

窗口布局非常复杂:许多嵌套面板,分割器,要放大的内容都是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正在改变?如何预防/解决此问题?

这是一个复制品(对不起,不能再短了):

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以及绑定错误。

2 个答案:

答案 0 :(得分:1)

您必须尝试将DataTemplate ContentTemplate用作ContentControl。由于ContentTemplateContent上运行,因此它会使用Content作为其DataContext。而您的Content包含ViewModel

一旦您的按钮不再是DataTemplate的一部分,它就会使用MainWindow's DataContext

没有看到您的评论,我假设您希望DataContext UserControl保持不变,即使您的UserControl不属于DataTemplate

因此,使用DataContext明确使用Button XAML RelativeSource的简单集<Button Content="{Binding Data}" DataContext="{Binding vm1, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" />

示例,

DataContext

在代码中使用foreach ($arr1[0] as $key => $entry) { $arr1[0][$key][$arr1[0][$key]["Str1"]] = $arr2[0][$entry["Str1"]]; } 并不是一个好主意。

答案 1 :(得分:1)

这里有一些一般的想法。

如果将对象(viewmodel)绑定到contentcontrol的Content属性,则wpf使用DataTemplate来可视化对象。如果您没有DataTemplate,您只需看到object.ToString()。 DataContext继承意味着DataContext继承到子元素。所以真正的用户控件将从父级继承DataContext。这是您在创建UserControl时在stackoverflow上找到的常见错误 - 它们通常会破坏DataContext继承并将DataContext设置为self或新的DataContext。

UserControl DataContext Binding