对齐控件中的文本底部

时间:2010-01-10 03:09:20

标签: wpf layout textblock

以下代码段:

<Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <StackPanel Orientation="Horizontal"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Center">            
            <Label Content="Name:"/>
            <Label Content="Itzhak Perlman" FontSize="44"/>
        </StackPanel>
    </Grid>
</Window>

呈现以下内容:
alt text

我有什么方法可以设置标签的样式,以便它们的文本底部对齐吗? 我对TextBlocks也有同样的问题。

注意:由于我一直在努力解决这个问题,请只发布您知道有效的确定答案。
我已经尝试过:VerticalAlignment,VerticalContentAlignment,Padding,Margin。还有什么我不知道的吗?

我已阅读this帖子,但它没有谈到不同字体大小的情况。

UPDATE :问题是,即使Padding设置为0,在ContentPresenter区域内的字体周围仍然存在不确定的空间。这个空间因字体大小而异。 如果我可以控制这个空间,我会处于更好的状态。

由于

7 个答案:

答案 0 :(得分:23)

另一个相当简单的解决方案:

1)使用TextBlock控件而不是Labels。原因是TextBlock比Label更轻 - 请参阅http://joshsmithonwpf.wordpress.com/2007/07/04/differences-between-label-and-textblock/

2)使用LineHeight和LineStackingStrategy = BlockLineHeight作为TextBlock样式。这将使两者在基线处轻松对齐。

<StackPanel Orientation="Horizontal"
            VerticalAlignment="Center"
            HorizontalAlignment="Center">
    <StackPanel.Resources>
        <Style TargetType="TextBlock">
            <Setter Property="LineHeight" Value="44pt"/>
            <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
        </Style>
    </StackPanel.Resources>            
    <TextBlock Text="Name:"/>
    <TextBlock Text="Itzhak Perlman" FontSize="44"/>
</StackPanel>

答案 1 :(得分:6)

没有XAML唯一解决方案,您必须使用后面的代码。此外,即使使用代码隐藏,也没有通用的解决方案,因为如果您的文本是多行的呢?在这种情况下应该使用哪个基线?或者,如果模板中有多个文本元素,该怎么办?如标题和内容,或更多,哪个基线呢?

简而言之,最好的办法是使用上/下边距手动对齐文本。

如果您愿意假设您有一个文本元素,则可以通过实例化具有所有相同属性的FormattedText对象来确定基线与元素顶部的像素距离现有文本元素。 FormattedText对象具有double Baseline属性,该属性保存该值。请注意,您仍然需要手动输入边距,因为该元素可能不会完全位于其容器的顶部或底部。

请参阅此MSDN论坛帖子:Textbox Baseline

这是我写的一个提取该值的方法。它使用反射来获取相关属性,因为它们对于任何单个基类都不常见(它们在ControlTextBlockPageTextElement以及其他基类上单独定义)。

public double CalculateBaseline(object textObject)
{
    double r = double.NaN;
    if (textObject == null) return r;

    Type t = textObject.GetType();
    BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public;

    var fontSizeFI = t.GetProperty("FontSize", bindingFlags);
    if (fontSizeFI == null) return r;
    var fontFamilyFI = t.GetProperty("FontFamily", bindingFlags);
    var fontStyleFI = t.GetProperty("FontStyle", bindingFlags);
    var fontWeightFI = t.GetProperty("FontWeight", bindingFlags);
    var fontStretchFI = t.GetProperty("FontStretch", bindingFlags);

    var fontSize = (double)fontSizeFI.GetValue(textObject, null);
    var fontFamily = (FontFamily)fontFamilyFI.GetValue(textObject, null);
    var fontStyle = (FontStyle)fontStyleFI.GetValue(textObject, null);
    var fontWeight = (FontWeight)fontWeightFI.GetValue(textObject, null);
    var fontStretch = (FontStretch)fontStretchFI.GetValue(textObject, null);

    var typeFace = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);

    var formattedText = new FormattedText(
        "W", 
        CultureInfo.CurrentCulture, 
        FlowDirection.LeftToRight, 
        typeFace, 
        fontSize, 
        Brushes.Black);

    r = formattedText.Baseline;

    return r;
}

