WPF: Sizing Canvas для хранения дочерних элементов - PullRequest
1 голос
/ 11 марта 2019

У меня есть задача нарисовать несколько прямоугольников и манипулировать ими.Поэтому я использую ItemsControl с ItemsPanel в качестве Canvas и упаковку ScrollViewer:

<ScrollViewer Height="200" Grid.Row="1" >
    <ItemsControl Name="rectanglesList" Background="AliceBlue">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding Y}"/>
                <Setter Property="Canvas.Top" Value="{Binding X}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border BorderThickness="1" BorderBrush="Black">
                    <Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="{Binding Color}"/>

                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</ScrollViewer>

Теперь, поскольку свойства высоты и ширины Canvas не включают его дочерние элементы,Scrollviewer не будет работать, если я не изменю высоту и ширину ItemsControl вручную, рассчитав сумму сумм высоты и ширины дочерних элементов и присвоив их ItemsControl.

Вопрос теперь в том, отсутствует ли какое-либо свойство, которое делает это автоматически?

Ответы [ 2 ]

1 голос
/ 16 марта 2019

@ Ответ Клеменса отличный.Я просто хотел добавить некоторый контекст, объясняющий, почему это необходимо в случае, если неясно.

В WPF панель Canvas не учитывает своих потомков, когда ее просят измерить себя.Родительский элемент управления должен обеспечить правильный размер холста для элементов, которые он будет содержать.

В подклассе @Clemens логика для рассмотрения всех дочерних элементов представлена ​​в MeasureOverride.

Обратите внимание, что это не касается детей, не являющихся FrameworkElement.Если вы добавите очень большое количество дочерних элементов, вы можете заметить снижение производительности во время макета.

1 голос
/ 11 марта 2019

Вы можете использовать пользовательский Canvas, который переопределяет метод MeasureOverride

public class MyCanvas : Canvas
{
    protected override Size MeasureOverride(Size constraint)
    {
        base.MeasureOverride(constraint);

        var size = new Size();

        foreach (var child in Children.OfType<FrameworkElement>())
        {
            var x = GetLeft(child) + child.Width;
            var y = GetTop(child) + child.Height;

            if (!double.IsNaN(x) && size.Width < x)
            {
                size.Width = x;
            }

            if (!double.IsNaN(y) && size.Height < y)
            {
                size.Height = y;
            }
        }

        return size;
    }
}

и который будет использоваться следующим образом:

<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <local:MyCanvas/>
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

Требуется, чтобы элемент контейнера элемента,кроме того, Canvas.Left и Canvas.Top имеют свои Width и Height, установленные в ItemContainerStyle:

<ItemsControl.ItemContainerStyle>
    <Style TargetType="ContentPresenter">
        <Setter Property="Canvas.Left" Value="{Binding X}"/>
        <Setter Property="Canvas.Top" Value="{Binding Y}"/>
        <Setter Property="Width" Value="{Binding Width}"/>
        <Setter Property="Height" Value="{Binding Height}"/>
    </Style>
</ItemsControl.ItemContainerStyle>

Тогда ItemTemplate будет выглядеть так:

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Border BorderThickness="1" BorderBrush="Black">
            <Rectangle Fill="{Binding Color}"/>
        </Border>
    </DataTemplate>
</ItemsControl.ItemTemplate>

или

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Rectangle StrokeThickness="1" Stroke="Black" Fill="{Binding Color}"/>
    </DataTemplate>
</ItemsControl.ItemTemplate>

Вы также можете поместить SCrollViewer в ControlTemplate ItemsControl:

<ItemsControl.Template>
    <ControlTemplate TargetType="ItemsControl">
        <ScrollViewer HorizontalScrollBarVisibility="Auto"
                      VerticalScrollBarVisibility="Auto">
            <ItemsPresenter/>
        </ScrollViewer>
    </ControlTemplate>
</ItemsControl.Template>
...