Entwickler-Ecke
Grafische Benutzeroberflächen (VCL & FireMonkey) - Länge einer formatierten Zeile in RichEdit
Daniel L. - Mi 27.10.10 17:29
Titel: Länge einer formatierten Zeile in RichEdit
Hallo!
Kann man die Länge in Pixel einer formatierten Textzeile in einer RichEdit bestimmen?
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| with RichEdit do begin SelAttributes.Color := clred; SelAttributes.Size := 20; Paragraph.Alignment := taCenter; Lines.Add('Wie lang ist eigentlich diese Zeile in Pixeln'); end; |
Schon mal Danke + Gruss: Daniel
elundril - Mi 27.10.10 18:23
Mittels Canvas.TextWidth;
hier ein beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| var bmp := TBitmap; begin bmp := TBitmap.Create(); bmp.canvas.Font := Richedit1.Font; laenge := bmp.canvas.textwidth(deine_Zeile); bmp.free end; |
das noch mit entsprechenden Ressourcenschutzblöcken ausstatten und voilá!
lg elundril
jaenicke - Mi 27.10.10 19:40
In einem RichEdit kann der Text aber beliebig formatiert sein.
Ich vermute daher, dass du da manuell durchgehen musst und die Zeichen entsprechend ihrer Formatierung behandeln musst. Eine fertige Routine gibt es dafür AFAIK nicht.
Gerd Kayser - Mi 27.10.10 23:50
Daniel L. hat folgendes geschrieben : |
Kann man die Länge in Pixel einer formatierten Textzeile in einer RichEdit bestimmen? |
Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27:
| procedure TMainform.Button1Click(Sender: TObject); var PunktZeile : TPoint; PunktAnfang : TPoint; CharPos : integer; begin CharPos := 0; SendMessage(RichEdit1.Handle, EM_PosFromChar, wParam(@PunktAnfang), lParam(CharPos)); CharPos := SendMessage(RichEdit1.Handle, EM_LineLength, 0, 0); SendMessage(RichEdit1.Handle, EM_PosFromChar, wParam(@PunktZeile), lParam(CharPos)); Label1.Caption := 'Anzahl Zeichen: ' + IntToStr(CharPos); Label2.Caption := 'Breite der Zeile (Space + Text): ' + IntToStr(PunktZeile.X); Label3.Caption := 'Breite der Zeile (nur Text): ' + IntToStr(PunktZeile.X - PunktAnfang.X + 1); end;
procedure TMainform.Button2Click(Sender: TObject); begin with RichEdit1 do begin Clear; SelAttributes.Color := clred; SelAttributes.Size := 20; Paragraph.Alignment := taCenter; Lines.Add('Wie lang ist eigentlich diese Zeile in Pixeln?'); end; end; |
Im Beispiel gehe ich von nur einer Zeile aus, also ggf. anpassen.
Edit1: Ob die Zeichenbreite des letzten Zeichens in den Werten eingerechnet ist, habe ich noch nicht geprüft.
@elundril: Deine Prozedur scheint falsche Werte zu liefern (203). Ich ermittele hingegen 509, was wohl eher hinkommen dürfte.
Edit2: Das letzte Zeichen ist mit eingerechnet, was man leicht überprüfen kann, wenn der Text aus nur einem Zeichen besteht.
jaenicke - Do 28.10.10 05:51
Stimmt, auf die Idee bin ich gar nicht gekommen. Über die Position des ersten und letzten Zeichens lässt sich die Breite natürlich berechnen. :zustimm:
Daniel L. - Do 28.10.10 09:12
@Gerd: Vielen Dank für die kompetente Antwort!
Verrätst du mir noch, was ich anpassen muss, um z.B. die 3. Zeile zu 'messen'?
Danke + Gruss: Daniel
jaenicke - Do 28.10.10 11:11
CharPos musst du (bezogen auf das Beispiel) entsprechend setzen.
Gerd Kayser - Do 28.10.10 13:14
Daniel L. hat folgendes geschrieben : |
Verrätst du mir noch, was ich anpassen muss, um z.B. die 3. Zeile zu 'messen'? |
Ich komme erst heute Nacht dazu, weiter zu programmieren. Jetzt stehen erst einmal zwei Stunden Gassigänge mit meiner verspielten Schäferhündin und ein längerer Zahnarztbesuch an.
Daniel L. - Do 28.10.10 19:16
hm, intern scheint TRichEdit für jede Textzeile 2 weitere Charakters dazuzuzählen (vielleicht #10 und #13 ?)
Folgende Funkion funzt bei mir:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:
| function TForm1.GetZeilenBreite (Zeile : integer) : integer; var i, CharCount : integer; ZeileAnfang, ZeileEnde : TPoint; begin
if (zeile > (RichEdit1.Lines.Count - 1)) or (zeile < 0) then begin result := -1; exit; end;
CharCount := 0;
for I := 0 to zeile - 1 do inc (CharCount, length (RichEdit1.Lines [i]) + 2);
SendMessage (RichEdit1.Handle, EM_PosFromChar, wparam (@ZeileAnfang), CharCount); CharCount := CharCount + SendMessage (RichEdit1.Handle, EM_LineLength, CharCount, 0); SendMessage(RichEdit1.Handle, EM_PosFromChar, wParam (@ZeileEnde), CharCount); result := ZeileEnde.X - ZeileAnfang.X; end;
procedure TForm1.Button1Click(Sender: TObject); begin showmessage ('Zeilenbreite = ' + Inttostr (GetZeilenBreite (1))); end; |
Ich hoffe, dass das auch programmiertechnisch in Ordnung ist und dass Gerd kein Zahnschmerzen hat :)
Gruss: Daniel
jaenicke - Do 28.10.10 19:29
Daniel L. hat folgendes geschrieben : |
Ich hoffe, dass das auch programmiertechnisch in Ordnung ist und dass Gerd kein Zahnschmerzen hat :) |
Ich bekomm da immer eher Bauchschmerzen. :P
Zur Sache:
Du brauchst keine Schleife. Mit EM_LINEINDEX bekommst du das erste Zeichen einer Zeile:
http://msdn.microsoft.com/en-us/library/bb761611(v=VS.85).aspx
Steht alles in der Doku. ;-)
// EDIT:
Ach ja: Die beiden Zeichen pro Zeile mehr sind in der Tat die Zeilenumbruchszeichen.
Gerd Kayser - Do 28.10.10 20:17
Daniel L. hat folgendes geschrieben : |
Ich hoffe, dass das auch programmiertechnisch in Ordnung ist und dass Gerd kein Zahnschmerzen hat :) |
Nö, keine Zahnschmerzen. Bin nur um ca. 3 Gramm Edelmetall wertvoller geworden (neue Zahnkrone). ;-)
Ich bin zur Zeit dabei, eine komfortable Funktion für die Aufgabe zu schreiben. Dazu brauche ich aber noch etwas Zeit (debuggen, testen usw.). Aber jetzt muß ich erst einmal wieder mit meiner Madame die Bäumchen im Park zählen und kontrollieren gehen. ;-)
Gerd Kayser - Do 28.10.10 23:51
So, hier ist meine Lösung, die auch Zeilenumbrüche durch WordWrap richtig behandelt. Bitte beachten, daß die Funktion verschieden aufgerufen werden kann:
1. mit Zeile = - 1: liefert die Zeilenanzahl zurück
2. mit Zeilennummer, aber NurText = false: Breite der Zeichenkette plus dem Leerraum vor dem 1. Zeichen
3. mit Zeilennummer, aber NurText = true: Breite der Zeichenkette
NurText kann beim Aufruf weggelassen werden. Dann arbeitet die Funktion mit NurText = true
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131:
| unit Main;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls;
type TMainform = class(TForm) RichEdit1: TRichEdit; Button1: TButton; Label1: TLabel; Label2: TLabel; Button2: TButton; Label3: TLabel; Edit1: TEdit; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private public function ErmittelnTextBreite(Handle : THandle; Zeile : integer; NurText : boolean = true): integer; end;
var Mainform: TMainform;
implementation
{$R *.dfm}
function TMainform.ErmittelnTextBreite(Handle : THandle; Zeile : integer; NurText : boolean = true): integer; var AnzZeilen : integer; ErstesZeichen : integer; LetztesZeichen : integer; PunktStart : TPoint; PunktEnde : TPoint; begin Result := - 1; AnzZeilen := SendMessage(Handle, EM_GetLineCount, 0, 0);
if Zeile = - 1 then Result := AnzZeilen else begin ErstesZeichen := SendMessage(Handle, EM_LineIndex, wParam(Zeile), 0); LetztesZeichen := SendMessage(Handle, EM_LineLength, wParam(ErstesZeichen), 0); SendMessage(Handle, EM_PosFromChar, wParam(@PunktStart), lParam(ErstesZeichen)); SendMessage(Handle, EM_PosFromChar, wParam(@PunktEnde), lParam(ErstesZeichen + LetztesZeichen));
if PunktStart.Y <> PunktEnde.Y then SendMessage(Handle, EM_PosFromChar, wParam(@PunktEnde), lParam(ErstesZeichen + LetztesZeichen - 1));
if NurText then Result := PunktEnde.X - PunktStart.X + 1 else Result := PunktEnde.X; end; end;
procedure TMainform.Button1Click(Sender: TObject); var Zeile : integer; AnzZeilen : integer; Breite : integer; begin if not TryStrToInt(Trim(Edit1.Text), Zeile) then ShowMessage('Fehler: Keine Ziffer im Edit-Feld!') else begin AnzZeilen := ErmittelnTextBreite(RichEdit1.Handle, - 1); Label1.Caption := 'Anzahl Zeilen: ' + IntToStr(AnzZeilen); if Zeile > (AnzZeilen - 1) then ShowMessage('Fehler: RichEdit enthält nicht so viele Zeilen!') else begin Breite := ErmittelnTextBreite(RichEdit1.Handle, Zeile, false); Label2.Caption := 'Breite (Space am Angfang + Text): ' + IntToStr(Breite); Breite := ErmittelnTextBreite(RichEdit1.Handle, Zeile); Label3.Caption := 'Breite (nur Text): ' + IntToStr(Breite); end; end;
end;
procedure TMainform.Button2Click(Sender: TObject); var Text1 : string; Text2 : string; begin Text1 := 'Wie lang ist eigentlich diese Zeile in Pixeln?'; Text2 := Text1 + Text1 + Text1; with RichEdit1 do begin Clear; SelAttributes.Color := clred; SelAttributes.Size := 20; Paragraph.Alignment := taCenter; Lines.Add(Text1); SelAttributes.Color := clred; SelAttributes.Size := 20; Paragraph.Alignment := taCenter; Lines.Add(Text2); SelAttributes.Color := clred; SelAttributes.Size := 20; Paragraph.Alignment := taLeftJustify; Lines.Add(Text1); SelAttributes.Color := clred; SelAttributes.Size := 20; Paragraph.Alignment := taRightJustify; Lines.Add(Text1); end; end;
end. |
Edit1: Fehler in Zeile 89 korrigiert
Daniel L. - Fr 29.10.10 09:39
Oh ja, stimmt, ohne die WordWrap Beachtung kämen ja ggf. falsche Werte raus.
EM_GetLineCount scheint immer eine um 1 grössere Zahl zu liefern, man muss also wieder 1 subtrahieren, oder man nimmt Richedit.lines.count
Gerd Kayser hat folgendes geschrieben : |
Delphi-Quelltext 1: 2:
| if NurText then Result := PunktEnde.X - PunktStart.X + 1 |
|
Bist du sicher, dass das stimmt?
Für eine leere Textzeile kommt dann 1 raus!
Gruss: Daniel
Gerd Kayser - Fr 29.10.10 11:47
Daniel L. hat folgendes geschrieben : |
EM_GetLineCount scheint immer eine um 1 grössere Zahl zu liefern, man muss also wieder 1 subtrahieren, oder man nimmt Richedit.lines.count |
Das ist so nicht richtig. Es sind zwar z. B. 6 Zeilen mit Text vorhanden, aber es gibt noch eine 7. (leere) Zeile, in die Du mit dem Cursor springen kannst. Wenn Du in dieser Leerzeile die Backspace-Taste drückst, so daß der Cursor in der vorhergehenden Zeile direkt hinter dem Text steht, erst dann stimmen die Werte von Richedit.lines.count und EM_GetLineCount überein.
Zitat: |
Delphi-Quelltext 1: 2:
| if NurText then Result := PunktEnde.X - PunktStart.X + 1 |
Bist du sicher, dass das stimmt?
Für eine leere Textzeile kommt dann 1 raus! |
Folgender Fall:
Eine Zeile ist linksbündig (taLeftJustify) ausgerichtet. Ohne "+ 1" ergäben sich dann folgende Werte für den String:
Nur Text: 508
aber Text plus Leerraum davor: 509
Das ist jetzt Auslegungssache, ob man das Pixel vor dem Text zur Textbreite hinzuzählt oder nicht. Du kannst das "+ 1" weglassen oder alternativ die Funktion am Ende um folgende Anweisung ergänzen:
Delphi-Quelltext
1: 2:
| if Result = 1 then Result := 0; |
Das überlasse ich Dir. Ist wie gesagt eine reine Interpretationssache.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!