WPF:调整画布以包含其子项

问题描述 投票:1回答:2

我有一个绘制多个矩形并操纵它们的任务。所以我使用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将无法工作。

现在的问题是,是否有任何财产缺失自动执行此操作?

c# wpf canvas itemscontrol
2个回答
1
投票

您可以使用覆盖MeasureOverride方法的自定义Canvas

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.LeftCanvas.Top之外,它还要求item容器元素在ItemContainerStyle中设置WidthHeight

<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放入ItemsControl的ControlTemplate中:

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

1
投票

@克莱门斯的答案很棒。我只想添加一些背景知道为什么在不清楚的情况下它是必要的。

在WPF中,Canvas小组在被要求自己测量时不考虑其子女。父控件负责确保画布的大小适合其包含的项目。

在@Clemens提供的子类中,考虑所有孩子的逻辑在MeasureOverride中提供。

请注意,这不会考虑任何非FrameworkElement儿童。如果添加大量子项,您可能会注意到布局期间的性能损失。

© www.soinside.com 2019 - 2024. All rights reserved.