WPF:调整大小 Canvas 以包含其子项
WPF: Sizing Canvas to contain its children
我有一个任务是绘制多个矩形并对其进行操作。所以我使用 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
。
现在的问题是,是否有任何 属性 会自动执行此操作?
您可以使用覆盖 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.Left
和 Canvas.Top
之外,还需要在 ItemContainerStyle 中设置 Width
和 Height
:
<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>
@Clemens 的回答很棒。我只是想添加一些上下文,说明为什么有必要这样做,以防不清楚。
在 WPF 中,Canvas
面板在被要求测量自身时不考虑它的 children。 parent 控件负责确保 canvas 的大小适合它将包含的项目。
在@Clemens提供的子类中,考虑所有children的逻辑在MeasureOverride
中提供。
请注意,这不会考虑任何非FrameworkElement
children。如果添加大量 children,您可能会注意到布局期间的性能下降。
我有一个任务是绘制多个矩形并对其进行操作。所以我使用 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
。
现在的问题是,是否有任何 属性 会自动执行此操作?
您可以使用覆盖 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.Left
和 Canvas.Top
之外,还需要在 ItemContainerStyle 中设置 Width
和 Height
:
<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>
@Clemens 的回答很棒。我只是想添加一些上下文,说明为什么有必要这样做,以防不清楚。
在 WPF 中,Canvas
面板在被要求测量自身时不考虑它的 children。 parent 控件负责确保 canvas 的大小适合它将包含的项目。
在@Clemens提供的子类中,考虑所有children的逻辑在MeasureOverride
中提供。
请注意,这不会考虑任何非FrameworkElement
children。如果添加大量 children,您可能会注意到布局期间的性能下降。