【问题标题】:How can I specify letter spacing or kerning, in a WPF TextBox?如何在 WPF 文本框中指定字母间距或字距?
【发布时间】:2011-08-16 03:49:34
【问题描述】:

我想修改 WPF 文本框中字符之间的间距。
类似于 CSS 中可用的 letter-spacing: 5px 之类的东西。
我认为在 XAML 中是可能的;最简单的方法是什么?

我找到了“Introduction to the GlyphRun Object and Glyphs Element”文档,发现它非常无用。

这是该页面的代码示例:

<!-- "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 属性的作用给出了以下“解释”:

我不知道这意味着什么。我也不确定 Indices 是否正确——代码中的注释谈到了我不关心的“字符宽度”。我想调整 个字符之间的宽度。

此外,没有关于如何将Glyphs 元素应用于文本框的示例。当我尝试它时,我的 WPF 测试应用程序崩溃了。


我想要做的是稍微增加 WPF 文本框中绘制字符之间出现的空白空间。文本的长度和内容会有所不同。每次有新角色时我都必须修改Indicies 属性吗?有没有办法说“每个角色都比平时多 20% 的空间”。

有人可以帮我吗?

【问题讨论】:

    标签: wpf xaml textbox kerning


    【解决方案1】:

    我尝试了 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>
    

    我可以绑定到任何字符串,并且不需要进行任何字符宽度检测来正确设置间距。右边距是字母之间的空格

    例子:

    【讨论】:

    • 这是一个不错的解决方案(我投了赞成票),但这肯定会导致性能问题。我们在某些地方使用了这个解决方案,但我们想在一个有几百个项目的ItemsControl 中使用它,这会增加渲染时间几秒钟。在这些情况下,我们回到了常规的TextBlock。所以请注意/小心!
    • 这也会破坏修剪或环绕文本的能力。
    【解决方案2】:

    我找到了一种使用TextBlock 类来设置字母间距的方法,因为它支持TranslateTransforms。通过将TextBlock.TextProperty 上的默认PropertyChangedCallback 替换为自定义@,我们可以将TranslateTransform 应用于TextBlock 中的每个字母。

    这是我完成的完整的分步编码:

    首先,我们创建一个自定义类并继承自TextBlock,如下所示:

    using System.Windows.Controls;
    
    namespace MyApp
    {
        class SpacedLetterTextBlock : TextBlock
        {
            public SpacedLetterTextBlock() : base()
            {
            }
        }
    }
    

    然后,在 XAML 中,我们将 TextBlock 更改为我们的自定义类(更多信息可以找到 here):

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

    最后,在.cs 代码隐藏文件中的InitializeComponent() 方法之前,添加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)
        )
    );
    

    ...并在每次TextProperty 更改时将TranslateTransform 应用于每个字母:

    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# 的完全新手,所以我的答案可能不是最干净的解决方案。如果您有任何关于如何改进此答案的 cmets,将不胜感激!

    其次,我还没有使用大量 TextBlock 元素测试此解决方案,并且(可能)存在巨大的性能损失,因为 TranslateTransform 应用于 TextBlock.Text 中的每个单独的字母。

    最后,TextBlock 的文本超出了TranslateTransform 的任何正的X 值的范围。我认为您可以重新计算TextBlock 的宽度,然后以编程方式放置它(?)

    【讨论】:

      【解决方案3】:

      FontStretch 适合您吗?

      否则您可能想查看this 有一张图片,显示了提前宽度的含义。虽然我以前没有这样做过,不知道这是否可行,增加左右侧轴承可能是你想要的!

      【讨论】:

        【解决方案4】:

        为了它的价值。 . .

        如果您可以选择将实现切换到 RichTextBox,这可能比您在 2013 年 9 月找到的解决方法更容易。我只是根据自己的需要尝试了它,它可以满足我的需要。我看不到 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");
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-21
          • 1970-01-01
          • 2016-09-13
          • 1970-01-01
          • 2013-04-21
          • 1970-01-01
          • 2011-05-20
          • 1970-01-01
          相关资源
          最近更新 更多