Delphi: центральная линия в TRichEdit с помощью прокрутки - PullRequest
1 голос
/ 13 мая 2010

У меня есть Delphi 2007 TRichEdit с несколькими строками. Я хочу прокрутить richedit по вертикали так, чтобы конкретный номер строки был приблизительно в центре видимой / отображаемой области richedit. Например, я хочу написать код для CenterLineInRichEdit в этом примере:

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;

Я рассмотрел использование сообщения WM_VSCROLL, и оно позволяет прокручивать вверх / вниз одну строку и т. Д., Но не прокручивать, чтобы отцентрировать конкретную строку.

Ответы [ 4 ]

3 голосов
/ 15 мая 2010

Исходя из представленных здесь идей, я придумал одно решение. Предполагается, что все строки в richedit имеют одинаковую высоту и что шрифт richedit по умолчанию правильно сообщает о своей высоте, но это может быть полезно для некоторых людей:

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;

Я проверил это с TRichEdit, но это может работать и для TMemo.

3 голосов
/ 13 мая 2010

Отправьте сообщение EM_LINESCROLL в RichEdit:

SendMessage(REdit.Handle, EM_LINESCROLL, 0, NumberOfVerticalLinesToScroll);

См. Тему EM_LINESCROLL MSDN .

2 голосов
/ 13 мая 2010

Попробуйте это;

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;

Ниже приведен альтернативный вариант, поскольку он центрирует первое вхождение строки вместо номера строки;

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;
2 голосов
/ 13 мая 2010

Вам нужно будет использовать пару сообщений Windows для общего управления этим аспектом вашего элемента управления:

  • EM_GETFIRSTVISIBLELINE для получения текущего, самого верхнего видимого номера строки (на основе 0)
  • EM_LINESCROLL для прокрутки текста вверх / вниз на указанное количество строк

Вам нужно будет рассчитать, сколько строк прокрутить вверх / вниз от текущей верхней строки, чтобы отобразить желаемый абсолютный номер строки, но вам придется самостоятельно рассчитать количество строк, видимых в элементе управления (используя шрифт метрика и контроль высоты).

Обратите внимание, что при использовании элемента управления RichEdit высота каждой строки может варьироваться в зависимости от шрифтов, примененных к тексту в элементе управления, поэтому любой подход, основанный только на номерах строк, может быть только приблизительно точным. Также я не уверен, что возможно определить текущий видимый диапазон элемента управления (то есть количество видимых в данный момент строк), поэтому его необходимо вычислить самостоятельно.

Из памяти элемент управления SynEdit предлагает некоторый дополнительный контроль над такими вещами, обеспечивая как свойство чтения / записи TopLine , так и свойство LinesInWindow . Тем не менее, я думаю, что SynEdit не поддерживает форматированный текст, но если это на самом деле не является проблемой в вашем приложении (т. Е. Вы можете использовать согласованный шрифт для всех строк в контенте), то это может быть привлекательной или подходящей альтернативой.

...