Как нарисовать цветную линию слева от TMemo, которая выглядит как желоб - PullRequest
3 голосов
/ 29 апреля 2019

Требуется компонент, полученный из TMemo (не компоненты TSyn)

Мне нужна линия слева (внутри или снаружи) от TMemo, толщина (опция) и цвет которой можно контролировать только для целей индикации. Он не обязательно должен быть функциональным в качестве водосточного желоба, но выглядит так, как у SynMemo, как показано на рисунке. Проблема с SynMemo заключается в том, что он не поддерживает шрифты переменной ширины, такие как Tahoma, но TMemo поддерживает.

enter image description here

Я попытался создать несколько составных компонентов с CustomContainersPack, объединив TShape с TMemo, даже наложив TMemo поверх TSynMemo, но не удалось, так как рисование при перетаскивании выглядело как дизассемблированный, а CCPack не настолько надежен для моей IDE .

KMemo, JvMemo и многие другие Torry.net компоненты были установлены и проверены на наличие скрытой поддержки для достижения того же, но ни один не работал.

Группирование компонентов вместе также не является решением для меня, так как многие события мыши связаны с Memo, и вызовы FindVCLWindow будут возвращать изменяющиеся компоненты под мышью. Кроме того, потребуется много компонентов, поэтому группировка с TPanel увеличит использование памяти.

Ответы [ 2 ]

5 голосов
/ 29 апреля 2019

Вы можете использовать сообщение WM_Paint и хак, чтобы сделать это без создания нового компонента, В противном случае создайте потомка TMemo и примените те же изменения ниже

 TMemo = class(Vcl.StdCtrls.TMemo)
  private
    FSidecolor: TColor;
    FSideColorWidth: Integer;
    FAskForAttention: Boolean;
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
    procedure SetSideColorWidth(const Value: Integer);
    procedure SetSideColor(const Value: TColor);
    procedure SetAskForAttention(const Value: Boolean);
  published
    property SideColor: TColor read FSideColor write SetSideColor default clRed;
    property SideColorWidth: Integer read FSideColorWidth write SetSideColorWidth default 2;
    property AskForAttension: Boolean read FAskForAttention write SetAskForAttention;
  end;

{ TMemo }

procedure TMemo.SetAskForAttention(const Value: Boolean);
begin
  FAskForAttention := Value;
  Invalidate;
end;

procedure TMemo.SetSideColor(const Value: TColor);
begin
  FSideColor := Value;
  Invalidate;
end;

procedure TMemo.SetSideColorWidth(const Value: Integer);
begin
  FSideColorWidth := Value;
  Invalidate;
end;

procedure TMemo.WMPaint(var Message: TWMPaint);
var
  DC: HDC;
  Pen: HPen;
  R,G,B: Byte;
begin
  inherited;
  if FAskForAttention then
  begin
    DC := GetWindowDC(Handle);
    try
      B := Byte(FSidecolor);
      G := Byte(FSidecolor shr 8);
      R := Byte(FSidecolor shr 16);

      Pen := CreatePen(PS_SOLID, FSideColorWidth, RGB(R,G,B));
      SelectObject(DC, Pen);
      SetBkColor(DC, RGB(R,G,B));
      Rectangle(DC, 1, 1, FSideColorWidth, Height - 1);
      DeleteObject(Pen);
    finally
      ReleaseDC(Handle, DC);
    end;
  end;
end;

И вы можете использовать это так

procedure TForm15.Button1Click(Sender: TObject);
begin
  memo1.SideColor := ColorBox1.Selected;
  memo1.SideColorWidth := 2;
  memo1.AskForAttension := True;
end;

и вы получите этот результат

enter image description here

Ограничения:

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

  • Если граница слишком толстая, вы получите следующий эффект enter image description here
  • При перемещении мыши линия иногда исчезает и не окрашивается (я думаю, что это из-за рисования прямоугольника фокуса).

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

Создание новых компонентов путем объединения двух элементов управления (TEdit и TTrackBar) в Delphi VCL

Это в основном те же идеи.


Edit:

Хорошо, я учел то, что упомянуто в комментариях, и адаптировал мой ответ,

Я также изменил способ получения холста компонента. Новая реализация становится такой

{ TMemo }

procedure TMemo.SetAskForAttention(const Value: Boolean);
var
  FormatRect: TRect;
begin
  if FAskForAttention <> Value then
  begin
    FAskForAttention := Value;

    if not FAskForAttention then
    begin
      Perform(EM_SETRECT, 0, nil);
    end
    else
    begin
      FormatRect := GetClientRect;

      if IsRightToLeft then
        FormatRect.Right := FormatRect.Right - FSideColorWidth - 3
      else
        FormatRect.Left := FormatRect.Left + FSideColorWidth + 3;

      Perform(EM_SETRECT, 0, FormatRect);
    end;
    Invalidate;
  end;
end;

procedure TMemo.SetSideColor(const Value: TColor);
begin
  if FSideColor <> Value then
  begin
    FSideColor := Value;
    Invalidate;
  end;
end;

procedure TMemo.SetSideColorWidth(const Value: Integer);
var
  FormatRect: TRect;
begin
  if FSideColorWidth <> Value then
  begin
    FSideColorWidth := Value;
    FormatRect := GetClientRect;

    if IsRightToLeft then
      FormatRect.Right := FormatRect.Right - FSideColorWidth - 3
    else
      FormatRect.Left := FormatRect.Left + FSideColorWidth + 3;

    Perform(EM_SETRECT, 0, FormatRect);
  end;
end;

procedure TMemo.WMPaint(var Message: TWMPaint);
var
  Canvas: TControlCanvas;
  CRect: TRect;
begin
  inherited;
  if FAskForAttention then
  begin
    Canvas := TControlCanvas.Create;
    try
      Canvas.Control := Self;
      Canvas.Font.Assign(Self.Font);

      CRect := GetClientRect;

      if IsRightToLeft then
        CRect.Left := CRect.Right - FSideColorWidth
      else
        CRect.Width := FSideColorWidth;

      Canvas.Brush.Color := FSidecolor;
      Canvas.Brush.Style := bsSolid;
      Canvas.FillRect(CRect);
    finally
      Canvas.Free;
    end;
  end;
end;

Ограничений по размеру нет, и они не перекрывают полосы прокрутки.

Окончательный результат:

enter image description here

Ссылки, которые я использовал, чтобы написать этот ответ:

3 голосов
/ 29 апреля 2019

Вместо написания пользовательского элемента управления, поместите панель или фигуру рядом со стандартным напоминанием и дайте ему любой цвет, который вам нравится.

Если это слишком утомительно, чтобы повторяться много раз, поместите памятку исформируйте рамку и поместите ее в хранилище.Установите анкеры, чтобы убедиться, что они правильно меняют размерВам даже не нужно писать код для этого, и у вас есть мгновенный «имитация пользовательского элемента управления».

Гораздо лучше и проще, чем написание, установка и тестирование пользовательского элемента управления IMO.

Теперь, если вы хотите поместить текст, цифры или значки в водосточный желоб, было бы полезно написать собственный элемент управления.Используйте EM_SETRECT, чтобы установить внутренний прямоугольник форматирования, и произвольно нарисуйте желоб в переопределенном методе Paint.Не забудьте позвонить inherited.

...