Объединение Viewbox, Canvas, ItemsPanelTemplate и ItemsPresenter в пользовательском элементе управления, наследуемом от ItemsPanel - PullRequest
0 голосов
/ 11 февраля 2019

Я хочу расширить ItemsPanel, чтобы я мог отобразить «многоуровневую» визуальную структуру, в которой у меня есть «рамка» с известным размером и множеством наложений, аналогично тому, каково было бы приложение для картографии или иллюстрации.

Проблема, с которой я сталкиваюсь, состоит в том, чтобы выяснить, как комбинировать вещи, чтобы все работало так, как ожидалось.Что я сделал до сих пор:

  • Создан элемент управления, который наследуется от ItemsControl;
  • Внутри элемента управления поместите Viewbox, содержащий ItemsPresenter
  • В элементах управления элемента управления создан стиль, нацеленный на его собственный тип, для которого ItemsPanel задан шаблон элементов, состоящий из Canvas.

Так что я ожидаю, что в режиме проверки живого дереваЯ должен видеть во вложенной структуре:

  • LayerContainer (имя класса моего элемента управления)
    • ViewBox
      • ItemsPresenter
      • Canvas
        • Item1
        • Item2

Вместо этого я вижу следующее:

  • LayerContainer
    • Граница
      • ItemsPresenter
      • Canvas
        • Viewbox
        • Item1
        • Item2

Итак, проблема в том, что ViewBox содержится внутри Canvas, наряду с отображаемыми элементами.

Myвопросn будет: как мне структурировать свой элемент управления 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 ]

0 голосов
/ 12 февраля 2019

Подумав и подумав, я понял, что могу "переопределить" свойство Template моего элемента управления, указав ControlTemplate, где я бы поставил Viewbox в качестве корня и внутри него ItemsPresenter.Затем при рендеринге каждый элемент будет помещен в ItemsPanelTemplate, содержащий Canvas, назначенный свойству ItemsPanel.

Ниже приведена окончательная рабочая форма.Принципом решения было установить также свойство Template (для ViewBox) помимо свойство ItemsPanel (для Canvas).

<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>
0 голосов
/ 12 февраля 2019

Если вам нужен рабочий пример того, как это сделать, то посмотрите мой 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 на Canvas, установите стиль ContentPresenter через ItemContainerStyle, а затем используйте DataTemplates и / или триггеры, чтобы указать внешний вид самих элементов коллекции.

...