【问题标题】:Size canvas to adjacent label's text height将画布调整为相邻标签的文本高度
【发布时间】:2023-03-19 23:39:01
【问题描述】:

对于 WPF 界面,我正在构建在标签旁边显示图标的用户控件。我在 Canvas 元素中构建图标,并将其粘贴在标签旁边的 DockPanel 中。我将它捆绑到一个 UserControl 中,以便整个控件可以绑定到单个值并重绘图标并在其更改时更新文本(想想电池表)。

我无法将图标的画布缩放到与标签文本大致相同的高度并将其大致定位在标签的基线上。有没有简单的方法可以做到这一点?如果我允许通过属性更改字体,我可以检查布局测量并相应地缩放/定位图标的画布吗?

作为参考,这里是电池表的 XAML,以及时钟和电池表的图像。请注意,电池表有点接近我想要的(在基线上,但不是文本的高度),但时钟太大了。

<UserControl x:Class="Controls.Battery"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:controls="clr-namespace:Controls"
         x:Name="Root"
         mc:Ignorable="d" 
         d:DesignHeight="200" d:DesignWidth="900">
<DockPanel DataContext="{Binding ElementName=Root}" LastChildFill="True" Height="200" Width="900">
    <Canvas DockPanel.Dock="Left" Width="200" Height="200" Background="Transparent">
        <Rectangle Width="180" Height="100" StrokeThickness="20" Canvas.Top="50" Stroke="White" RadiusX="5" RadiusY="5">
            <Rectangle.Fill>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                    <GradientStop Color="White" Offset="0"/>
                    <GradientStop Color="White" Offset="{Binding Percent}"/>
                    <GradientStop Color="Transparent" Offset="1"/>
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <Rectangle Width="40" Height="50" StrokeThickness="20" Canvas.Top="75" Canvas.Right="0" Stroke="White" RadiusX="5" RadiusY="5"/>
    </Canvas>

    <Viewbox HorizontalAlignment="Left">
        <TextBlock Text="{Binding Percent, Converter={controls:PercentConverter}}" Foreground="White" />
    </Viewbox>
</DockPanel>

