2010-07-12 6 views
17

Ich muss ein TRichEdit zur Laufzeit verwenden, um die Konvertierung von RTF zu Text wie besprochen here. Es ist mir gelungen, dies zu tun, aber ich musste ein Dummy-Formular als Eltern festlegen, wenn ich nicht die TRichedit.Lines bevölkern kann. (Fehler: Eltern fehlt). Ich füge meine Funktion unten, kann jemand einen Weg vorschlagen, um zu vermeiden, ein Elternteil zu definieren? Kannst du das auch kommentieren und mir sagen, ob du eine performantere Idee findest?Verwenden von TRichEdit zur Laufzeit ohne ein Elternteil zu definieren

Hinweis: Ich brauche eine Zeichenfolge, nicht TStrings als Ausgabe, deshalb wurde es so konzipiert.

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; 

UPDATE: Nach der Antwort, die ich die Funktion aktualisiert und hier als Referenz schreiben:

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; 
+0

Line Feed Char ist # 10, nicht # 13, Carriage Return –

+0

Es ist ärgerlich, eine visuelle Komponente (TRichEdit) zu referenzieren, die vermutlich in einem separaten Thread Synchronize aufrufen muss. Ich möchte RTF in reinen Text auf einem Server konvertieren, und ich habe noch keinen Code gefunden, den ich noch benutzen kann. Aber danke, dass du das geschrieben hast. – Alister

+0

Ich habe die Create-Aufrufe außerhalb der try/finally-Blöcke verschoben, was der richtige Weg ist.Andernfalls, wenn eine Ausnahme im Create-Aufruf ist, wird der Code versuchen, Free für eine nicht initialisierte Variable aufzurufen (denken Sie daran, dass eine Ausnahme bedeutet, dass der Code niemals zur aufrufenden Anweisung zurückkehrt, sondern direkt zum except/finally-Teil springt) zu der Variable wird nie getan). – HeartWare

Antwort

28

TRichEdit Steuerelement ist ein Wrapper um das RichEdit-Steuerelement in Windows. Windows-Steuerelemente sind ... gut .. Windows, und sie brauchen einen Fenstergriff, um zu arbeiten. Delphi muss CreateWindow oder CreateWindowEx aufrufen, um das Handle zu erstellen, und beide Routinen benötigen ein gültiges übergeordnetes Fensterhandle. Delphi versucht, das Handle des übergeordneten Elements des Steuerelements zu verwenden (und es macht Sinn!). Glücklicherweise kann man einen alternativen Konstruktor verwenden (den CreateParanted(HWND) Konstruktor) und die netten Leute bei Microsoft haben die HWND_MESSAGE als Eltern für Fenster verwendet, die eigentlich kein "Fenster" brauchen (nur Messaging).

Dieser Code funktioniert wie erwartet:

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; 
+0

Toller Trick, lass mich die Frage nochmal auf den neuesten Stand bringen. – LaBracca

+1

Ich setze dies als akzeptierte Antwort, weil es zeigt, wie man ein TRichEdit-Steuerelement * fast * erstellt, ohne ein Elternteil zu definieren. – LaBracca

+0

+1 für CreateParented von mir auch. – Despatcher

8

Dieser Teil des Weges ist die VCL funktioniert, und du gehst nicht zu ohne eine schwere Umgehungslösung anders arbeiten zu lassen. Sie müssen jedoch kein Dummy-Formular als Eltern definieren. benutze einfach dein aktuelles Formular und setze visible := false; auf den TRichEdit.

Wenn Sie jedoch wirklich die Leistung verbessern möchten, können Sie die Schleife, die Sie zum Erstellen einer Ergebniszeichenfolge verwenden, einfach wegwerfen. Es muss viel Speicher neu zugewiesen und kopiert werden. Verwenden Sie die Text-Eigenschaft von TrichEdit.Lines, um eine CRLF zwischen jeder Zeile zu erhalten, und DelimitedText, um etwas anderes zu erhalten, z. B. Leerzeichen. Sie verwenden einen internen Puffer, der nur einmal zugewiesen wird, was die Verkettung erheblich beschleunigt, wenn Sie mit viel Text arbeiten.

+0

+1 für 'Sichtbar: = false' –

+0

Ich wusste nicht über diese Funktion von TString, ich werde meine Frage aktualisieren, indem ich die endgültige Version, ich fügte auch eine Laufzeit generierte Form, so dass die Funktion eine" Standalone "-Funktion wird . – LaBracca

4

Ich verwende DrawRichText, um RTF ohne ein RichEdit-Steuerelement zu zeichnen. (IIRC heißt das Windowless Rich Edit Controls.) Vielleicht können Sie dies auch zum Konvertieren verwenden - allerdings habe ich das noch nie ausprobiert.

0

dies die hilfreich war für mich mit TRichEdit um loszulegen, aber nicht mit der Umwandlung. Dies funktioniert jedoch wie erwartet, und Sie brauchen nicht auf die Linie Trennzeichen zu setzen:

// 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; 

Ich bin mit dem TStringList „l“ in der Umstellung auf Ebene, weil irgendwie die TStringStream jedes einzelne Zeichen in einer neuen Zeile setzt .

Edit: machte den Code ein bisschen schöner und entfernte nicht verwendete Variablen.

Verwandte Themen