Выровнять нижнюю часть текста в элементах управления - PullRequest
16 голосов
/ 10 января 2010

Следующий фрагмент:

<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. Есть ли что-то еще, о чем я не знаю?

Я прочитал этот пост , но в нем не говорится о сценарии с другим размером шрифта.

ОБНОВЛЕНИЕ : Проблема в том, что даже для Padding установлено значение 0, в пределах области ContentPresenter все еще остается неопределенное пространство вокруг шрифта. это пространство зависит от размера шрифта. Если бы я мог контролировать это пространство, я был бы в лучшем положении.

Спасибо

Ответы [ 6 ]

23 голосов
/ 29 ноября 2010

Еще одно довольно простое решение:

1) Используйте элементы управления TextBlock вместо меток. Причина в том, что 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>
6 голосов
/ 10 января 2010

Не существует единственного решения XAML, вы должны использовать код позади. Кроме того, даже с выделенным кодом, для этого нет общего решения, потому что, если ваш текст многострочный? Какую базовую линию следует использовать в этом случае? Или что, если в вашем шаблоне несколько текстовых элементов? Таких как заголовок и контент, или больше, какой базовый уровень тогда?

Короче говоря, лучше всего выровнять текст вручную, используя верхние / нижние поля.

Если вы хотите сделать предположение, что у вас есть один текстовый элемент, вы можете определить расстояние в пикселях базовой линии от вершины элемента, создав экземпляр объекта FormattedText со всеми теми же свойствами объекта. существующий текстовый элемент. Объект FormattedText имеет свойство double Baseline, которое содержит это значение. Обратите внимание, что вам все равно придется вручную вводить поле, поскольку элемент может не располагаться точно в верхней или нижней части своего контейнера.

См. Это сообщение на форуме MSDN: Текстовое поле Baseline

Вот метод, который я написал, который извлекает это значение. Он использует отражение, чтобы получить соответствующие свойства, потому что они не являются общими для какого-либо одного базового класса (они определены отдельно в Control, TextBlock, Page, TextElement и, возможно, в других).

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

РЕДАКТИРОВАТЬ: Шимми, в ответ на ваш комментарий, я не думаю, что вы на самом деле пробовали это решение, потому что оно работает. Вот пример:

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);
5 голосов
/ 20 января 2010

Мне действительно нравятся креативные решения, которые представлены здесь, но я думаю, что в долгосрочной перспективе (каламбур) мы должны использовать это:

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

Единственное, что отсутствует в элементе Run, - это привязка данных свойства Text, но это может быть добавлено рано или поздно.

Выполнение не исправит выравнивание надписей и их текстовых полей, но для многих простых ситуаций выполнение будет очень приятным.

2 голосов
/ 28 марта 2013
<TextBlock>
<InlineUIContainer BaselineAlignment="Baseline"><TextBlock>Small</TextBlock></InlineUIContainer>
<InlineUIContainer BaselineAlignment="Baseline"><TextBlock FontSize="50">Big</TextBlock></InlineUIContainer>
</TextBlock>

Это должно хорошо работать. Эксперимент с базовой линией / дном / центром / вершиной.

1 голос
/ 20 ноября 2014

Конструктор XAML поддерживает выравнивание элементов управления TextBlock по базовой линии во время разработки:

enter image description here

Это назначает фиксированные поля вашим элементам управления. Пока размеры шрифта не меняются во время выполнения, выравнивание будет сохранено.

0 голосов
/ 11 января 2010

Я действительно нашел простой ответ, основанный на Aviad .

Я создал конвертер, который содержит функцию Aviad, которая принимает сам элемент и возвращает вычисленную толщину.

Тогда я настроил

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

Недостатком является то, что это, очевидно, занимает исходное свойство Margin, поскольку TextBlock не имеет шаблона, поэтому мы не можем установить его через TemplateBinding.

...