【问题讨论】:

    标签: c# wpf user-controls


    【解决方案1】:

    Canvas 的子元素永远不会调整大小,它们只是定位在指定的坐标处。所以改变画布的大小不会影响它的孩子。此外,Canvas 的子元素总是被赋予他们想要的完整大小。所以在你的情况下,我认为最好使用 Grid 元素。 类似的东西:

    <UserControl x:Class="Controls.Battery"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:controls="clr-namespace:Controls"
         x:Name="Root"
         mc:Ignorable="d" 
         d:DesignHeight="200" d:DesignWidth="900">
    <Grid DataContext="{Binding ElementName=Root}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
    
        <Viewbox Grid.Column="0"
                 VerticalAlignment="Center"
                 Stretch="Uniform"
                 StretchDirection="Both">
            <Grid Margin="10 60 10 40">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <Rectangle Grid.Column="0"
                           Width="180"
                           Height="100"
                           RadiusX="5"
                           RadiusY="5"
                           Stroke="White"
                           StrokeThickness="20">
                    <Rectangle.Fill>
                        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                            <GradientStop Offset="0" Color="White" />
                            <GradientStop Offset="{Binding Percent}" Color="White" />
                            <GradientStop Offset="1" Color="Transparent" />
                        </LinearGradientBrush>
                    </Rectangle.Fill>
                </Rectangle>
                <Rectangle Grid.Column="1"
                           Width="40"
                           Height="50"
                           Margin="-20 0 0 0"
                           RadiusX="5"
                           RadiusY="5"
                           Stroke="White"
                           StrokeThickness="20" />
            </Grid>
        </Viewbox>
    
        <Viewbox Grid.Column="1"
                 HorizontalAlignment="Left"
                 VerticalAlignment="Center">
            <TextBlock Foreground="White" Text="{Binding Percent, Converter={controls:PercentConverter}}" />
        </Viewbox>
    </Grid>
    

    【讨论】:

      【解决方案2】:

      这是我能找到的最接近的解决方案。我将 TextBlock 移动到画布内并用 ViewBox 包裹以缩放整个区域,并在 TextBlock 周围使用 ViewBox 以缩放以匹配画布高度。我还将画布高度更改为 100(以消除电池上方和下方的空间),并对矩形进行了一些定位调整:-

              <Viewbox Stretch="Uniform"
                       StretchDirection="Both">
                  <Canvas DockPanel.Dock="Left"
                          Width="400"
                          Height="100"
                          Background="Black">
                      <Rectangle Width="180"
                                 Height="100"
                                 StrokeThickness="20"
                                 Canvas.Top="0"
                                 Stroke="White"
                                 RadiusX="5"
                                 RadiusY="5">
                          <Rectangle.Fill>
                              <LinearGradientBrush StartPoint="0,0"
                                                   EndPoint="1,0">
                                  <GradientStop Color="White"
                                                Offset="0" />
                                  <GradientStop Color="White"
                                                Offset="{Binding Percent}" />
                                  <GradientStop Color="Transparent"
                                                Offset="1" />
                              </LinearGradientBrush>
                          </Rectangle.Fill>
                      </Rectangle>
                      <Rectangle Width="40"
                                 Height="50"
                                 StrokeThickness="20"
                                 Canvas.Top="25"
                                 Canvas.Right="200"
                                 Stroke="White"
                                 RadiusX="5"
                                 RadiusY="5" />
      
                      <Viewbox Stretch="UniformToFill"
                               StretchDirection="Both"
                               Canvas.Left="200"
                               Height="100">
                          <TextBlock Text="1%"
                                     Padding="0"
                                     Margin="0"
                                     LineStackingStrategy="BlockLineHeight"
                                     LineHeight="20"
                                     FontSize="20"
                                     Foreground="White" />
                      </Viewbox>
                  </Canvas>
              </Viewbox>
      

      将它放到一个窗口中 - 你会发现画布和文本在调整窗口大小时都成比例地缩放(并保持它们的纵横比)。

      电池与文本的顶部和底部没有完全对齐,但我怀疑这是由于字体本身存在间距。我发现我可以通过在TextBlock 上使用负上边距和下边距来改进事情,但这可能是“hacky”,因为调整量取决于所使用的字体。

      可能有更优雅的解决方案,例如改为使用几何/路径作为电池图标。顺便说一句,您见过出色的“Metro Studio”工具吗?这是一组免费的 XAML 图标 - 里面有一些电池,您可以根据自己的需要调整其中的一个。

      【讨论】:

      • 感谢您的出色回答,以及您必须投入的所有工作。我很感激,但采用了一种基于公认答案的方法。来自我的 +1。
      【解决方案3】:

      您不应在 xaml 中使用精确的像素值,而应绑定这些值并使用转换器来设置正确的值。 (至少这是我会做的......)

      给你的 TextBlock 命名:

      <Viewbox HorizontalAlignment="Left">
          <TextBlock x:Name="textPart" Text="{Binding Percent, Converter={controls:PercentConverter}}" Foreground="White" />
      </Viewbox>
      

      之后,您可以绑定到 ActualWidthActualHeight 属性。

      例如:

      <Canvas DockPanel.Dock="Left" 
              Width="200" 
              Height="{Binding Path=ActualHeight, ElementName=textPart}" 
              Background="Transparent">
      ...
      

      为了进一步改善外观,您应该引入转换器,例如,您可以将宽度设置为高度的给定百分比,或者您想要的任何内容。

      此外,您可以在画布中引入ScaleTransform,这很可能会减少对转换器的需求,因为您可以设计一种尺寸的“图片”,并且只需修改 ScaleTansform 以使其正确大小。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-09-30
        • 1970-01-01
        • 1970-01-01
        • 2020-03-02
        • 1970-01-01
        • 1970-01-01
        • 2015-02-27
        • 2017-01-12
        相关资源
        最近更新 更多