DockSite / Tab "Header" - Upgrade from 2015 to 2019 challenge

Docking/MDI for WPF Forum

Posted 8 months ago by Brad Salmon
Version: 19.1.0684
Avatar

I am working on upgrading our application from version 2015 to 2019. I have it running, but working through some details with Docking. We are using MVVM pattern. We have a DockSite defined like:

<docking:DockSite DocumentItemsSource="{Binding Tabs}" AreNewTabsInsertedBeforeExistingTabs="False"
DocumentItemContainerStyle="{StaticResource DocumentItemStyle}">
<docking:Workspace>
<docking:TabbedMdiHost/>
</docking:Workspace>
</docking:DockSite>

The Tabs collection is an ObservableCollection of a class similar to:

public class DockItem : ContentControl, INotifyPropertyChanged
{
public IViewModel ViewModel { get; set; }
// other public properties for Header, Title, IsActive, IsSelected, ImageToShow, etc.
}

When we add items to the Tabs collection, that show as expected in the DockSite. My current issue is that I am trying to get the "tab title" to display how it used to when we were using the Header and HeaderTemplate in our DocumentItemStyle style. I understand from the documentation I can use a DataTemplate for the TabbedMdiTabContextContentTemplate property on the DockingWindow. I am struggling, however, to get the elements in the template to acces properties on our DockItem class of the tab. The documentation says:

The data context of the DataTemplate will be the DataContext of the docking window, unless the DataContext is a UIElement. In that case, no data context is passed since it could otherwise lead to logical tree issues.

I'm assuming my issue is due to our DockItem class deriving from ContentControl so "no data context is passed...".

My template has:

<Style x:Key="DockingItemStyle" TargetType="docking:DockingWindow" >
     <Setter Property="Title" Value="{Binding Header}" />
     <Setter Property="TabbedMdiTabContextContentTemplate">
           <Setter.Value>
                    <DataTemplate>
                          <StackPanel Orientation="Horizontal">
                                  <TextBlock Text="{Binding Header}" />
                                  <Image Source="{Binding ImageToShow}" />
                          </StackPanel>
                     </DataTemplate>
             </Setter.Value>
       </Setter>

</Style>

Is there any way for the bindings in the DataTemplate to either access the DockItem class properties or the DocumentWindow properties?

I have looked at the sample to get some ideas to try but would like have to make some large changes to how we have done some things; so hoping for an easier option.

[Modified 8 months ago]

Comments (5)

Posted 8 months ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Brad,

That is correct, the data contexts get tricky if you are using UIElements as your DocumentItemsSource.  We have to prevent the normal data context from being passed in for certain scenarios to avoid the logical tree issue you mentioned.

I would highly recommend that you pass in plain CLR objects in as your Tabs.  Then have a Content property on them where you return your ContainerControl and bind the DockingWindow.Content to that property.  That would allow you to properly get to other properties that you could define on your CLR object like Title, ImageSource, etc. or have a ViewModel sub-property with those properties on it.


Actipro Software Support

Posted 8 months ago by Brad Salmon
Avatar

I've tried this, but binding the DockingWindow.Content property doesn't seem to work. I defined a property on my DockItem class called "ItemContent" (of type UIElement). Then in my Style for the DockingWindow I have

   <Setter Property="Content" Value="{Binding ItemContent}"/>

my other bindings for the style are working, but when the tab displays the "content" that appears is just the name of my DockItem class. I set a breakpoint in my DockSite_WindowRegistered method and grabbed the DockingWindow object. It's DataContext property is the DockItem object and the Content property is also the DockItem object (vs. the DockItem's ItemContent).

Posted 8 months ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Brad,

Yes, that's true, it's likely binding the DockItem data item as the DockingWindow.Content.  The general intent with our MVVM support is for you to supply a DataTemplate for the "Content" and that's your UI, similar to how ItemsControls work. 

If you wish to keep your setup where you have a Control of your own that you wish to include directly, you might have to make a DataTemplate that has a ContentPresenter in it, then bind that ContentPresenter.Content to your ItemContent property.  Offhand, something like this:

<Setter Property="ContentTemplate">
 <Setter.Value>
  <DataTemplate>
   <ContentPresenter Content="{Binding ItemContent}" />
  </DataTemplate>
 </Setter.Value>
</Setter>

[Modified 8 months ago]


Actipro Software Support

Posted 8 months ago by Brad Salmon
Avatar

I got things to work. I first tried to set the ContentTemplate of the DocumentWindow in the Style I defined and was specifying in the DockSite.DocumentItemContainerStyle property. That did not work. Then I defined a stand-alone DataTemplate like:

<DataTemplate x:Key="DocumentWindowTemplate">
         <ContentPresenter Content="{Binding ItemContent}" />
</DataTemplate>

And set my DockSite.DocumentItemTemplate to that DataTemplate and it is working.

I appreciate the help on this. I do feel, however, that there are a lot of moving pieces and not very intuitive. Why didn't binding the Content property of the DocumentWindow work? Why didn't setting the ContentTemplate of the DocumentWindow work?

Anyway, this problem is solved....now I move onto the next problem (A new Post is forecoming :)

Posted 8 months ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Brad,

It works like an ItemsControl where we have this method:

protected virtual void PrepareContainerForItemOverride(DockingWindow container, object item, DockingWindowItemKind kind) {
	if (container == null)
		throw new ArgumentNullException("container");

	// Apply content templates and container styles
	if (kind == DockingWindowItemKind.Document) {
		container.ContentTemplate = this.DocumentItemTemplate;
		container.ContentTemplateSelector = this.DocumentItemTemplateSelector;

		ApplyContainerStyle(item, container, this.DocumentItemContainerStyleSelector, this.DocumentItemContainerStyle);
	}
	else {
		container.ContentTemplate = this.ToolItemTemplate;
		container.ContentTemplateSelector = this.ToolItemTemplateSelector;

		ApplyContainerStyle(item, container, this.ToolItemContainerStyleSelector, this.ToolItemContainerStyle);
	}
	container.Content = item;
}

So the DockingWindow is getting its Content, ContentTemplate, and ContentTemplateSelector properties set in this DockSite method.  If you wanted to apply a universal DataTemplate, you could set DockSite.DocumentItemTemplate since per the above code, that takes priority over a Style setting of those three properties.


Actipro Software Support

The latest build of this product (v2019.1 build 0686) was released 2 months ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.