When transitioning from Delphi 2006 to Delphi XE2, one of the things that we learned is that RichEdit 2.0 replaces internally CRLF
pairs with a single CR
character. This has the unfortunate effect of throwing off all character index calculations based on the actual text string on the VCL's side.
The behavior I can see by tracing through the VCL code is as follows:
WM_GETTEXT
message (done in TControl.GetTextBuf
) will return a text buffer that contains CRLF
pairs.WM_GETTEXTLENGTH
message (done in TControl.GetTextLen
) will return a value as if the text still contains CRLF
characters.EM_SETSELEX
message (i.e. setting SelStart
) will treat the input value as if the text contains only CR
characters.This causes all sorts of things to fail (such as syntax highlighting) in our application. As you can tell, everything is off by exactly one character for every new line up to that point.
Obviously, since this is inconsistent behavior, we must be missing something or doing something very wrong.
Does anybody else has any experience with the transition from a RichEdit 1.0 to a RichEdit 2.0 control and how did you solve this issue? Finally, is there any way to force RichEdit 2.0 to use CRLF
pairs just like RichEdit 1.0?
We also ran into this very issue.
We do a "mail merge" type of thing where we have templates with merge codes that are parsed and replaced by data from outside sources.
This index mismatch between pos(mystring, RichEdit.Text) and the positioning index into the RichEdit text using RichText.SelStart broke our merge.
I don't have a good answer but I came up with a workaround. It's a bit cumbersome (understatment!) but until a better solution comes along...
The workaround is to use a hidden TMemo and copy the RichEdit text to it and change the CR/LF pairs to CR only. Then use the TMemo to find the proper positioning using pos(string, TMemo) and use that to get the selstart position to use in the TRichEdit.
This really sucks but hopefully this workaround will help others in our situation or maybe spark somebody smarter than me into coming up with a better solution.
I'll show a little sample code...
Since we are replacing text using seltext we need to replace text in BOTH the RichEdit control and the TMemo control to keep the two synchronized.
StartToken and EndToken are the merge code delimiters and are a constant.
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With