编辑:Shimmy,在回复你的评论时,我不相信你真的尝试过这个解决方案,因为它有效。这是一个例子:

Example Baseline Alignment

这是XAML:

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="TextBlock">
            <Setter Property="Margin" Value="0,40,0,0"/>
        </Style>
    </StackPanel.Resources>
    <StackPanel Orientation="Horizontal">
        <TextBlock Name="tb1" Text="Lorem " FontSize="10"/>
        <TextBlock Name="tbref" Text="ipsum"/>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBlock Name="tb2" Text="dolor "  FontSize="20"/>
        <TextBlock Text="sit"/>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBlock Name="tb3" Text="amet "  FontSize="30"/>
        <TextBlock Text="consectetuer"/>
    </StackPanel>
</StackPanel>

这是实现此目的背后的代码

double baseRef = CalculateBaseline(tbref);
double base1 = CalculateBaseline(tb1) - baseRef;
double base2 = CalculateBaseline(tb2) - baseRef;
double base3 = CalculateBaseline(tb3) - baseRef;
tb1.Margin = new Thickness(0, 40 - base1, 0, 0);
tb2.Margin = new Thickness(0, 40 - base2, 0, 0);
tb3.Margin = new Thickness(0, 40 - base3, 0, 0);

答案 2 :(得分:5)

我非常喜欢这里展示的创意解决方案,但我认为从长远来看(双关语)我们应该使用这个:

<TextBlock>
   <Run FontSize="20">What</Run>
   <Run FontSize="36">ever</Run>
   <Run FontSize="12" FontWeight="Bold">FontSize</Run>
</TextBlock>

Run元素中唯一缺少的是Text属性的数据绑定,但可能迟早会添加。

A Run不会修复标签及其文本框的对齐方式,但对于许多简单的情况,Run会做得很好。

答案 3 :(得分:2)

<TextBlock>
<InlineUIContainer BaselineAlignment="Baseline"><TextBlock>Small</TextBlock></InlineUIContainer>
<InlineUIContainer BaselineAlignment="Baseline"><TextBlock FontSize="50">Big</TextBlock></InlineUIContainer>
</TextBlock>

这应该很有效。使用Baseline / Bottom / Center / Top进行实验。

答案 4 :(得分:1)

XAML设计器支持在设计时按基线对齐TextBlock控件:

enter image description here

这会为您的控件指定固定边距。只要字体大小在运行时不变,就会保留对齐方式。

答案 5 :(得分:0)

我实际上找到了基于Aviad的简单答案。

我创建了一个包含Aviad函数的转换器,该函数接受元素本身并返回计算的厚度。

然后我设置

<Style TargetType="TextBlock">
    <Setter Property="Margin" 
        Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource converters:TextAlignmentMarginConverter}}" />
</Style>

缺点是这显然占用了原始的Margin属性,因为TextBlock没有模板,因此我们无法通过TemplateBinding设置它。

答案 6 :(得分:0)

在博客文章XAML text improvements in Windows 8.1中,介绍了如何根据基准对齐两个不同字体大小的TextBlock。诀窍是将TextLineBounds="TrimToBaseline"VerticalAlignment="Bottom"结合使用。这样可以将尺寸减小到基线以下,然后将TextBlock下移。然后,您可以通过在放入容器的容器上设置Margin,将它们移回所需的高度。

示例:

<Grid Margin="some margin to lift the TextBlocks to desired height">
    <TextBlock Text="{x:Bind ViewModel.Name, Mode=OneWay}"
                Style="{StaticResource HeaderTextBlockStyle}"
                VerticalAlignment="Bottom"
                TextLineBounds="TrimToBaseline" />
    <TextBlock Text="{x:Bind ViewModel.Description.Yield, Mode=OneWay}"
                Style="{StaticResource SubheaderTextBlockStyle}"
                VerticalAlignment="Bottom"
                HorizontalAlignment="Right"
                TextLineBounds="TrimToBaseline" />
</Grid>