拉伸标签固定宽度的 WPF 居中文本

WPF centered text in stretched label fixed width

我的目标是 simpel,但是我想不通。对标题感到抱歉,但无法提出更好的解释...

我有一个带有显示当前时间标签的用户控件(连接到一个间隔为 1 秒的计时器)。标签是其 parent 的宽度,文本居中对齐。格式为DateTime.ToString("HH : mm : ss"),FontFamily和大小可由用户自行调整。到目前为止没有什么奇怪的......但是,文本居中对齐所以当时间是 12:34:02 像素宽度不同于 12:34:11。 (当然取决于字体)这会导致标签跳转(因为它会自动居中)

下面的代码就是一个例子。 canvas 用于在其上绘制内容并使用视框,因此它在他的 parent.

中自动调整自身大小

代码:

 <Grid>
    <Viewbox>
        <Canvas Name="canv" Height="300" Width="300">
            <StackPanel Name="stckpnlDateTime">

                <Label Name="lblDateOrText" 
                       Grid.Column="0"
                       Grid.Row="0"
                       Content = "------" 
                       FontSize="25"
                       Foreground="GhostWhite"
                       HorizontalContentAlignment="Center"
                       VerticalAlignment="Bottom"
                       FontFamily="Arial"
                       Width="Auto"/>

                <Label Name="lblTime"
                       Grid.Column="0"
                       Grid.Row="1"
                       Content = "-- : -- : --"
                       FontSize="25"
                       Foreground="GhostWhite"
                       HorizontalContentAlignment="Center"
                       VerticalAlignment="Top"
                       FontFamily="DS-Digital"
                       Width="Auto"/>
            </StackPanel>                
        </Canvas>
    </Viewbox>
</Grid>

Private Sub SystemTime_Tick(sender As Object, e As EventArgs) Handles tmrSystemTime.Tick
    lblTime.Content = Now.ToString("HH : mm : ss")
End Sub

所以我尝试了一种不同的方法,一个有 10 列和 8 个标签的网格,每个字符一个,并将标签拉伸到它的 parent(单元格)。这有效并将字符保持在固定位置。但是最后一列的宽度比其余的要小......在这张图片中你可以看到我的意思,第二个紫色列就是我的意思。 Example alignment

代码:

 <UserControl.Resources>
    <Style x:Key="LabelStyle" TargetType="Label">
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontFamily" Value="DS-Digital" />
        <Setter Property="FontSize" Value="40"/>
        <Setter Property="HorizontalAlignment" Value="Center"/>
        <Setter Property="HorizontalContentAlignment" Value="Right"/>
        <Setter Property="Background" Value="Green" />
    </Style>
</UserControl.Resources>

<Grid HorizontalAlignment="Stretch">
    <Viewbox HorizontalAlignment="Stretch">
        <Canvas Name="canv" Height="300" Width="300" HorizontalAlignment="Stretch">
            <StackPanel Name="stckpnlDateTime" HorizontalAlignment="Stretch">                    
                <Label Name="lblDateOrText" 
                       Grid.Column="0"
                       Grid.Row="0"
                       Content = "" 
                       FontSize="25"
                       Foreground="GhostWhite"
                       HorizontalContentAlignment="Center"
                       VerticalAlignment="Bottom"
                       FontFamily="Arial"
                       Width="Auto"/>

                <Grid Name="GridTimeLabel" HorizontalAlignment="Stretch"  Width="Auto" Grid.Column="0" Grid.Row="1">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="1*"/>
                    </Grid.ColumnDefinitions>


                    <Label Background="Purple" Grid.Column="0" Grid.Row="0"/>
                    <Label Name="lblTime1" Grid.Column="1" Grid.Row="0" Style="{StaticResource LabelStyle}" Content="-"/>
                    <Label Name="lblTime2" Grid.Column="2" Grid.Row="0" Style="{StaticResource LabelStyle}" Content="-"/>
                    <Label Name="lblTime3" Grid.Column="3" Grid.Row="0" Style="{StaticResource LabelStyle}" Content=":"/>
                    <Label Name="lblTime4" Grid.Column="4" Grid.Row="0" Style="{StaticResource LabelStyle}" Content="-"/>
                    <Label Name="lblTime5" Grid.Column="5" Grid.Row="0" Style="{StaticResource LabelStyle}" Content="-"/>
                    <Label Name="lblTime6" Grid.Column="6" Grid.Row="0" Style="{StaticResource LabelStyle}" Content=":"/>
                    <Label Name="lblTime7" Grid.Column="7" Grid.Row="0" Style="{StaticResource LabelStyle}" Content="-"/>
                    <Label Name="lblTime8" Grid.Column="8" Grid.Row="0" Style="{StaticResource LabelStyle}" Content="-"/>
                    <Label Background="Purple" Grid.Column="9" Grid.Row="0"/>
                </Grid>                 
            </StackPanel>                
        </Canvas>
    </Viewbox>
