可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
If a long RTF-sequenz (eg 150 000 chars) is streamed into a TRichEdit
control (in XE4), the control does not display the text but instead shows the raw RTF code:
{\rtf1\ansi\ansicpg1252\deff0...
What is wrong?
procedure TForm1.Button1Click(Sender: TObject); var RtfText: string; Stream: TStringStream; begin RtfText := GenerateRtfText(); Stream := TStringStream.Create(RtfText); try RichEdit2.PlainText := False; RichEdit2.Lines.LoadFromStream(Stream); //<--- ERROR: RichEdit displays raw RTF-Code // if RtfText is too long if StartsText('{\rtf', RichEdit2.Lines.Text) then begin ShowMessage('Oh no, not converted!'); //WORKAROUND: 2nd try seems to work... //Stream.Position := 0; //RichEdit2.Lines.LoadFromStream(Stream); end; finally Stream.Free; end; end;
For instance with following RTF generation function:
function TForm1.GenerateRtfText: string; var I: Integer; Stream: TStringStream; const DOES_WORK_COUNT = 10000; DOES_NOT_WORK_COUNT = 15000; begin //Fill RichEdit1.Lines.BeginUpdate; try //for I := 0 to DOES_WORK_COUNT do for I := 0 to DOES_NOT_WORK_COUNT do RichEdit1.Lines.Add(IntToStr(I)); finally RichEdit1.Lines.EndUpdate; end; //Convert to RTF Stream := TStringStream.Create; try RichEdit1.Lines.SaveToStream(Stream); Result := Stream.DataString; finally Stream.Free; end; end;
Edited: Even copy and paste does not work correctly:
This is what I did:
- I copied the generated content of RichEdit1 (lines 1..15000 with numbers 1..15000) into notpad.exe to remove any RTF
- I copied the content of notepad into RichEdit2
Result:
- only 12773 lines are displayed correctly. The last line is only
12
- if I try to add another char into the TRichEdit nothing happens
- if I remove 10 chars (per backspace) I can add exactly 10 chars afterwards...
Is there a hidden character limit for TRichEdit?
回答1:
Rich edit control has a text limit.
Try using EM_EXLIMITTEXT
message, which sets an upper limit to the amount of text the user can type or paste into a rich edit control. This message also limit the amount of text that you can stream into a rich edit control when streaming RTF (PlainText = False
). but does not limit the control when streaming plain text.
e.g.:
const RE_MAX_TEXT_SIZE = 256000; SendMessage(RichEdit1.Handle, EM_EXLIMITTEXT, 0, RE_MAX_TEXT_SIZE);
or:
SendMessage(RichEdit1.Handle, EM_EXLIMITTEXT, 0, $7FFFFFF0);
for the maximum limit as implemented in TRichEditStrings.LoadFromFile()
: RichEdit.DoSetMaxLength($7FFFFFF0);
However, DoSetMaxLength()
is not correctly used in the sources, as it should be called before the stream is loaded. Also, DoSetMaxLength()
is not used at all for TRichEditStrings.LoadFromStream()
. Remy mentioned this in the comments of his answers.
回答2:
In addition to what kobik said:
TRichEdit.Lines.LoadFromStream()
uses EM_STREAMIN
internally. When TRichEdit.PlainText
is false, LoadFromStream()
will first try to load the stream data as RTF, and if any error is encountered than it will re-load the stream data as plain text instead. That is why you see the raw RTF code appear.
RTF is an ASCII-based format, and so LoadFromStream()
expects 8-bit RTF data (and in the case of PlainText=True
, will try to convert it to Unicode). Try using AnsiString
and TMemoryStream
instead of (Unicode)String
and TStringStream
for your RTF stream.
type TReadOnlyMemoryBufferStream = class(TCustomMemoryStream) public constructor Create(APtr: Pointer; ASize: NativeInt); function Write(const Buffer; Count: Longint): Longint; override; end; constructor TReadOnlyMemoryBufferStream.Create(APtr: Pointer; ASize: NativeInt); begin inherited Create; SetPointer(APtr, ASize); end; function TReadOnlyMemoryBufferStream.Write(const Buffer; Count: Longint): Longint; begin Result := 0; end; procedure TForm1.Button1Click(Sender: TObject); var RtfText: AnsiString; Stream: TReadOnlyMemoryBufferStream; begin RtfText := GenerateRtfText(); Stream := TReadOnlyMemoryBufferStream.Create(PAnsiChar(RtfText), Length(RtfText)); try RichEdit2.PlainText := False; RichEdit2.Lines.LoadFromStream(Stream); ... finally Stream.Free; end; end; function TForm1.GenerateRtfText: AnsiString; var I: Integer; Stream: TMemoryStream; const DOES_WORK_COUNT = 10000; DOES_NOT_WORK_COUNT = 15000; begin //Fill RichEdit1.Lines.BeginUpdate; try //for I := 0 to DOES_WORK_COUNT do for I := 0 to DOES_NOT_WORK_COUNT do RichEdit1.Lines.Add(IntToStr(I)); finally RichEdit1.Lines.EndUpdate; end; //Convert to RTF Stream := TMemoryStream.Create; try RichEdit1.PlainText := False; RichEdit1.Lines.SaveToStream(Stream); SetString(Result, PAnsiChar(Stream.Memory), Stream.Size); finally Stream.Free; end; end;