Использование TRichEdit во время выполнения без определения родителя

Мне нужно использовать TRichEdit во время выполнения, чтобы выполнить преобразование rtf в текст, как обсуждалось здесь. Мне удалось это сделать, но мне пришлось установить фиктивную форму в качестве родителя, иначе я не могу заполнить TRichedit.Lines. (Ошибка: родитель отсутствует). Я вставляю свою функцию ниже, может ли кто-нибудь предложить способ избежать определения родителя? Можете ли вы также прокомментировать это и сказать мне, найдете ли вы более эффективную идею?

Примечание. Мне нужна строка, а не TStrings в качестве вывода, поэтому она была разработана таким образом.

function RtfToText(const RTF: string;ReplaceLineFeedWithSpace: Boolean): string;
var
  RTFConverter: TRichEdit;
  MyStringStream: TStringStream;
  i: integer;
  CustomLineFeed: string;

begin
  if ReplaceLineFeedWithSpace then
    CustomLineFeed := ' '
    else
    CustomLineFeed := #13;
  try
    RTFConverter := TRichEdit.Create(nil);
    try
      MyStringStream := TStringStream.Create(RTF);
      RTFConverter.parent := Form4; // this is the part I don't like
      RTFConverter.Lines.LoadFromStream(MyStringStream);
      RTFConverter.PlainText := True;
      for i := 0 to RTFConverter.Lines.Count - 1 do
      begin
        if i < RTFConverter.Lines.Count - 1 then
          Result := Result + RTFConverter.Lines[i] + CustomLineFeed
          else
          Result := Result + RTFConverter.Lines[i];
      end;
    finally
      MyStringStream.Free;
    end;
  finally
    RTFConverter.Free;
  end;

end;

ОБНОВЛЕНИЕ: после ответа я обновил функцию и пишу здесь для справки:

function RtfToText(const RTF: string;ReplaceLineFeedWithSpace: Boolean): string;
var
  RTFConverter: TRichEdit;
  MyStringStream: TStringStream;
begin
  RTFConverter := TRichEdit.CreateParented(HWND_MESSAGE);
  try
    MyStringStream := TStringStream.Create(RTF);
    try
      RTFConverter.Lines.LoadFromStream(MyStringStream);
      RTFConverter.PlainText := True;
      RTFConverter.Lines.StrictDelimiter := True;
      if ReplaceLineFeedWithSpace then
        RTFConverter.Lines.Delimiter := ' '
        else
        RTFConverter.Lines.Delimiter := #13;
      Result := RTFConverter.Lines.DelimitedText;
    finally
      MyStringStream.Free;
    end;
  finally
    RTFConverter.Free;
  end;
end;

person LaBracca    schedule 12.07.2010    source источник
comment
Символ перевода строки имеет номер 10, а не номер 13, что означает возврат каретки.   -  person Premature Optimization    schedule 08.09.2011
comment
Раздражает необходимость ссылаться на визуальный компонент (TRichEdit), который, я полагаю, требует, чтобы вы вызывали Synchronize, когда делаете это в отдельном потоке. Я хочу преобразовать RTF в обычный текст на сервере, и я пока не нашел кода, который мне было бы удобно использовать. Но спасибо за публикацию этого.   -  person Alister    schedule 30.01.2012
comment
Я переместил вызовы Create за пределы блоков try/finally, что является правильным способом сделать это. В противном случае, если в вызове Create есть исключение, код попытается вызвать Free для неинициализированной переменной (помните, что исключение означает, что код никогда не возвращается к вызывающему оператору, а переходит непосредственно к части exclude/finally, поэтому присваивание к переменной никогда не делается).   -  person HeartWare    schedule 08.04.2016


Ответы (4)


