Delphi: Zentrum Spezifische Linie in TRichEdit durch Scrolling
Frage
Ich habe eine Delphi 2007 TRichEdit mit mehreren Linien in ihr. Ich mag die Richedit vertikal so bewegen, dass eine bestimmte Zeilennummer, wenn etwa im sichtbaren / Anzeigebereich des Richedit zentriert. Zum Beispiel möchte ich den Code für CenterLineInRichEdit in diesem Beispiel schreiben:
procedure CenterLineInRichEdit(Edit: TRichEdit; LineNum: Integer);
begin
...
Edit.ScrollTo(...);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
REdit: TRichEdit;
i: Integer;
begin
REdit := TRichEdit.Create(Self);
REdit.Parent := Self;
Redit.ScrollBars := ssVertical;
REdit.SetBounds(10, 10, 200, 150);
for i := 1 to 25 do
REdit.Lines.Add('This is line number ' + IntToStr(i));
CenterLineInRichEdit(REdit, 13);
end;
Ich sah in der WM_VSCROLL Nachricht mit, und es ermöglicht Scrollen nach oben / unten einer Zeile, etc., aber nicht Scrollen eine bestimmte Zeile zu zentrieren.
Lösung
Geben Sie diesen einen Versuch;
procedure VertCenterLine(RichEdit: TRichEdit; LineNum: Integer);
// I don't know the reason but the RichEdit 2 control in VCL does not
// respond to the EM_SCROLLCARET in Richedit.h but it does so to the
// constant in WinUser.h
const
EM_SCROLLCARET = $00B7;
var
TextPos: lResult;
Pos: TSmallPoint;
begin
TextPos := SendMessage(RichEdit.Handle, EM_LINEINDEX, LineNum, 0);
if TextPos <> -1 then begin
// Go to top
SendMessage(RichEdit.Handle, EM_SETSEL, 0, 0);
SendMessage(RichEdit.Handle, EM_SCROLLCARET, 0, 0);
// Get the coordinates for the beginning of the line
Longint(Pos) := SendMessage(RichEdit.Handle, EM_POSFROMCHAR, TextPos, 0);
// Scroll from the top
SendMessage(RichEdit.Handle, WM_VSCROLL,
MakeWParam(SB_THUMBPOSITION, Pos.y - RichEdit.ClientHeight div 2), 0);
// Optionally set the caret to the beginning of the line
SendMessage(RichEdit.Handle, EM_SETSEL, TextPos, TextPos);
end;
end;
Die unten ist eine Alternative, dass es zentriert das erste Vorkommen eines Strings statt einer Zeilennummer;
procedure VertCenterText(RichEdit: TRichEdit; Text: string);
const
EM_SCROLLCARET = $00B7;
var
FindText: TFindText;
TextPos: lResult;
Pos: TSmallPoint;
begin
FindText.chrg.cpMin := 0;
FindText.chrg.cpMax := -1;
FindText.lpstrText := PChar(Text);
TextPos := SendMessage(RichEdit.Handle, EM_FINDTEXT,
FR_DOWN or FR_WHOLEWORD, Longint(@FindText));
if TextPos <> -1 then begin
SendMessage(RichEdit.Handle, EM_SETSEL, 0, 0);
SendMessage(RichEdit.Handle, EM_SCROLLCARET, 0, 0);
Longint(Pos) := SendMessage(RichEdit.Handle, EM_POSFROMCHAR, TextPos, 0);
SendMessage(RichEdit.Handle, WM_VSCROLL,
MakeWParam(SB_THUMBPOSITION, Pos.y - RichEdit.ClientHeight div 2), 0);
SendMessage(RichEdit.Handle, EM_SETSEL, TextPos, TextPos);
end;
end;
Andere Tipps
Basierend auf den Ideen, die hier kam ich mit einer Lösung auf. Er geht davon aus, dass alle die Linien im Richedit die gleiche Höhe sind und dass die Standardschrift des Richedit korrekt meldet seine Höhe, aber es könnte für einige Leute nützlich sein:
type
TCustomEditHack = class(TCustomEdit);
procedure CenterLineInEdit(Edit: TCustomEdit; LineNum: Integer);
var
VisibleLines: Integer;
TopLine: Integer;
FirstLine: Integer;
begin
FirstLine := Edit.Perform(EM_GETFIRSTVISIBLELINE, 0, 0);
VisibleLines := Round(Edit.ClientHeight / Abs(TCustomEditHack(Edit).Font.Height));
if VisibleLines <= 1 then
TopLine := LineNum
else
TopLine := Max(LineNum - Round((VisibleLines/2)) + 1, 0);
if FirstLine <> TopLine then
Edit.Perform(EM_LINESCROLL, 0, TopLine - FirstLine);
end;
Getestet habe ich diese mit TRichEdit, aber es könnte genauso gut für TMemo arbeiten.
Senden Sie eine EM_LINESCROLL Nachricht an das RichEdit:
SendMessage(REdit.Handle, EM_LINESCROLL, 0, NumberOfVerticalLinesToScroll);
Siehe EM_LINESCROLL MSDN-Thema .
Sie müssen ein paar Windows-Nachrichten verwenden, um diesen Aspekt Ihrer Kontrolle in einer generischen Art und Weise zu manipulieren:
- EM_GETFIRSTVISIBLELINE die aktuelle, oberste sichtbare Zeilennummer (0-basiert)
- EM_LINESCROLL den Text nach oben / unten um eine bestimmte Anzahl von Zeilen blättern
Sie müssen, wie viele Zeilen berechnen, um nach oben / unten aus der aktuellen Top-Line eine gewünschte absolute Zeilennummer in den Blick zu bringen, aber Sie werden die Anzahl der Zeilen sichtbar in der Steuer berechnen müssen sich (Schriftart verwenden Metriken und Steuerhöhe).
Beachten Sie, dass mit einem RichEdit-Steuerelement der Höhe jeder Zeile kann je nach Schriftart auf den Text in der Steuerung angelegt, um jeder Ansatz basierend auf Zeilennummern allein wahrscheinlich nur annähernd genau sein. Auch ich bin nicht sicher, dass es möglich ist, den aktuellen sichtbaren Bereich der Steuerung (das heißt die Anzahl der Leitungen zur Zeit sichtbar) direkt zu bestimmen, so dass es die Berechnung selbst notwendig ist.
Aus dem Gedächtnis, der SynEdit Steuer bietet einige zusätzliche Kontrolle über solche Dinge, die beide bieten eine Lese- / Schreib TopLine Immobilien sowie ein LinesInWindow Eigenschaft. Aber ich denke, SynEdit nicht Rich-Text-fähig ist, aber wenn dies in Ihrer Anwendung nicht wirklich ein Anliegen ist (dh Sie eine konsistente Schriftart für alle Zeilen des Inhalts verwenden können), dann kann es eine attraktive oder geeignete Alternative sein.