Выровнять нижнюю часть текста в элементах управления

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

<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 остается неопределенное пространство. это пространство зависит от размера шрифта. Если бы я мог контролировать это пространство, я был бы в лучшем положении.

Спасибо


person Shimmy Weitzhandler    schedule 10.01.2010    source источник
comment
ах хаа! Ваше обновление проясняет проблему.   -  person Steve Psaltis    schedule 10.01.2010


Ответы (7)


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

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

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

См. Это сообщение на форуме MSDN: Textbox 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;
}

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

Пример выравнивания базовой линии

Вот 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);
person Aviad P.    schedule 10.01.2010
comment
@ אביעד, а что мне делать с этим значением? что на самом деле такое «Базовая линия»? - person Shimmy Weitzhandler; 10.01.2010
comment
Базовая линия - это расстояние между верхом текстового элемента и линией, на которой «сидят» все буквы. Если, например, вы установите для поля Top значение 50 минус смещение базовой линии, вы гарантированно выровняетесь по определенной строке независимо от размера шрифта. - person Aviad P.; 10.01.2010
comment
Это то, что я пробовал, но не работает. Я все еще ищу решение. Проблема заключается в промежутке между полем и «верхней частью текста», о котором вы говорите. Я обновил свой вопрос. Спасибо за все ваши усилия אביעד. Какие-нибудь Новости? - person Shimmy Weitzhandler; 10.01.2010
comment
Большое спасибо, Авиад. Я хотел бы сделать все мои текстовые элементы управления (особенно TextBlocks, чтобы пройти через эту процедуру, есть ли способ Xamly установить его глобально, или мне нужно будет унаследовать эти элементы управления? Я подумал, давайте спросим экспертов, вы такой человек ! - person Shimmy Weitzhandler; 11.01.2010
comment
Вы должны использовать код позади, более того, вам нужно настроить поля в зависимости от конкретного шаблона элемента управления вашего текстового элемента. Т.е. если у него есть границы, отступы и т. д., вы должны изменить код, чтобы учесть это. - person Aviad P.; 11.01.2010
comment
По-прежнему существует проблема, потому что, если бы мы могли знать фактическую высоту родительского элемента, мы могли бы заменить это «40», которое является просто постоянным значением по умолчанию, на динамический элемент управления размером шрифта самого высокого размера. И проблема в том, что ActualSize родительского элемента отображается только после дочерних элементов, и вы не можете выполнять итерацию через братьев и сестер TextBlock, поскольку, когда вы выполняете преобразование, а итератор находится на 1-м дочернем элементе, другие братья и сестры еще не отображаются и не не существует. Есть хорошая идея? Мне бы очень хотелось сделать эту вещь более динамичной, спасибо за все. - person Shimmy Weitzhandler; 11.01.2010
comment
На самом деле 40 - это просто постоянное смещение для базовой линии, оно вам действительно не нужно, вы можете просто заменить его на 0. - person Aviad P.; 11.01.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>
person TI82    schedule 29.11.2010
comment
@ Шимми, ты пробовал это? Это просто и эффективно, и это должно быть ответом. У меня хорошо работает. - person ygoe; 18.08.2016

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

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

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

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

person Erno de Weerd    schedule 20.01.2010
comment
Имейте в виду, что во многих случаях метки предпочтительнее для упрощения применения стилей к меткам без применения к текстовым блокам. Учитывая пример оригинального плаката «Имя: ВАШЕ ИМЯ ЗДЕСЬ», было бы более правильным использовать метку для хранения текста Имя :. - person jpierson; 25.08.2010

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

Это должно сработать. Поэкспериментируйте с Baseline / Bottom / Center / Top.

person HelloSam    schedule 28.03.2013

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

введите описание изображения здесь

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

person Michael Diomin    schedule 20.11.2014
comment
Да, это работает. Возможно, эта функция не была доступна в то время, когда был задан вопрос (2010 г.), но я подтверждаю, что выравнивание может быть выполнено таким образом в конструкторе WPF в Viusal Studio 2015 (и, очевидно, также в некоторых более ранних версиях, как указывает дата этого ответа). Оба настроенных объекта должны содержать текст, поэтому при выравнивании, например, Label и TextBox (который обычно пуст), временно добавьте в него текст. - person miroxlav; 24.01.2017

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

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

Затем я настроил

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

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

person Shimmy Weitzhandler    schedule 11.01.2010
comment
Это хороший способ, но конвертер проведет вас только частично, а не полностью, потому что, например, если размер шрифта изменится во время запуска программы, свойство поля не будет автоматически переоценено, вам придется сделать это явно. Кроме того, конвертер должен учитывать другие элементы шаблона, такие как отступы, границы и т. Д., Поэтому вам все равно потребуется ручная настройка (возможно, параметр конвертера). - person Aviad P.; 11.01.2010
comment
Как видите, я передаю все управление конвертеру, поэтому параметры params не нужны. Во всяком случае, я надеюсь улучшить его, когда у меня будет время. Я хочу, чтобы он был основан на высоте родителя, поэтому он не должен быть статическим значением, таким как «40». - person Shimmy Weitzhandler; 12.01.2010

В статье блога XAML улучшения текста в Windows 8.1 объясняется, как вы можете выровнять два TextBlock шрифта разного размера по их базовой линии. Уловка TextLineBounds="TrimToBaseline" в сочетании с VerticalAlignment="Bottom". Это удаляет размер ниже их базовой линии, а затем перемещает TextBlocks вниз. Затем вы можете переместить их обратно на желаемую высоту, установив 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>
person hansmbakker    schedule 25.09.2020