Элемент управления TRichEdit представляет собой оболочку элемента управления RichEdit в Windows. Элементы управления Windows... ну... Windows, и для работы им нужен дескриптор окна. Delphi необходимо вызвать CreateWindow или CreateWindowEx для создания дескриптора, и обеим подпрограммам для работы требуется допустимый родительский дескриптор окна. Delphi пытается использовать дескриптор родителя элемента управления (и это имеет смысл!). К счастью, можно использовать альтернативный конструктор (конструктор CreateParanted(HWND)), и милые люди из Microsoft создали HWND_MESSAGE для использования в качестве родителя для окон, которым на самом деле не нужно «окно» (только для обмена сообщениями).

Этот код работает так, как ожидалось:

procedure TForm2.Button2Click(Sender: TObject);
var R:TRichEdit;
    L:TStringList;
begin
  R := TRichEdit.CreateParented(HWND_MESSAGE);
  try
    R.PlainText := False;
    R.Lines.LoadFromFile('C:\Temp\text.rtf');
    R.PlainText := True;

    Memo1.Lines.Text := R.Lines.Text;
  finally 
    R.Free;
  end;
end;
person Cosmin Prund    schedule 12.07.2010
comment
Я установил это как принятый ответ, потому что он показывает, как создать элемент управления TRichEdit почти без определения родителя. - person LaBracca; 12.07.2010
comment
Можно ли сделать такое преобразование потокобезопасным способом? Я предполагаю (и проверял), что попытки сделать это в рабочем потоке очень опасны. - person Rafael Nobre; 01.04.2011

Это часть того, как работает VCL, и вы не сможете заставить его работать по-другому без некоторых сложных обходных путей. Но вам не нужно определять фиктивную форму в качестве родителя; просто используйте свою текущую форму и установите visible := false; в TRichEdit.

Однако, если вы действительно хотите повысить производительность, вы можете отказаться от цикла, который вы используете для построения строки результата. Ему приходится много перераспределять и копировать память. Используйте свойство Text в TrichEdit.Lines, чтобы получить CRLF между каждой строкой, и DelimitedText, чтобы получить что-то еще, например пробелы. Они используют внутренний буфер, который выделяется только один раз, что немного ускорит конкатенацию, если вы работаете с большим количеством текста.

person Mason Wheeler    schedule 12.07.2010
comment
Я не знал об этих функциях TString, я обновлю свой вопрос, разместив окончательную версию, я также включил форму, сгенерированную во время выполнения, поэтому функция становится автономной функцией. - person LaBracca; 12.07.2010

Я использую DrawRichText для рисования RTF без элемента управления RichEdit. (IIRC это называется богатые элементы управления редактированием без окон.) Может быть, вы можете использовать это также для преобразования - однако я никогда не пробовал этого.

person Uli Gerhardt    schedule 12.07.2010

Это было наиболее полезным для меня, чтобы начать работу с TRichEdit, но не с преобразованием. Однако это работает так, как ожидалось, и вам не нужно устанавливать разделитель строк:

// RTF to Plain:
procedure TForm3.Button1Click(Sender: TObject);
var
    l:TStringList;
    s:WideString;
    RE:TRichEdit;
    ss:TStringStream;
begin
    ss := TStringStream.Create;
    s := Memo1.Text; // Input String
    RE := TRichEdit.CreateParented(HWND_MESSAGE);
    l := TStringList.Create;
    l.Add(s);
    ss.Position := 0;
    l.SaveToStream(ss);
    ss.Position := 0;
    RE.Lines.LoadFromStream(ss);
    Memo2.Text := RE.Text; // Output String
end;

// Plain to RTF:
procedure TForm3.Button2Click(Sender: TObject);
var
    RE:TRichEdit;
    ss:TStringStream;
begin
    RE := TRichEdit.CreateParented(HWND_MESSAGE);
    RE.Text := Memo2.Text; // Input String
    ss := TStringStream.Create;
    ss.Position := 0;
    RE.Lines.SaveToStream(ss);
    ss.Position := 0;
    Memo1.Text := ss.ReadString(ss.Size); // Output String
end;

Я использую TStringList "l" при преобразовании в обычный, потому что каким-то образом TStringStream помещает каждый символ в новую строку.

Редактировать: Сделал код немного лучше и удалил неиспользуемые переменные.

person user3437976    schedule 12.12.2014