WPF centered text in stretched label fixed width

My goal is simpel, however i can't figure it out. Sorry for the Titel but couldn't come up with a better explanation...

I have usercontrol with an label that display's the current time (hooked up to a timer with 1 second interval). The label is the width of its parent and the text is aligned in the center. The format is DateTime.ToString("HH : mm : ss"), the FontFamily and size can be adjusted by the user. So far nothing strange... But, the text is aligned centered so when time is lets say 12:34:02 the pixel width different than 12:34:11. (of course depending on the font) This causes the label jump (because it auto centers itself)

The code below is an example of it. the canvas is used to draw stuff on it and the viewbox is used so it autosizes itself in his parent.

Code:

 <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

So i tried a different approach, great a grid with 10 columns and 8 labels, one for each char, and stretch the labels to its parent (cell). This works and keeps the chars on a fixed position. But the width of last column is smaller then the rest... In this image you can see hat i mean, the second purple column is what i mean. Example alignment

Code:

 <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>

Long story short, i'm stuk.... hopefully someone could point me in the right direction.


I have two methods in mind.

First method: Change to a monospace font, this will ensure that no matter what time value you throw at it, the width will be constant. But you may not find a good/suitable font using this method, though.

Second method: If you are not using MVVM, try this (C# code, I'm not too used to 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;
}

This will force the width to stay constant. Depending on the font, you need to manually set the time value that takes up the most space.

Also, to be sure it works properly, you may need to wrap the Label in a Grid. Make the Grid have 3 columns, set the label in column 1 (middle column), and set the columndefinitions' widths to * , auto and * respectively in that order.


Your Canvas has a width of 300. It looks to me as there is less than 300 px available. This is why the last column is smaller.


@Jai, thanks for pointing me in the direction of the .Measure() Sub. After fiddling around i ended up with the 3 column grid, measuring the size of the label with the new content, setting the size of the label. This causes the Column to realign, which holds the label in place.

The code: (created new WPF program for the test, The colors are to see the difference between child and 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>

And the code behind it:

Class MainWindow

''' <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

Thanks all for the help, appreciate the effort and time! :-)

链接地址: http://www.djcxy.com/p/53314.html

上一篇: 迫使Maven将依赖项复制到target / lib中

下一篇: WPF居中文本拉伸标签固定宽度