给定ContentPresenter而不是ViewModel的Avalon Dock 2.0 LayoutItemTemplateSelector

Dav*_*veS 4 .net c# wpf mvvm avalondock

我已经在这几周了...我正在创建一个在主窗口中使用Avalon Dock 2.0的WPF应用程序.我试图以MVVM的方式使用Docking Manager,所以我已经DockingManager.DocumentsSource绑定了ObservableCollection<object>我的一个属性MainViewModel.我还创建了一个自定义DataTemplateSelector并绑定它DockingManager.LayoutItemTemplateSelector.我遇到的问题:

  1. 我添加了一个ViewModel文件来源.
  2. 我的习惯DataTemplateSelector.SelectTemplate()被称为.
  3. item参数in SelectTemplate()是一个System.Windows.Controls.ContentPresenter而不是ViewModel我添加的对象.
  4. 即使我返回正确的DataTemplate,它最终也会被绑定到ContentPresenter而不是ViewModel包含在内ContentPresenter.

我设法在一个简单的WPF项目中复制问题,这是相关的代码:

主窗口:

<!-- MainWindow markup DataContext is bound to
      I omitted the usual xmlns declarations -->
<Window 
        xmlns:xcad="http://schemas.xceed.com/wpf/xaml/avalondock"
        xmlns:local="clr-namespace:AvalonTest"
        Title="MainWindow">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <xcad:DockingManager DocumentsSource="{Binding Docs}">
            <xcad:DockingManager.LayoutItemTemplateSelector>
                <local:TestTemplateSelector>
                    <local:TestTemplateSelector.TheTemplate>
                        <DataTemplate>
                            <local:TestView/>
                        </DataTemplate>
                    </local:TestTemplateSelector.TheTemplate>
                </local:TestTemplateSelector>
            </xcad:DockingManager.LayoutItemTemplateSelector>

            <xcad:LayoutRoot>
                <xcad:LayoutPanel Orientation="Vertical">
                    <xcad:LayoutAnchorablePane/>
                    <xcad:LayoutDocumentPane/>
                </xcad:LayoutPanel>
            </xcad:LayoutRoot>
        </xcad:DockingManager>
    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)

MainViewModel:

class MainViewModel
{
    //Bound to DockingManager.DocumentsSource
    public ObservableCollection<object> Docs { get; private set; }

    public MainViewModel()
    {
        Docs = new ObservableCollection<object>();
        Docs.Add(new TestViewModel());
    }
}
Run Code Online (Sandbox Code Playgroud)

DataTemplateSelector:

class TestTemplateSelector : DataTemplateSelector
{
    public TestTemplateSelector() {}

    public DataTemplate TheTemplate { get; set; }

    //When this method is called, item is always a ContentPresenter
    //ContentPresenter.Content will contain the ViewModel I add
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        //Just return the only template no matter what
        return TheTemplate;
    }
}
Run Code Online (Sandbox Code Playgroud)

TestView:

<!-- TestTemplateSelector will always return this TestView -->
<UserControl x:Class="AvalonTest.TestView"
             xmlns:local="clr-namespace:AvalonTest">
    <Grid>
        <StackPanel Orientation="Vertical">
            <TextBox Text="{Binding TestText}"/>
            <Button Content="A Button"/>
        </StackPanel>
    </Grid>
</UserControl>
Run Code Online (Sandbox Code Playgroud)

TestViewModel:

//TestView.DataContext should be set to this, but instead
//it gets set to a containing ContentPresenter
class TestViewModel : ObservableObject
{
    private string testText = "TESTTESTTEST";
    public string TestText
    {
        get { return testText; }
        set
        {
            testText = value;
            RaisePropertyChanged("TestText");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

在此输入图像描述

TestView没有正确绑定TestViewModel,因此"TESTTESTTEST"没有出现在TextBox.我已经检查了Avalon Dock的示例MVVM项目,他们DataTemplateSelector总是得到ViewModel而不是ContentPresenter.我究竟做错了什么?

Kar*_*ayo 5

在TestTemplateSelector上更改SelectTemplate的定义,如下所示:

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        //check if the item is an instance of TestViewModel
        if (item is TestViewModel)
            return TheTemplate;

        //delegate the call to base class
        return base.SelectTemplate(item, container);
    }
Run Code Online (Sandbox Code Playgroud)

您应该始终检查传递的项是否是目标视图模型的实例,如果不是,则将调用委托给基类,以便WPF可以处理您不关心的对象.

  • @KarelTamayo,刚试了这个,确实有效.我调试了项目,SelectTemplate的调用确实以你描述它的方式发生.你能在答案中加上这个解释吗?此外,如果您有关于为什么会发生这种情况的参考,那也很棒,谢谢! (2认同)