改变画布内控件位置后,滚动条不可见。

3

我创建了一个继承自 WPF Canvas 的自定义画布控件。在主窗口中,我像这样使用它 -

<ScrollViewer
    HorizontalScrollBarVisibility="Auto"
    VerticalScrollBarVisibility="Auto">
    <RTD:RTDesignerCanvas
        Margin="5"
        Background="White"
        x:Name="canvas1"
        Focusable="True"
        AllowDrop="True">
    </RTD:RTDesignerCanvas>
</ScrollViewer>

一切都正常,但当我尝试像这样设置其内部控件的位置时

Canvas.SetTop(item, 200);

滚动条不可见,控件被隐藏在下方。有趣的是,如果我向其中添加另一个控件,则滚动条将可见,我可以向下滚动以查看先前的控件。

我尝试使用

base.InvalidateVisual();
base.UpdateLayout();
base.InvalidateArrange();

更改项目的TopLeft后没有发生任何事情;我是漏掉了什么还是这是由于某些错误导致的?
更新:
为了澄清,假设我有一个画布,它的widthheight都是100。现在,如果我使用Canvas.SetLeft(myControl, 200)移动一个已经添加到画布中的控件,则它将移动到默认情况下不可见的位置,并且滚动条也被禁用,因此无法看到该控件。
现在,如果我向画布添加另一个控件,滚动条会正确显示,并且我可以通过滚动查看先前的控件。
3个回答

8

你在自定义 Canvas 中覆盖了 MeasureOverride 吗?Canvas 总是报告 (0, 0) 的 DesiredSize,因此 ScrollViewer 永远不会认为它需要滚动。

请参见 此 StackOverflow 回答,其中建议使用 Grid 而不是 Canvas,并使用 Margin 属性进行定位。Grid 将根据其子元素的大小和位置报告其大小,因此 ScrollViewer 将知道它需要滚动。

更新:

ScrollViewer 将给其子元素提供所需的任何大小,并且只有在子元素大于 ScrollViewer 时才需要滚动。为了使其正确滚动,您需要报告足够大以包含所有子控件的 DesiredSize。您可以通过像这样覆盖 MeasureOverride 来实现:

protected override Size MeasureOverride(Size constraint)
{
    base.MeasureOverride(constraint);
    var desiredSize = new Size();
    foreach (UIElement child in Children)
    {
        desiredSize = new Size(
            Math.Max(desiredSize.Width, GetLeft(child) + child.DesiredSize.Width),
            Math.Max(desiredSize.Height, GetTop(child) + child.DesiredSize.Height));
    }
    return desiredSize;
}

然而,更简单的解决方案是利用 Grid 类已经实现了这样的测量方式。您可以使用子元素的 Margin 属性来精确定位它们,而不是使用 Canvas.Left 和 Canvas.Top 属性。如果您目前正在执行此操作

Canvas.SetLeft(item, 100);
Canvas.SetTop(item, 200);

在 Canvas 中,您可以改为执行以下操作。
item.Margin = new Thickness(100, 200, 0, 0);

将其放置在单元格网格中的相同位置。

感谢您的回复Quartermeister,我没有在自定义画布中覆盖MeasureOverride;从您的答案中我不清楚是否应该覆盖它?但是,您说得对,“滚动查看器永远不会认为需要滚动”,这正是发生的情况。使用网格(Grid)不是一个选项,因为我需要移动、调整大小等功能,这些功能必须使用画布(Canvas)。 - akjoshi
我的错误,我不知道怎么错过了它。实际上,我正在覆盖它并在其中执行相同的操作。今天我发现对于某些操作根本没有调用此方法(这就是为什么滚动条不可见)。我不得不在每个操作之后显式使用InvalidateMeasure()来刷新画布。 - akjoshi

1

Quartermeister的回答帮助我解决了这个问题。

在每次操作后,我不得不明确地使用base.InvalidateMeasure()来刷新画布并使滚动条可见。


0

Akjoshi,

这一定是个bug。下次画布消失时,请在其上运行Snoop,并检查它的位置。确保检查以下画布属性:ActualWidthActualHeightOpacityVisibility


谢谢Anvaka,实际上我的画布并没有被隐藏,而是其中的控件被隐藏了。我已经更新了问题以使其更加清晰。 - akjoshi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接