Объединение Viewbox, Canvas, ItemsPanelTemplate и ItemsPresenter в пользовательском элементе управления, наследуемом от ItemsPanel
Я хочу продлить ItemsPanel
так что я могу отобразить "многоуровневую" визуальную структуру, в которой у меня есть "рамка" с известным размером и множеством наложений, аналогично тому, каким будет приложение для картографии или иллюстрации.
Проблема, которую я имею, состоит в том, чтобы выяснить, как комбинировать вещи, чтобы все работало, как ожидалось. Что я сделал до сих пор:
- Создан элемент управления, который наследуется от
ItemsControl
; - Внутри управления, положить
Viewbox
содержащийItemsPresenter
- В ресурсах элемента управления создан стиль, нацеленный на его собственный тип, задающий
ItemsPanel
к шаблону Items, состоящему изCanvas
,
Таким образом, я ожидаю, что в Live Tree Inspection я должен увидеть вложенную структуру:
- LayerContainer (имя класса моего элемента управления)
- ViewBox
- ItemsPresenter
- холст
- Элемент1
- Элемент2
- ViewBox
Вместо этого я вижу следующее:
- LayerContainer
- бордюр
- ItemsPresenter
- холст
- Viewbox
- Элемент1
- Элемент2
- бордюр
Таким образом, проблема в том, что ViewBox содержится внутри Canvas, наряду с визуализированными элементами.
Тогда мой вопрос будет таким: как мне структурировать свой элемент управления LayerContainer таким образом, чтобы порядок размещения был ItemsPresenter->Viewbox->Canvas->Items?
Вот мой контроль (имя на самом деле не 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>
2 ответа
Если вам нужен рабочий пример того, как это сделать, то посмотрите мой редактор Perfy на GitHub, соответствующая часть находится в файле MainWindow.xaml. Также показано, как реализовать масштабирование и прокрутку (если вы хотите поддерживать оба, тогда вам на самом деле не нужен ViewBox, только родитель ScrollViewer и LayoutTransform для ItemsControl).
Чтобы ответить на ваш вопрос, каждый элемент обернут в ContentPresenter, трюк состоит в том, чтобы вместо этого установить позицию Canvas для этого родительского элемента. ItemsControl выставляет ItemContainerStyle
что позволяет вам сделать именно это:
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
В частности, обратите внимание, что вам не нужно явно объявлять ItemsPresenter
сами, это сделано для вас в силу того факта, что вы уже используете ItemsControl для начала. Просто установите свой ItemsPanel
на холст, установите стиль ContentPresenter
с помощью ItemContainerStyle
а затем использовать DataTemplates
и / или триггеры для указания внешнего вида самих предметов коллекции.
Подумав и подумав, я понял, что могу "переопределить" Template
собственность моего контроля, с ControlTemplate
где я бы положил Viewbox
как корень и внутри него ItemsPresenter. Затем при рендеринге каждый элемент будет помещен в ItemsPanelTemplate
содержащий Canvas
, назначенный ItemsPanel
имущество.
Ниже приведена окончательная рабочая форма. Принципом решения было установить также Template
свойство (для ViewBox), кроме ItemsPanel
собственность (для холста).
<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.Template>
<ControlTemplate TargetType="ItemsControl">
<Grid x:Name="container">
<Viewbox Stretch="Uniform"
Width="{Binding ActualWidth, ElementName=container}"
Height="{Binding ActualHeight, ElementName=container}">
<ItemsPresenter/>
</Viewbox>
</Grid>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="{Binding Parametros.Colunas}"
Height="{Binding Parametros.Linhas}"
IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>