Avalon Dock 2.0 LayoutItemTemplateSelector 给出了 ContentPresenter 而不是 ViewModel

Avalon Dock 2.0 LayoutItemTemplateSelector given ContentPresenter instead of ViewModel

我已经在这几个星期了...我正在创建一个 WPF 应用程序,它在 Main Window 中使用 Avalon Dock 2.0。我正在尝试以 MVVM 方式使用对接管理器,因此我在 MainViewModel 中将 DockingManager.DocumentsSource 绑定到 ObservableCollection<object> 属性。我还创建了一个自定义 DataTemplateSelector 并将其绑定到 DockingManager.LayoutItemTemplateSelector。我遇到的问题:

  1. 我在文档源中添加了一个ViewModel
  2. 调用了我的自定义 DataTemplateSelector.SelectTemplate()
  3. SelectTemplate() 中的项目参数是 System.Windows.Controls.ContentPresenter 而不是我添加的 ViewModel 对象。
  4. 即使我 return 是正确的 DataTemplate,它最终会绑定到 ContentPresenter,而不是 ContentPresenter 中包含的 ViewModel

我设法在一个基本的 WPF 项目中复制了这个问题,这里是相关代码:

主要Window:

<!-- 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>

主视图模型:

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

    public MainViewModel()
    {
        Docs = new ObservableCollection<object>();
        Docs.Add(new TestViewModel());
    }
}

数据模板选择器:

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;
    }
}

测试视图:

<!-- 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>

测试视图模型:

//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");
        }
    }
}

结果:

TestView 没有正确绑定到 TestViewModel,因此 "TESTTESTTEST" 没有出现在 TextBox 中。我查看了 Avalon Dock's sample MVVM project,他们的 DataTemplateSelector 总是得到 ViewModel 而不是 ContentPresenter。我做错了什么?

更改 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);
    }

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