For starters, try the following:
- Wrap your text in a container in order to get
ActualHeight
, because that's not comming from a RowDefinition
- Add
TextBlock.LineHeight
and TextBlock.LineStackingStrategy
settings to the label (taken idea from https://stackoverflow.com/a/7568216/5265292)
- Experiment with different fonts... for example, when I set
FontFamily="Arial"
, I get different spacing details than with the default font.
.
<Border
x:Name="container1"
Grid.Row="1" Grid.Column="1"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Padding="0" Margin="0" BorderThickness="0">
<Label
Name ="FileLocationLabel"
Content="TESTg"
Foreground="Black" Background="Beige"
FontWeight="Bold"
HorizontalAlignment="Left"
Padding="0" Margin="0"
TextBlock.LineHeight="{Binding ElementName=container1,Path=ActualHeight}"
TextBlock.LineStackingStrategy="BlockLineHeight"
FontSize="{Binding ElementName=container1,Path=ActualHeight}"/>
</Border>
If you need more exact font sizing, you may want to compute the actual text size for specific font settings, similar to this answer https://stackoverflow.com/a/15982149/5265292
Note I added a "g"
to the test content because it is using the lower part of text space, that would stay unused with "TEST"
I decided to take a closer look at the problem and came up with the following converter to determine a font size and lineheight in order to support different texts and fonts.
/// <summary>
/// Values:
/// - text to be rendered
/// - available height
/// - framework element with values for fontfamily etc
///
/// By default, returns a font size for a given text to fit into available height
/// With parameter = "lineheight", produces the lineheight for a given text and font size
/// </summary>
public class FontSizeProvider : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string text = values[0] as string;
double height = System.Convert.ToDouble(values[1]);
FrameworkElement f = values[2] as FrameworkElement;
if (height <= 0.0)
{
height = 10;
}
var param = parameter as string;
// (1) in order to compute the LineHeight, take the font size, produce formatted text
// and determine the LineHeight based on available space and the size of text below the baseline
// (2) in order to compute the fontsize, take the container height, produce formatted text
// and scale the font depending on the actual text size
double fsize = param == "lineheight" ? TextBlock.GetFontSize(f) : height;
FontFamily family = TextBlock.GetFontFamily(f);
FontStyle style = TextBlock.GetFontStyle(f);
FontWeight weight = TextBlock.GetFontWeight(f);
FontStretch stretch = TextBlock.GetFontStretch(f);
// produce the formatted text
var formattedText = new FormattedText(text, culture,
f.FlowDirection,
new Typeface(family, style, weight, stretch),
fsize, Brushes.Black);
// get the result
if (param == "lineheight")
{
var afterBaseline = formattedText.Height - formattedText.Baseline + formattedText.OverhangAfter;
// for some reason, the desired line height needs to be scaled according to the font family line sizes
var scalingFactor = family.LineSpacing / family.Baseline;
var lineHeightResult = (height - afterBaseline) * scalingFactor;
return System.Convert.ChangeType(lineHeightResult, targetType);
}
else
{
var fontSizeResult = height * height / formattedText.Extent;
return System.Convert.ChangeType(fontSizeResult, targetType);
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Instance in resources:
<local:FontSizeProvider x:Key="fontSizeProvider"/>
Usage:
<Border x:Name="container1"
Grid.Row="1" Grid.Column="1"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Padding="0" Margin="0" BorderThickness="0">
<Label
Content="TÉ"
Foreground="Black" Background="Beige"
HorizontalAlignment="Left"
Padding="0" Margin="0"
TextBlock.LineStackingStrategy="BlockLineHeight">
<Label.FontSize>
<MultiBinding Converter="{StaticResource fontSizeProvider}">
<Binding RelativeSource="{RelativeSource Self}" Path="Content"/>
<Binding ElementName="container1" Path="ActualHeight"/>
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</Label.FontSize>
<TextBlock.LineHeight>
<MultiBinding Converter="{StaticResource fontSizeProvider}" ConverterParameter="lineheight">
<Binding RelativeSource="{RelativeSource Self}" Path="Content"/>
<Binding ElementName="container1" Path="ActualHeight"/>
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</TextBlock.LineHeight>
</Label>
</Border>
The font size is scaled so that the actual text with the actual font is enlarged to fill the height of the container. This means, for the same container height, the font size will be different between text "Ég"
(smaller font, since text is using much height) and "ace"
(larger font since all letters are small height).
At computing the LineHeight
I ran into a strange problem: I had to multiply the available height by 1.22
(for Segoe UI font) in order to make the baseline of text appear at the bottom of the available space. This didn't really work for some other fonts. After some testing I found out that the right scaling factor per font is given by fontfamily.LineSpacing / fontfamily.Baseline
.
With the scaling factor problem solved, the approach is pretty straight forward: compute the height of text that is rendered below the baseline and adjust the lineheight according to text height minus the extra space below.