I am creating a custom theme for my WPF application that uses the AvalonDock docking framework.
I have already opened a GitHub issue for my question on the AvalonDock repo but I'm hoping I can get an answer faster here (and ready to put a bounty on this ASAP).
In my custom theme I have moved the tab items for the LayoutAnchorablePane
to stack vertically on the left side, and the pane uses a Grid with column sizes Auto, *, Auto
.
I would like to write a Trigger for the style that moves the tabs from the left column to the right column when the LayoutAnchorablePane
is attached to the right side of the root layout panel. (So that the tabs are always on the outside)
Here is the relevant section of my theme's XAML that I am trying to put the trigger on. This is almost identical to the LayoutAnchorablePaneControl
template from the generic.xaml style in AvalonDock:
<Grid
ClipToBounds="true"
KeyboardNavigation.TabNavigation="Local"
SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Following border is required to catch mouse events -->
<Border Grid.ColumnSpan="3" Background="Transparent" />
<StackPanel
x:Name="HeaderPanel"
Width="40"
Grid.Column="0"
Panel.ZIndex="1"
IsItemsHost="true"
KeyboardNavigation.TabIndex="1" />
<Border
x:Name="ContentPanel"
Grid.Column="1"
Background="Transparent"
BorderThickness="2"
BorderBrush="{StaticResource PrimaryBrush}"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
KeyboardNavigation.TabNavigation="Cycle">
<ContentPresenter
x:Name="PART_SelectedContentHost"
ContentSource="SelectedContent"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ??? }">
<Setter TargetName="HeaderPanel" Property="Grid.Column" Value="2"/>
</DataTrigger>
</ControlTemplate.Triggers>
As far as I can tell, there is no property on LayoutAnchorablePane
or any of its interfaces that exposes which side of the layout the pane is on. So I'm lost for what I can put in {Binding ??? }
in my DataTrigger.
It seems like I need to implement the property myself and use my own builds of AvalonDock. I would like to avoid this if it's at all possible; So maybe there's some clever MarkupExtension or Converter idea I could implement in my own code? Maybe my assumption that this can be done with DataTrigger
can be challenged too. I would be happy to use a completely code-behind solution for this.
Thanks to BionicCode for the really detailed answer, I ended up only needing a few hints from that answer to solve the problem in my own way. So I thought it would be worth sharing my code too.
LayoutAnchorablePaneControl
inherits from TabControl
so it already has the TabStripPlacement
property that the style can bind to on its templated parent.
So the new style replaces Grid
with DockPanel
and looks like this:
<DockPanel
ClipToBounds="true"
KeyboardNavigation.TabNavigation="Local"
SnapsToDevicePixels="true">
<!-- Following border is required to catch mouse events -->
<Border Background="Transparent" />
<StackPanel
x:Name="HeaderPanel"
Width="40"
DockPanel.Dock="{TemplateBinding TapStripPlacement}"
Panel.ZIndex="1"
IsItemsHost="true"
KeyboardNavigation.TabIndex="1" />
<Border
x:Name="ContentPanel"
Background="Transparent"
BorderThickness="2"
BorderBrush="{StaticResource PrimaryBrush}"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
KeyboardNavigation.TabNavigation="Cycle">
<ContentPresenter
x:Name="PART_SelectedContentHost"
ContentSource="SelectedContent"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</DockPanel>
Now this style will move the tabs to any of the sides (left/right/top/bottom) depending on the LayoutAnchorablePaneControl.TabStripPlacement
property.
In code behind (for the Window that has the DockingManager) I attached an event handler to DockingManager.Layout.Updated
that runs the following method:
private void UpdateTabSides()
{
foreach (LayoutAnchorablePaneControl apc in DockManager.LayoutRootPanel.FindLogicalChildren<LayoutAnchorablePaneControl>())
{
var side = apc.Model.GetSide();
if (side == AnchorSide.Right)
{
apc.TabStripPlacement = Dock.Right;
}
else
{
apc.TabStripPlacement = Dock.Left;
}
}
}
I find this approach to be a lot simpler than BionicCode's answer but they deserve the bounty for pushing me in the right direction.