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?
LayoutItemTemplateSelector
was modeled after the way ContentTemplate was done, and if I recall correctly it had some slightly different behavior... namely that it used the.Content
for the.DataContext
, and the.Content
is a ContentPresenter that wraps your ViewModel – Croquet<local:TestView DataContext="{TemplateBinding Content}" />
? – Croquet