问题
The following snippet:
<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>
Renders the following:

Is there any way I can set in the Labels' styles so that their text bottoms should be aligned?
I have the same question with TextBlocks as well.
NOTE: since I've been struggling with this issue for a while, please post only certains answers that you know that work.
I already tried: VerticalAlignment, VerticalContentAlignment, Padding, Margin. Is there anything else I am not aware of?
I've read this post, but it doesn't talk about a scenario of different font size.
UPDATE: The problem is, that even Padding is set to 0 there is still an indeterminate space around the font, within the ContentPresenter area. this space varies on the font size. If I could control this space I would be in a better situation.
Thanks
回答1:
There is no XAML only solution, you have to use code behind. Also, even with code-behind, there's no general solution for this, because what if your text is multi-line? Which baseline should be used in that case? Or what if there are multiple text elements in your template? Such as a header and a content, or more, which baseline then?
In short, your best bet is to align the text manually using top/bottom margins.
If you're willing to make the assumption that you have a single text element, you can figure out the pixel distance of the baseline from the top of the element by instantiating a FormattedText
object with all the same properties of the existing text element. The FormattedText
object has a double
Baseline
property which holds that value. Note that you still would have to manually enter a margin, because the element might not sit exactly against the top or bottom of its container.
See this MSDN forum post: Textbox Baseline
Here's a method I wrote that extracts that value. It uses reflection to get the relevant properties because they are not common to any single base class (they are defined separately on Control
, TextBlock
, Page
, TextElement
and maybe others).
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;
}
EDIT: Shimmy, in response to your comment, I don't believe you've actually tried this solution, because it works. Here's an example:

Here's the 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>
And here's the code behind that achieves this
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:
Another fairly simple solution:
1) Use TextBlock controls instead of Labels. The reason being that TextBlock is lighter weight than Label - see http://joshsmithonwpf.wordpress.com/2007/07/04/differences-between-label-and-textblock/
2) Use the LineHeight and LineStackingStrategy = BlockLineHeight for your TextBlock style. This will align the two at their baseline easily.
<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>
回答3:
I really like the creative solutions that are presented here but I do think that in the long run (pun intended) we should use this:
<TextBlock>
<Run FontSize="20">What</Run>
<Run FontSize="36">ever</Run>
<Run FontSize="12" FontWeight="Bold">FontSize</Run>
</TextBlock>
The only thing that is missing from the Run element is databinding of the Text property but that might be added sooner or later.
A Run will not fix the alignment of labels and their textboxes but for many simple situation the Run will do quite nicely.
回答4:
<TextBlock>
<InlineUIContainer BaselineAlignment="Baseline"><TextBlock>Small</TextBlock></InlineUIContainer>
<InlineUIContainer BaselineAlignment="Baseline"><TextBlock FontSize="50">Big</TextBlock></InlineUIContainer>
</TextBlock>
This should works well. Experiment with Baseline/Bottom/Center/Top.
回答5:
XAML designer supports aligning TextBlock
controls by baseline at design time:

This assigns fixed margins to your controls. As long as font sizes do not change at run time, the alignment will be preserved.
回答6:
I actually found a simple answer based on Aviad's.
I created a converter that contains Aviad's function that accepts the element itself and returns calculated Thickness.
Then I set up
<Style TargetType="TextBlock">
<Setter Property="Margin"
Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource converters:TextAlignmentMarginConverter}}" />
</Style>
The disadvantage is that this is obviously occupies the original Margin property, since a TextBlock doesn't have a template so we can't set it via TemplateBinding.
来源:https://stackoverflow.com/questions/2035943/align-bottoms-of-text-in-controls