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

时间:2015-09-15 07:36:06

标签: c# .net 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. SelectTemplate()中的item参数是System.Windows.Controls.ContentPresenter,而不是我添加的ViewModel对象。
  4. 即使我返回正确的DataTemplate,它也会被绑定到ContentPresenter,而不是ViewModel中包含的ContentPresenter
  5. 我设法在一个简单的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>
    

    MainViewModel:

    class MainViewModel
    {
        //Bound to DockingManager.DocumentsSource
        public ObservableCollection<object> Docs { get; private set; }
    
        public MainViewModel()
        {
            Docs = new ObservableCollection<object>();
            Docs.Add(new TestViewModel());
        }
    }
    

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

    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>
    

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

    结果:

    enter image description here

    TestView未正确绑定到TestViewModel,因此“{TESTTESTTEST”未显示在TextBox中。我已检出Avalon Dock's sample MVVM project,他们的DataTemplateSelector始终获取ViewModel而不是ContentPresenter。我做错了什么?

1 个答案:

答案 0 :(得分: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);
    }

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