Avalon Dock 2.0 LayoutItemTemplateSelector given ContentPresenter instead of ViewModel

試著忘記壹切 提交于 2019-11-30 17:48:07

问题


I've been at this for weeks...I am creating a WPF application that uses Avalon Dock 2.0 in the the Main Window. I am trying to use the Docking Manager in a MVVM way, so I have DockingManager.DocumentsSource bound to an ObservableCollection<object> property in my MainViewModel. I also created a custom DataTemplateSelector and bound it to DockingManager.LayoutItemTemplateSelector. The problem I am having:

  1. I add a ViewModel to the documents source.
  2. My custom DataTemplateSelector.SelectTemplate() is called.
  3. The item parameter in SelectTemplate() is a System.Windows.Controls.ContentPresenter instead of the ViewModel object that I added.
  4. Even if I return the correct DataTemplate, it ends up getting bound to the ContentPresenter instead of the ViewModel contained within the ContentPresenter.

I managed to replicate the problem in a bare-bones WPF project, here is the relevant code:

MainWindow:

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

The Result:

TestView is not properly bound to the TestViewModel and therefore "TESTTESTTEST" does not show up in the TextBox. I have checked out Avalon Dock's sample MVVM project and their DataTemplateSelector always gets the ViewModel instead of ContentPresenter. What am I doing wrong?


回答1:


Change the definition for SelectTemplate on TestTemplateSelector as follows:

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

You should always check if the item passed is an instance of your target view model and if isn't, delegate the call to the base class so WPF can handle the objects you don't care about.



来源:https://stackoverflow.com/questions/32580286/avalon-dock-2-0-layoutitemtemplateselector-given-contentpresenter-instead-of-vie

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!