I want to extend ItemsPanel
so that I can display a "layered" visual structure where I have a "frame" with a known size and a lot of overlays, similar to what a cartographic or illustration application would be.
The problem I am having is to find out how to combine things so that everything works as expected. What I have done so far:
- Created a control which inherits from
ItemsControl
; - Inside the control, put a
Viewbox
containing anItemsPresenter
- In the control's Resources, created a Style targeting its own type, setting the
ItemsPanel
to an ItemsTemplate consisting of aCanvas
.
So I would expect that, under Live Tree Inspection, I should see, in a nested structure:
- LayerContainer (the class name of my control)
- ViewBox
- ItemsPresenter
- Canvas
- Item1
- Item2
- ViewBox
Instead, what I see is this:
- LayerContainer
- Border
- ItemsPresenter
- Canvas
- Viewbox
- Item1
- Item2
- Border
So the problem is that the ViewBox is contained inside Canvas, alongside the rendered items.
My question then would be: how do I structure my LayerContainer control in a way that the nesting order is ItemsPresenter->Viewbox->Canvas->Items?
Here is my control (the name is not actually LayerContainer
)
<ItemsControl x:Class="Miotec.PressureMapping.UserControls.BaroLayerContainer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Miotec.PressureMapping.UserControls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<ItemsControl.Resources>
<Style TargetType="local:BaroLayerContainer">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Canvas Width="{Binding Parametros.Colunas}"
Height="{Binding Parametros.Linhas}"
IsItemsHost="True"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.Resources>
<Viewbox Stretch="Uniform" x:Name="container">
<ItemsPresenter
Width="{Binding ActualWidth, ElementName=container}"
Height="{Binding ActualHeight, ElementName=container}"/>
</Viewbox>
</ItemsControl>
If you want a working example of how to do this then check out my Perfy editor on GitHub, the relevant part is in the MainWindow.xaml file. It also shows how to implementing zoom and scrolling (if you want to support both then you don't actually need a ViewBox, just a ScrollViewer parent and a LayoutTransform on the ItemsControl).
To answer your question, each item gets wrapped in a ContentPresenter, the trick is to set your Canvas position on this parent item instead. ItemsControl exposes the
ItemContainerStyle
which allows you to do just that:In particular, note that you do not need to explicitly declare an
ItemsPresenter
yourself, that's done for you by virtue of the fact that you're already using an ItemsControl to begin with. Just set yourItemsPanel
to Canvas, set the style of theContentPresenter
viaItemContainerStyle
and then useDataTemplates
and/or triggers to specify the look of the collection items themselves.