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
Viewboxcontaining anItemsPresenter - In the control's Resources, created a Style targeting its own type, setting the
ItemsPanelto 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
ItemContainerStylewhich allows you to do just that:In particular, note that you do not need to explicitly declare an
ItemsPresenteryourself, that's done for you by virtue of the fact that you're already using an ItemsControl to begin with. Just set yourItemsPanelto Canvas, set the style of theContentPresenterviaItemContainerStyleand then useDataTemplatesand/or triggers to specify the look of the collection items themselves.