WPF TextFormatter中第二行的缩进

我正在使用TextFormatter制作WPF文本编辑器。 我需要在每个段落中缩进第二行。

第二行中的缩进宽度应该类似于第一行上第一个单词的宽度,包括第一个单词后面的空格。 像这样的东西:

Indent of second line in Indentation Inde second line in Indentation Indenta of second line in Indentation of second l ine in Indentation of second line in Inde ntation of second line in 

第二件事:段落的最后一行应该在中心。

如何实现这一目标?

提前致谢!!

这远非易事。 我建议你使用WPF的高级文本格式 。

有一个官方(相对较差,但它是唯一的)样本: TextFormatting 。

所以,我创建了一个带有文本框和特殊自定义控件的小型示例应用程序,它可以按照您想要的方式同时呈现文本框中的文本(好吧,几乎可以看到最后的评论)。

         

我选择编写一个自定义控件,但你也可以构建一个几何体(比如官方的’TextFormatting’样本)。

 [ContentProperty(nameof(Text))] public class MyTextControl : FrameworkElement { // I have only declared Text as a dependency property, but fonts, etc should be here public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyTextControl), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); private List _lines = new List(); private TextFormatter _formatter = TextFormatter.Create(); public string Text { get => ((string)GetValue(TextProperty)); set { SetValue(TextProperty, value); } } protected override Size MeasureOverride(Size availableSize) { // dispose old stuff _lines.ForEach(l => l.Dispose()); _lines.Clear(); double height = 0; double width = 0; var ts = new MyTextSource(Text); var index = 0; double maxWidth = availableSize.Width; if (double.IsInfinity(maxWidth)) { // it means width was not fixed by any constraint above this. // we pick an arbitrary value, we could use visual parent, etc. maxWidth = 100; } double firstWordWidth = 0; // will be computed with the 1st line while (index < Text.Length) { // we indent the second line var props = new MyTextParagraphProperties(new MyTextRunProperties(), _lines.Count == 1 ? firstWordWidth : 0); var line = _formatter.FormatLine(ts, index, maxWidth, props, null); if (_lines.Count == 0) { // get first word and whitespace real width (so we can support justification / whitespaces widening, kerning) firstWordWidth = line.GetDistanceFromCharacterHit(new CharacterHit(ts.FirstWordAndSpaces.Length, 0)); } index += line.Length; _lines.Add(line); height += line.TextHeight; width = Math.Max(width, line.WidthIncludingTrailingWhitespace); } return new Size(width, height); } protected override void OnRender(DrawingContext dc) { double height = 0; for (int i = 0; i < _lines.Count; i++) { if (i == _lines.Count - 1) { // last line centered (using pixels, not characters) _lines[i].Draw(dc, new Point((RenderSize.Width - _lines[i].Width) / 2, height), InvertAxes.None); } else { _lines[i].Draw(dc, new Point(0, height), InvertAxes.None); } height += _lines[i].TextHeight; } } } // this is a simple text source, it just gives back one set of characters for the whole string public class MyTextSource : TextSource { public MyTextSource(string text) { Text = text; } public string Text { get; } public string FirstWordAndSpaces { get { if (Text == null) return null; int pos = Text.IndexOf(' '); if (pos < 0) return Text; while (pos < Text.Length && Text[pos] == ' ') { pos++; } return Text.Substring(0, pos); } } public override TextRun GetTextRun(int index) { if (Text == null || index >= Text.Length) return new TextEndOfParagraph(1); return new TextCharacters( Text, index, Text.Length - index, new MyTextRunProperties()); } public override TextSpan GetPrecedingText(int indexLimit) => throw new NotImplementedException(); public override int GetTextEffectCharacterIndexFromTextSourceCharacterIndex(int index) => throw new NotImplementedException(); } public class MyTextParagraphProperties : TextParagraphProperties { public MyTextParagraphProperties(TextRunProperties defaultTextRunProperties, double indent) { DefaultTextRunProperties = defaultTextRunProperties; Indent = indent; } // TODO: some of these should be DependencyProperties on the control public override FlowDirection FlowDirection => FlowDirection.LeftToRight; public override TextAlignment TextAlignment => TextAlignment.Justify; public override double LineHeight => 0; public override bool FirstLineInParagraph => true; public override TextRunProperties DefaultTextRunProperties { get; } public override TextWrapping TextWrapping => TextWrapping.Wrap; public override TextMarkerProperties TextMarkerProperties => null; public override double Indent { get; } } public class MyTextRunProperties : TextRunProperties { // TODO: some of these should be DependencyProperties on the control public override Typeface Typeface => new Typeface("Segoe UI"); public override double FontRenderingEmSize => 20; public override Brush ForegroundBrush => Brushes.Black; public override Brush BackgroundBrush => Brushes.White; public override double FontHintingEmSize => FontRenderingEmSize; public override TextDecorationCollection TextDecorations => new TextDecorationCollection(); public override CultureInfo CultureInfo => CultureInfo.CurrentCulture; public override TextEffectCollection TextEffects => new TextEffectCollection(); } 

这是结果:

在此处输入图像描述

我没做过的事情:

  • 这不支持编辑,它不是文本框。 对于如此小的赏金来说,这是太多的工作:-)
  • 支持多段。 我刚刚缩小了样本中的第二行。 您需要解析文本以提取“段落”,无论您认为是什么。
  • 应添加DPI感知支持(适用于.NET Framework 4.6.2或更高版本)。 这是在’TextFormatting’示例中完成的,您基本上需要携带PixelsPerDip值。
  • 在某些边缘情况下会发生什么(只有两条线等)
  • 在自定义控件上公开常用属性(FontFamily等)