</Grid>

长话短说,我很困惑....希望有人能给我指出正确的方向。

我想到了两种方法。

第一种方法: 更改为 monospace 字体,这将确保无论您输入什么时间值,宽度都是恒定的。但是,您可能无法使用此方法找到 good/suitable 字体。

第二种方法: 如果你没有使用 MVVM,试试这个(C# 代码,我不太习惯VB.net):

// Code-behind
public override void OnApplyTemplate()
{
    base.OnApplyTemplate();
    var label = this.lblTime;
    label.Text = "00:00:00";
    label.Measure(); // Force it to measure
    label.Width = label.DesiredSize.Width;
}

这将强制宽度保持不变。根据字体的不同,需要手动设置占用最多的时间值space.

此外,为确保其正常工作,您可能需要将 Label 包装在 Grid 中。使 Grid 有 3 列,在第 1 列(中间列)中设置标签,并将列定义的宽度依次设置为 *auto*

您的 Canvas 宽度为 300。在我看来,可用空间不足 300 像素。这就是最后一列较小的原因。

@Jai,感谢您为我指明 .Measure() Sub 的方向。在四处摆弄之后,我最终得到了 3 列网格,用新内容测量标签的大小,设置标签的大小。这会导致 Column 重新对齐,从而将标签固定到位。

代码:(新建WPF程序测试,颜色是为了看child和parent的区别)

    <Grid Background="Tomato">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <Label Name="lblTime"
                   Grid.Column="1"
                   Grid.Row="0"
                   Content = "-- : -- : --"
                   FontSize="50"
                   Foreground="Black"
                   Background="Beige"
                   HorizontalContentAlignment="Left"
                   VerticalAlignment="Center"
                   FontFamily="DS-Digital"/>
</Grid>

及其背后的代码:

Class 主窗口

''' <summary>
''' Timer for updating the time Clock
''' </summary>
Dim WithEvents tmrSystemTime As New DispatcherTimer With {.Interval = TimeSpan.FromSeconds(1)} 'Set Timer interval on every second.

Sub New()

    ' This call is required by the designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.
    tmrSystemTime.Start()
End Sub

Private Sub SystemTime_Tick(sender As Object, e As EventArgs) Handles tmrSystemTime.Tick
    'Set the time in the label
    lblTime.Content = Now.ToString("HH : mm : ss")

    'Measure and set the size of the label
    MeasureSizeTimeLabel()
End Sub

''' <summary>
''' Measure the Max Size of the label with a specific Format
''' </summary>
Private Sub MeasureSizeTimeLabel()

    'Store the Max size of the Time Label in this variable
    Dim MaxClockSize As Size

    'Measure the Max size of the clock label and use this width
    'lblTime.Content = "00 : 00 : 00"
    lblTime.Measure(New Size(Double.PositiveInfinity, Double.PositiveInfinity))
    MaxClockSize = lblTime.DesiredSize

    'Now Set the size of the label
    lblTime.Width = MaxClockSize.Width
    lblTime.Height = MaxClockSize.Height
End Sub

感谢大家的帮助,感谢付出的努力和时间! :-)