如何绑定到控件的文本实际宽度(包括其边距)?

6
根据一些人的说法,实际宽度可以使用下面示例中显示的ActualWidth属性来获得。这很有道理,但我似乎遇到了相互矛盾的行为。
<Canvas Width="{Binding ActualWidth,ElementName=Expy}">
  <Expander x:Name="Expy"
                    HorizontalAlignment="Left"
                    Margin="0,0,0,0"
                    VerticalAlignment="Top" ...>
  ...
  </Expander>
</Canvas>

在上述设置中,行为与期望一致,并且尽管紧密挤在一起,但包含画布的面板中的下一个元素没有被前一个元素覆盖。
然而,如果我将边距调整得更宽一些,我可以清楚地看到画布侵入了下一个元素,估计是与我在margin属性中请求的像素数相同。因此,实际宽度不是实际宽度,而是不包括边距的宽度。
1. 我是否混淆了什么,如果是,是什么? 2. 如何获取并绑定到实际渲染宽度?

enter image description here


我同意Clemens的观点。我倾向于使用网格布局,利用RowDefinitions和ColumnDefinitions。大多数情况下,我不会在任何东西上设置宽度,而是允许其相对于窗口进行调整大小。 - Mike Eason
1
在我看来,边距不应该包含在元素的宽度/高度中,这是与填充区别的关键之处。 - Grx70
基于 OP 希望实现的目标,使用 Canvas 在这种情况下是没有问题的(请参见此 SO 问题)。@Clemens - Steven Rands
1
@MikeEason,还有其他的考虑因素限制了我使用画布/扩展器。当然,如果需要的话,这可能会改变,所以我欢迎建议。不过,我仍然很好奇第二个问题的答案——如果需要绑定宽度并同时尊重设置的边距,应该怎么做? - Konrad Viltersten
@KonradViltersten,请查看我的更新答案,了解如何在仍使用画布的情况下完成此操作。 - Steven Rands
显示剩余2条评论
3个回答

9
链接中的答案说:
实际宽度要考虑填充和边距...
这是不正确的。ActualWidth仅包括填充,而不包括边距(ActualHeight也是如此)。
该答案下面的评论提供了适当的更正。
以下XAML代码说明了这个问题:
<Window x:Class="..."
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <TextBlock x:Name="First" Text="Some text" Padding="10" Margin="0,0"
            HorizontalAlignment="Left" Background="Yellow" />
        <TextBlock Text="{Binding ActualWidth, ElementName=First}" />

        <TextBlock x:Name="Second" Text="Some text" Padding="10" Margin="10,0"
            HorizontalAlignment="Left" Background="LimeGreen" />
        <TextBlock Text="{Binding ActualWidth, ElementName=Second}" />
    </StackPanel>
</Window>

如果您运行此代码,您会发现“Some text”文本块的ActualWidth值都相同,尽管第二个文本块应用了水平边距。
您问如何处理这个问题。由于ActualWidth属性不包括边距,因此无法通过绑定来解决此问题,正如前面所述。您可以将边距应用于父元素(画布),而不是应用于扩展器。
换句话说,您需要这样做:
<Canvas Width="{Binding ActualWidth,ElementName=Expy}">
    <Expander x:Name="Expy" ... Margin="10" ... >
        ...
    </Expander>
</Canvas>

执行以下操作:

<Canvas Width="{Binding ActualWidth,ElementName=Expy}" Margin="10">
    <Expander x:Name="Expy" ... >
        ...
    </Expander>
</Canvas>

哦,对于建议的解决方法加一。我差点按照我在评论中给Clemens的描述去尝试使用额外的面板等方式来解决问题。你的提示似乎要少花费很多精力。 - Konrad Viltersten

5

是的,Konrad。你有些混淆了。

每当我们提到实际(高度/宽度)时,它指的是渲染后的大小。你之前说的是正确的。但是,在WPF布局过程中(包括测量和排列阶段),实际(高度/宽度)的值是在初始化之后才得到的,这是你需要先了解的问题,以便找到真正的问题原因。

首先,将任何内容与实际值绑定将永远不会给您想要的结果,因为这样做违反了WPF布局链。根据WPF布局阶段,在测量阶段中,WPF获取布局中每个控件的指定大小(例如在高度和宽度中指定的值),然后在排列阶段中,它实际上以最佳方式分配控件到布局中。指定的大小可能会在排列阶段后发生变化。

此外,应注意,实际参数包括呈现大小加填充值(但不包括边距)。在你的例子中,我猜测旁边的面板是导致你报告的问题的原因。只有当我看到整个布局时才能确认。

但作为一项预防措施,您始终可以停止使用实际参数进行绑定。您可以使用宽度和高度值进行绑定,肯定可以解决问题。


很好。我实际上更喜欢不使用ActualWidth。我只是遵循了在回答我的另一个问题时建议的提示。也许我应该更加怀疑,但我相信这个建议。谢谢。(另外,我想你的意思是我混淆了某些东西,而不是我自己混淆了,呵呵。) - Konrad Viltersten
2
@KonradViltersten 在某些情况下,绑定到 ActualWidth(或 ActualHeight)是没有问题的。许多人似乎在说您可以在不绑定这些属性的情况下做您想要的事情。但是,没有人展示任何支持这些论点的代码。我可能被证明错误,但我怀疑您无法获得所需的布局,而不是用外部网格元素使 XAML 复杂化 显式设置扩展器控件的宽度。 - Steven Rands

1

你不能将margin包含在ActualWidth中。解决方法是使用DesiredSize.WidthDesiredSize.Height,这将考虑到Margin。但它是一个UIElement。


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