首页 > 解决方案 > 无法在画布内居中画布

问题描述

我在 WPF 中有一个视图,我需要在画布中将画布居中。我知道这不是最合适的容器,但我们还有其他组件将出现在这个画布中,它将大大简化我们的工作。

所以基本上,我目前有这段代码:

<Canvas Name="RootContainer" Background="#373B3F" ClipToBounds="True" MouseLeftButtonDown="OnMainCanvasMouseLeftButtonDown">
    <Canvas.DataContext>
        <local:SomeViewModel/>
    </Canvas.DataContext>
    
    <Canvas Name="SomeContainer" Background="#373B3F" MouseMove="OnCanvasMouseMove" MouseWheel="OnCanvasMouseWheel">
        <Canvas.Left>
            <MultiBinding Converter="{StaticResource CenterValueConverter}">
                <Binding ElementName="RootContainer" Path="ActualWidth" />
                <Binding ElementName="SomeContainer" Path="ActualWidth" />
            </MultiBinding>
        </Canvas.Left>
        <Canvas.Top>
            <MultiBinding Converter="{StaticResource CenterValueConverter}">
                <Binding ElementName="RootContainer" Path="ActualHeight" />
                <Binding ElementName="SomeContainer" Path="ActualHeight" />
            </MultiBinding>
        </Canvas.Top>

        <ItemsControl ItemsSource="{Binding SomeChilds}" Name="ItemsControl">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Canvas.Left" Value="{Binding Left}" />
                    <Setter Property="Canvas.Top" Value="{Binding Top}" />
                </Style>
            </ItemsControl.ItemContainerStyle>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource ConventionBasedDataTemplateSelector}"
                                MouseLeftButtonDown="OnMouseLeftButtonDown" PreviewMouseLeftButtonUp="OnPreviewMouseLeftButtonUp" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <!-- Some other controls over here -->
    </Canvas>
</Canvas>

这个转换器:

public class CenterValueConverter : IMultiValueConverter
{

    public object Convert(object[] values,
        Type targetType,
        object parameter,
        CultureInfo culture)
    {
        if (values == null || values.Length != 2)
        {
            return null;
        }

        double totalWidth = (double)values[0];
        double size = (double)values[1];
        return totalWidth / 2 - (size / 2);
    }

    public object[] ConvertBack(object value,
        Type[] targetTypes,
        object parameter,
        CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

问题是第二个值(SomeContainer.ActualWidth/ActualHeight)总是带有值 0(即使我有一些真实的元素)。

知道为什么以及如何解决这个问题吗?SomeContainer或者在?中居中的另一种 XAML 方式RootContainer

编辑 也许我计划使用这么多的一些额外信息Canvas

标签: wpfxamlcanvas

解决方案


我猜你的 ActualHeight/ActualWidth 是 = 0 因为你还没有加载窗口。为了使这些计算考虑到 Canvas 的尺寸,您可以使用“OnContentRendered”事件,该事件将在显示窗口后启动,因此您将显示它们的尺寸。如果你的 Canvas 可能会改变尺寸,你也可以使用SizeChangedevent. 即使我认为这有点复杂,您也可以使用取自Clemen 的答案的XAML 代码:

<Canvas Background="Transparent"
        SizeChanged="ViewportSizeChanged"
        MouseLeftButtonDown="ViewportMouseLeftButtonDown"
        MouseLeftButtonUp="ViewportMouseLeftButtonUp"
        MouseMove="ViewportMouseMove"
        MouseWheel="ViewportMouseWheel">

    <Canvas x:Name="canvas" Width="1000" Height="600">
        <Canvas.RenderTransform>
            <MatrixTransform x:Name="transform"/>
        </Canvas.RenderTransform>

        <Ellipse Fill="Red" Width="100" Height="100" Canvas.Left="100" Canvas.Top="100"/>
        <Ellipse Fill="Green" Width="100" Height="100" Canvas.Right="100" Canvas.Bottom="100"/>
    </Canvas>
</Canvas>

推荐阅读