在 TRichEdit 中加载长 RTF 文本不起作用

Loading long RTF-Text in TRichEdit does not work

如果长 RTF 序列(例如 150 000 个字符)流式传输到 TRichEdit 控件(在 XE4 中),则控件不显示文本,而是显示原始 RTF 代码:

{\rtf1\ansi\ansicpg1252\deff0...

怎么了?

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;

例如具有以下RTF生成功能:

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;

已编辑:即使复制和粘贴也无法正常工作:

这是我所做的:

结果:

TRichEdit 是否有隐藏的字符限制?

Rich Edit 控件有文本限制。

尝试使用 EM_EXLIMITTEXT 消息,它设置了用户可以键入或粘贴到 Rich Edit 控件中的文本数量上限。此消息还限制了在流式传输 RTF (PlainText = False) 时可以流式传输到 Rich Edit 控件中的文本量。但在串流纯文本时不限制控制

例如:

const
  RE_MAX_TEXT_SIZE = 256000;

SendMessage(RichEdit1.Handle, EM_EXLIMITTEXT, 0, RE_MAX_TEXT_SIZE);

或:

SendMessage(RichEdit1.Handle, EM_EXLIMITTEXT, 0, FFFFFF0);

对于 TRichEditStrings.LoadFromFile() 中实施的最大限制:RichEdit.DoSetMaxLength(FFFFFF0); 但是,DoSetMaxLength() 在源代码中 不正确 使用,因为它应该在 加载流之前被调用。此外,DoSetMaxLength() 根本不用于 TRichEditStrings.LoadFromStream()。 Remy 在他的回答评论中。

除了kobik说的:

TRichEdit.Lines.LoadFromStream() 在内部使用 EM_STREAMIN。当 TRichEdit.PlainText 为 false 时,LoadFromStream() 将首先尝试以 RTF 格式加载流数据,如果遇到任何错误,它将以纯文本格式重新加载流数据。这就是您看到原始 RTF 代码出现的原因。

RTF 是一种基于 ASCII 的格式,因此 LoadFromStream() 需要 8 位 RTF 数据(在 PlainText=True 的情况下,将尝试将其转换为 Unicode)。尝试使用 AnsiStringTMemoryStream 而不是 (Unicode)StringTStringStream 作为您的 RTF 流。

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;