RichEdit 2.0 使用单个 CR 字符作为换行符会引发 SelStart 计算(Delphi XE2)
Posted
技术标签:
【中文标题】RichEdit 2.0 使用单个 CR 字符作为换行符会引发 SelStart 计算(Delphi XE2)【英文标题】:RichEdit 2.0's usage of single CR character as linebreak throws off SelStart calculations (Delphi XE2) 【发布时间】:2012-01-04 10:40:03 【问题描述】:当从 Delphi 2006 过渡到 Delphi XE2 时,我们学到的一件事是 RichEdit 2.0 在内部将 CRLF
对替换为单个 CR
字符。这会导致基于 VCL 端的实际文本字符串的所有字符索引计算都被丢弃。
我通过跟踪VCL代码可以看到的行为如下:
-
发送
WM_GETTEXT
消息(在TControl.GetTextBuf
中完成)将返回一个包含CRLF
对的文本缓冲区。
发送WM_GETTEXTLENGTH
消息(在TControl.GetTextLen
中完成)将返回一个值,就好像文本仍然包含CRLF
字符一样。
相比之下,发送EM_SETSELEX
消息(即设置SelStart
)会将输入值视为文本包含仅 CR
字符。
这会导致我们的应用程序中的各种事情(例如语法高亮)失败。正如你所知道的,到那时为止,每一条新线都恰好偏离了一个字符。
显然,由于这是不一致的行为,我们一定是遗漏了什么或做了一些非常错误的事情。
是否有其他人对从 RichEdit 1.0 到 RichEdit 2.0 控件的转换有任何经验,您是如何解决这个问题的?最后,有什么办法可以强制 RichEdit 2.0 像 RichEdit 1.0 一样使用 CRLF
对?
【问题讨论】:
恐怕您必须遵守这一点,因为EM_SETSEL
是通用编辑控制消息,而不是富编辑专用消息。
据我所知,TRichEdit 只是 Windows 富编辑控件的一个包装器。因此,您可能无能为力,只能围绕此进行计算以获得正确的位置。或者您使用其他控件,例如 WPRichText。
TRichEdit
不太适合作为语法高亮编辑器。考虑使用一个实际的语法高亮编辑器组件,例如 SynEdit (synedit.sourceforge.net)。
【参考方案1】:
我们也遇到了这个问题。
我们做“邮件合并”类型的事情,我们有带有合并代码的模板,这些代码被来自外部来源的数据解析和替换。
pos(mystring, RichEdit.Text) 和使用 RichText.SelStart 在 RichEdit 文本中的定位索引之间的索引不匹配破坏了我们的合并。
我没有一个好的答案,但我想出了一个解决方法。这有点麻烦(轻描淡写!),但在出现更好的解决方案之前......
解决方法是使用隐藏的 TMemo 并将 RichEdit 文本复制到其中并将 CR/LF 对更改为仅 CR。然后使用 TMemo 使用 pos(string, TMemo) 找到正确的位置,并使用它来获取要在 TRichEdit 中使用的 selstart 位置。
这真的很糟糕,但希望这种解决方法可以帮助其他人在我们的情况下,或者可能激发比我更聪明的人想出更好的解决方案。
我将展示一些示例代码...
由于我们使用 seltext 替换文本,因此我们需要替换 RichEdit 控件和 TMemo 控件中的文本以保持两者同步。
StartToken 和 EndToken 是合并代码分隔符,是一个常量。
function TEditForm.ParseTest: boolean;
var TagLength: integer;
var ValueLength: integer;
var ParseStart: integer;
var ParseEnd: integer;
var ParseValue: string;
var Memo: TMemo;
begin
Result := True;//Default
Memo := TMemo.Create(nil);
try
Memo.Parent := self;
Memo.Visible := False;
try
Memo.Lines.Clear;
Memo.Lines.AddStrings(RichEditor.Lines);
Memo.Text := stringreplace(Memo.Text,#13#10,#13,[rfReplaceAll]);//strip CR/LF pairs and replace with CR
while (Pos(StartToken, Memo.Text) > 0) and (Pos(EndToken, Memo.Text) > 0) do begin
ParseStart := Pos(StartToken, Memo.SelText);
ParseEnd := Pos(EndToken, Memo.SelText) + Length(EndToken);
if ParseStart >= ParseEnd then begin//oops, something's wrong - bail out
Result := true;
myEditor.SelStart := 0;
exit;
end;
TagLength := ParseEnd - ParseStart;
ValueLength := (TagLength - Length(StartToken)) - Length(EndToken);
ParseValue := Copy(Memo.SelText, (ParseStart + Length(StartToken)), ValueLength);
Memo.selstart := ParseStart - 1; //since the .text is zero based, but pos is 1 based we subtract 1
Memo.sellength := TagLength;
RichEditor.selstart := ParseStart - 1; //since the .text is zero based, but pos is 1 based we subtract 1
RichEditor.sellength := TagLength;
TempText := GetValue(ParseValue);
Memo.SelText := TempText;
RichEditor.SelText := TempText;
end;
except
on e: exception do
begin
MessageDlg(e.message,mtInformation,[mbOK],0);
result := false;
end;
end;//try..except
finally
FreeAndNil(Memo);
end;
end;
【讨论】:
仅供参考:FreeAndNil(Memo)
完全没有必要;由于Memo
是一个局部变量,因此在过程中最后的end;
超出范围,是否设置为nil
是没有意义的。只需调用Memo.Free
就足够了,而且 IMO 更具可读性。 (FreeAndNil
在我看来,Memo
的范围比当前程序更广。)
首先非常感谢您的回复。我不认为你真的需要一个隐藏的 TMemo。只要我们在谈论变通方法,您所要做的就是计算 CRLF 对的数量直到所需位置,然后为找到的每一对从后者中减去一个。结果数字将是您的 SelStart。然而,即使这样也不是真的可以接受,因为它太慢而且太麻烦了。我仍然无法相信我们在这里没有遗漏一些明显的东西。
@Ken - 同意。这是我第一次尝试拼凑一些东西。它可以使用一些工作。
@user1127813 - 我一直在查看 TRichEditStrings 代码中的 vcl.comctrls.pas 单元,并且看到代码看起来试图通过计算 CRLF 来解决 CRLF/CR 不匹配的区域但不起作用。看看 TRichEditStrings.Insert。【参考方案2】:
从插入符号位置减去EM_LINEFROMCHAR
怎么样? (或EM_GETSEL
的位置)任你选择。
您甚至可以获得两个EM_LINEFROMCHAR
变量。一个从选择开始,另一个从所需的插入符号/选择位置,如果您只想知道选择中有多少 cl/cr 对。
【讨论】:
亲爱的 Joshua,欢迎来到 Stack Overflow。 - 你有没有注意到你回答的问题是多年前的:-)?因此,如果您回答后没有任何反应,请不要失望。 -- 尝试像 OP 那样使用适当的语法格式:***.com/help/formatting。 -- 像你这样的建议最好放在 cmets 中。了解如何使用它们。祝你好运!以上是关于RichEdit 2.0 使用单个 CR 字符作为换行符会引发 SelStart 计算(Delphi XE2)的主要内容,如果未能解决你的问题,请参考以下文章