我正在使用avalon编辑编写自定义软件,我正在寻找一种方法来使线条之间的空间(高度)更大。目前,每当用户结束写一行并想要写另一行时,我就被迫添加一个空行。
我已经开始研究似乎计算defaultLineHeight的TextView
类,但我唯一能够影响的是视觉插入符号的高度,而不是内容本身。
目前我正在考虑让每一对线看不见但我希望有一种更简单的方法来实现在线之间添加更多空间的简单操作。
这是我正在检查的班级TextView
的方法。欢迎任何提示或提示。
void CalculateDefaultTextMetrics()
{
if (defaultTextMetricsValid)
{
return;
}
defaultTextMetricsValid = true;
if (formatter != null)
{
var textRunProperties = CreateGlobalTextRunProperties();
using (
var line = formatter.FormatLine(
new SimpleTextSource("x", textRunProperties),
0,
32000,
new VisualLineTextParagraphProperties { defaultTextRunProperties = textRunProperties },
null))
{
wideSpaceWidth = Math.Max(1, line.WidthIncludingTrailingWhitespace);
defaultBaseline = Math.Max(1, line.Baseline);
defaultLineHeight = Math.Max(1, line.Height);
}
}
else
{
wideSpaceWidth = FontSize / 2;
defaultBaseline = FontSize;
**defaultLineHeight = FontSize + 3; // bigger value only affects caret height :(**
}
// Update heightTree.DefaultLineHeight, if a document is loaded.
if (heightTree != null)
{
heightTree.DefaultLineHeight = defaultLineHeight;
}
}
由于
答案 0 :(得分:5)
DefaultLineHeight
是默认字体中一条线的高度,用作每条线高度的初始假设。 (例如,用于计算滚动条位置)
每当实际测量一条线(TextView.BuildVisualLine
)时,测量的高度将存储在高度树中,覆盖默认高度。这是因为自动换行(或改变字体大小的线性变换器)可能导致每一行具有不同的高度。
目前尚不支持行间距。如果您想添加它,可以尝试更改VisualLine
的高度计算,例如通过更改VisualLine.SetTextLines()
。
答案 1 :(得分:2)
包括@Peter Moore所说的内容。为了强制文本正确呈现,还需要一个步骤。
VisualLine.cs
的底部是VisualLineDrawingVisual
类,它负责绘制实际文本但无法访问TextView
类。
修改构造函数以包含double lineSpacing
作为参数,并将textLine.Height
的所有实例乘以lineSpacing
。
在VisualLine.Render()
中,将textView.LineSpacing
作为VisualLineDrawingVisual
的现在修改构造函数的辅助参数传递。在这之后一切都应该正确绘制。
以下是修改后代码的完整视图:
TextView.cs
public static readonly DependencyProperty LineSpacingProperty =
DependencyProperty.Register("LineSpacing", typeof(double), typeof(TextView),
new FrameworkPropertyMetadata(1.0));
public double LineSpacing {
get { return (double) GetValue(LineSpacingProperty); }
set { SetValue(LineSpacingProperty, value); }
}
VisualLine.cs
第269行
internal void SetTextLines(List<TextLine> textLines) {
this.textLines = textLines.AsReadOnly();
Height = 0;
foreach (TextLine line in textLines)
Height += line.Height * textView.LineSpacing;
}
第335行
public double GetTextLineVisualYPosition(TextLine textLine, VisualYPosition yPositionMode) {
if (textLine == null)
throw new ArgumentNullException("textLine");
double pos = VisualTop;
foreach (TextLine tl in TextLines) {
if (tl == textLine) {
switch (yPositionMode) {
case VisualYPosition.LineTop:
return pos;
case VisualYPosition.LineMiddle:
return pos + tl.Height / 2 * textView.LineSpacing;
case VisualYPosition.LineBottom:
return pos + tl.Height * textView.LineSpacing;
case VisualYPosition.TextTop:
return pos + tl.Baseline - textView.DefaultBaseline;
case VisualYPosition.TextBottom:
return pos + tl.Baseline - textView.DefaultBaseline + textView.DefaultLineHeight;
case VisualYPosition.TextMiddle:
return pos + tl.Baseline - textView.DefaultBaseline + textView.DefaultLineHeight / 2;
case VisualYPosition.Baseline:
return pos + tl.Baseline;
default:
throw new ArgumentException("Invalid yPositionMode:" + yPositionMode);
}
}
else {
pos += tl.Height * textView.LineSpacing;
}
}
throw new ArgumentException("textLine is not a line in this VisualLine");
}
第386行
public TextLine GetTextLineByVisualYPosition(double visualTop) {
const double epsilon = 0.0001;
double pos = this.VisualTop;
foreach (TextLine tl in TextLines) {
pos += tl.Height * textView.LineSpacing;
if (visualTop + epsilon < pos)
return tl;
}
return TextLines[TextLines.Count - 1];
}
第701行
internal VisualLineDrawingVisual Render() {
Debug.Assert(phase == LifetimePhase.Live);
if (visual == null)
visual = new VisualLineDrawingVisual(this, textView.LineSpacing);
return visual;
}
第714行
public VisualLineDrawingVisual(VisualLine visualLine, double lineSpacing) {
this.VisualLine = visualLine;
var drawingContext = RenderOpen();
double pos = 0;
foreach (TextLine textLine in visualLine.TextLines) {
textLine.Draw(drawingContext, new Point(0, pos), InvertAxes.None);
pos += textLine.Height * lineSpacing;
}
this.Height = pos;
drawingContext.Close();
}
答案 2 :(得分:1)
我知道这是一篇很老的帖子,但我刚才遇到了同样的问题。我设法通过以下修改很容易地添加行间距:
double
的{{1}}添加新的TextView
依赖项属性,其默认值为LineSpacing
1.0
中,将VisualLine.SetTextLines
乘以line.Height
textView.LineSpacing
和VisualLine.GetTextLineVisualYPosition
中,将VisualLine.GetTextLineByVisualYPosition
的每次出现乘以tl.Height
然后,您可以直接在代码中或通过自定义XAML样式等设置textView.LineSpacing
。
这似乎就是必要的。
PS - AvalonEdit太棒了!
答案 3 :(得分:1)
除了@Robert Jordan所说的,我还需要做出更多改变才能获得满意的结果。
首先,要使文本标记覆盖覆盖整个行的高度,请在BackgroundGeometryBuilder.cs中更改以下行
ProcessTextLines方法,第196行:
第215行:
yield return new Rect(pos, y, 1, line.Height * textView.LineSpacing);
第259行:
lastRect = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height * textView.LineSpacing);
第259行:
Rect extendSelection = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height * textView.LineSpacing);
我遇到的第二个问题是文本呈现在现在延伸的行高的顶部,而不是底部。要解决这个问题,需要在渲染线之前添加额外的空间,而不是之后。因此,请进行以下更改:
VisualLine.cs,第714行
public VisualLineDrawingVisual(VisualLine visualLine, double lineSpacing) {
this.VisualLine = visualLine;
var drawingContext = RenderOpen();
double pos = 0;
foreach (TextLine textLine in visualLine.TextLines) {
pos +=(textLine.Height * lineSpacing)-textLine.Height
textLine.Draw(drawingContext, new Point(0, pos), InvertAxes.None);
pos += textLine.Height;
}
this.Height = pos;
drawingContext.Close();
}
此外,我发现将其转换为支持动态逐行行间距是微不足道的。只需将LineSpacing属性移动到VisualLine,然后添加如下的VisualLineTransformer:
class LinePaddingTransformer : IVisualLineTransformer
{
public LinePaddingTransformer()
{
}
public void Transform(ITextRunConstructionContext context, IList<VisualLineElement> elements)
{
int index = context.VisualLine.FirstDocumentLine.LineNumber - 1;
//You need to calculate your padding from the line index somehow.
//for example create a list of objects with the spacing in them and pass to this transformer.
double spacing= MyCalculateLinePaddingMethod(index);
context.VisualLine.LineSpacing= spacing;
}
}
如果您要求仅在文本换行时将间距添加到第一行,则需要检查先前与LineSpacing属性相乘的每个位置,并检查TextLine是否是第一行中的第一行集合。