Как я могу указать межбуквенный интервал или кернинг в текстовом поле WPF?

Я хотел бы изменить интервал между символами в текстовом поле WPF.
Что-то вроде того, что доступно в CSS.
Я думаю, что это возможно в XAML; какой самый простой способ?

Я нашел документ «Введение в объект GlyphRun и элемент Glyphs», и нашел это чрезвычайно бесполезным.

Это пример кода с этой страницы:

<!-- "Hello World!" with explicit character widths for proportional font -->
<Glyphs 
   FontUri             = "C:\WINDOWS\Fonts\ARIAL.TTF"
   FontRenderingEmSize = "36"
   UnicodeString       = "Hello World!"
   Indices             = ",80;,80;,80;,80;,80;,80;,80;,80;,80;,80;,80"
   Fill                = "Maroon"
   OriginX             = "50"
   OriginY             = "225"
/>

На той же странице документации дается "объяснение" того, что делает свойство Indices:

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

Я понятия не имею, что все это значит. Я также не уверен, что индексы - это правильно - комментарий в коде говорит о «ширине символа», которая меня не волнует. Я хочу отрегулировать ширину между символами.

Кроме того, нет примера того, как применить элемент Glyphs к TextBox. Когда я попробовал, мое тестовое приложение WPF просто вылетело.


Я хочу немного увеличить пустое пространство, которое появляется между нарисованными символами в текстовом поле WPF TextBox. Текст будет различаться по длине и содержанию. Должен ли я изменять свойство Indicies каждый раз, когда появляется новый персонаж? Есть ли способ сказать: «Сделайте на 20% больше места, чем обычно, для каждого персонажа».

Кто-нибудь может мне помочь?


person Cheeso    schedule 30.04.2011    source источник


Ответы (4)


Я попробовал Glyphs и FontStretch и не смог легко получить желаемый результат. Я смог придумать подход, который подходит для моих целей. Может быть, это сработает и для других.

<ItemsControl ItemsSource="{Binding SomeString}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}" 
                       Margin="0,0,5,0"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Я могу привязаться к любой строке, и мне не нужно определять ширину символа, чтобы правильно установить интервал. Правое поле - это пространство между буквами.

Пример:

Кернинг

person BrianR    schedule 18.09.2013
comment
Это хорошее решение (я проголосовал за него), но это определенно может вызвать проблемы с производительностью. Мы используем это решение в некоторых местах, но мы хотели использовать его в ItemsControl с несколькими сотнями элементов, и это добавляло несколько секунд ко времени рендеринга. В тех сценариях мы вернулись к обычному TextBlock. Так что просто будь в курсе / осторожно! - person Matt Jacobi; 11.12.2014
comment
Это также нарушит возможность обрезки или переноса текста. - person vlaku; 12.06.2017

подходит ли вам FontStretch?

В противном случае вы можете изучить это там - изображение, показывающее, что означает ширина продвижения. Хотя я не делал этого раньше и не знаю, работает ли это увеличение правого и левого подшипников, возможно, вам нужно!

person Markus Hütter    schedule 30.04.2011

Я нашел способ установить интервал между буквами с помощью TextBlock, поскольку он поддерживает TranslateTransforms. Заменив PropertyChangedCallback по умолчанию в TextBlock.TextProperty на пользовательский, мы можем применить TranslateTransform к каждой букве в TextBlock.

Вот полное пошаговое кодирование, которое я сделал:

Сначала мы создаем собственный класс и наследуем от TextBlock следующим образом:

using System.Windows.Controls;

namespace MyApp
{
    class SpacedLetterTextBlock : TextBlock
    {
        public SpacedLetterTextBlock() : base()
        {
        }
    }
}

Затем в XAML мы меняем TextBlock на наш настраиваемый класс (дополнительную информацию можно найти здесь):

<Window x:Class="MyApp.MainWindow"
        ...
        xmlns:app="clr-namespace:MyApp">
   <Grid>
       <app:SpacedLetterTextBlock>
           Some Text
       </app:SpacedLetterTextBlock>
   </Grid>
</Window>

Наконец, перед методом InitializeComponent() в файле кода программной части .cs добавьте метод OverrideMetadata следующим образом:

// This line of code adds our own callback method to handle any changes in the Text
//   property of the TextBlock
SpacedLetterTextBlock.TextProperty.OverrideMetadata(
    typeof(SpacedLetterTextBlock),
    new FrameworkPropertyMetadata(null,
            FrameworkPropertyMetadataOptions.AffectsRender,
            new PropertyChangedCallback(OnTextChanged)
    )
);

... и применяйте TranslateTransform к каждой букве каждый раз, когда TextProperty изменяется:

private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    SpaceLettersOut(d);
}

// This method takes our custom text block and 'moves' each letter left or right by 
//  applying a TranslateTransform
private static void SpaceLettersOut(DependencyObject d)
{
    SpacedLetterTextBlock thisBlock = (SpacedLetterTextBlock)d;
            
    for (int i = 1; i <= thisBlock.Text.Length; i++)
    {
        // TranslateTransform supports doubles and negative numbers, so you can have
        //  whatever spacing you need - do see 'Notes' section in the answer for
        //  some limitations.
        TranslateTransform transform = new TranslateTransform(2, 0);
        TextEffect effect = new TextEffect();
        effect.Transform = transform;
        effect.PositionStart = i;
        effect.PositionCount = thisBlock.Text.Length;

        thisBlock.TextEffects.Add(effect);
        if (effect.CanFreeze)
        {
            effect.Freeze();
        }
    }
}

ПРИМЕЧАНИЯ:

Во-первых, я новичок в WPF и C #, поэтому мой ответ может быть не самым чистым из доступных решений. Если у вас есть какие-либо комментарии о том, как улучшить этот ответ, мы будем очень признательны!

Во-вторых, я не тестировал это решение с большим количеством TextBlock элементов, и это (вероятно) сильно снижает производительность, поскольку TranslateTransform применяется к каждой отдельной букве в TextBlock.Text.

Наконец, текст TextBlock выходит за пределы с любым положительным значением X для TranslateTransform. Думаю, можно пересчитать ширину TextBlock и только потом программно (?)

person Alisher Turubayev    schedule 04.07.2021

Для чего это стоит. . .

Если у вас есть возможность переключить вашу реализацию на RichTextBox, это может быть проще, чем обходной путь, который вы нашли в сентябре 2013 года. Я просто попробовал его для своих нужд, и он работает для того, что мне нужно. Я не вижу способа с помощью RichTextBox контролировать кернинг отдельных пространств, как это делают наборщики. Но TextBox ел дополнительные пробелы (объединяя несколько смежных пробелов в одно пространство), как HTML. Мне нужно, чтобы в пробелах отображался такой же интервал, как в моей текстовой строке, и RichTextBox делает это.

<RichTextBox x:Name="MyRichTextBox"></RichTextBox>

Я не эксперт, но, похоже, вы не можете указать текстовое содержимое в XAML. Мне пришлось указать это в событии кода программной части:

this.MyRichTextBox.AppendText("V A R I E D      S P A C E S");
person philologon    schedule 05.05.2014