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:
- I add a
ViewModel
to the documents source. - My custom
DataTemplateSelector.SelectTemplate()
is called. - The item parameter in
SelectTemplate()
is aSystem.Windows.Controls.ContentPresenter
instead of theViewModel
object that I added. - Even if I return the correct
DataTemplate
, it ends up getting bound to theContentPresenter
instead of theViewModel
contained within theContentPresenter
.
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?
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