【问题标题】:How do i custom draw of TEdit control text?如何自定义绘制 TEdit 控件文本?
【发布时间】:2012-04-03 05:18:09
【问题描述】:

我想使用与默认值不同的 Font.Color 绘制一段 TEdit.Text。有什么例子可以做到这一点吗?

我正在尝试做这样的事情:

注意:这个截图图片只是一个毛茸茸的草稿,但它让我相信什么问题可以解决。

【问题讨论】:

  • 欢迎您分享您的“解决方案”,以便我们讨论并提供反馈(截图不是解决方案)。
  • @kobik,可能会有一系列简单的问题专门针对我偶然发现的特定问题。但这可能会在以后发生,目前我没有卡住。

标签: delphi vcl


【解决方案1】:

Edit 控件不具有 owner-draw 支持,但您可以 custom-draw 通过对它进行子类化并处理 WM_PAINT(以及许多其他消息)。这是可行的,但实际上 100% 正确实施将是一个痛苦的世界。来自文档:Developing Custom Draw Controls in Visual C++

请注意,所有者绘制适用于大多数控件。但是,它不适用于编辑控件;对于列表控件,它仅适用于报表视图样式

我也很想知道兔子洞有多深,所以,
这是一个使用插入器类的代码示例(仍然需要实现选择,但是当插入符号在控件中时自定义绘图可以工作):

type
  TEdit = class(StdCtrls.TEdit)
  private
    FCanvas: TCanvas;
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  protected
    procedure WndProc(var Message: TMessage); override;
    procedure Paint; virtual;
    procedure PaintWindow(DC: HDC); override;
    property Canvas: TCanvas read FCanvas;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

...

constructor TEdit.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FCanvas := TControlCanvas.Create;
  TControlCanvas(FCanvas).Control := Self;
end;

destructor TEdit.Destroy;
begin
  FCanvas.Free;
  inherited Destroy;
end;

procedure TEdit.Paint;
var
  R: TRect;
  I: Integer;
  S: String;
begin
  R := ClientRect;
  Inc(R.Left, 1);
  Inc(R.Top, 1);
  Canvas.Brush.Assign(Self.Brush);
  Canvas.Font.Assign(Self.Font);
  for I := 1 to Length(Text) do
  begin
    if Text[I] in ['0'..'9'] then
      Canvas.Font.Color := clRed
    else
      Canvas.Font.Color := clGreen;
    S := Text[I];
    DrawText(Canvas.Handle, PChar(S), -1, R, DT_LEFT or DT_NOPREFIX or
      DT_WORDBREAK or DrawTextBiDiModeFlagsReadingOnly);
    Inc(R.Left,Canvas.TextWidth(S));
  end;
end;

procedure TEdit.PaintWindow(DC: HDC);
begin
  FCanvas.Lock;
  try
    FCanvas.Handle := DC;
    try
      TControlCanvas(FCanvas).UpdateTextFlags;
      Paint;
    finally
      FCanvas.Handle := 0;
    end;
  finally
    FCanvas.Unlock;
  end;
end;

procedure TEdit.WMPaint(var Message: TWMPaint);
begin
  ControlState := ControlState+[csCustomPaint];
  inherited;
  ControlState := ControlState-[csCustomPaint];
end;

procedure TEdit.WndProc(var Message: TMessage);
begin
  inherited WndProc(Message);
  with Message do
    case Msg of
      CM_MOUSEENTER, CM_MOUSELEAVE, WM_LBUTTONUP, WM_LBUTTONDOWN,
      WM_KEYDOWN, WM_KEYUP,
      WM_SETFOCUS, WM_KILLFOCUS,
      CM_FONTCHANGED, CM_TEXTCHANGED:
      begin
        Invalidate;
      end;
   end; 
end;

【讨论】:

  • 谢谢,这个例子确实推动了我的学习,并指出了我完全错过的许多细微差别(例如,csCustomPaint)。
  • 由于您没有使用控件自己的表面,因此您必须考虑所有可能的展示案例。例如,除了选择之外,您还必须考虑何时应以第一个字符以外的字符开头显示文本 - 当文本较大以适合控件并且用户已将其滚动到末尾时。实际上,您要做的是开发您的编辑控件,而不是使用一个!..
  • @SertacAkyuz 我刚刚使用了kobik提供的代码,只是对文本的绘制方式进行了更改,您提到的所有情况都得到了妥善处理。当然这是 5 年后在不同的操作系统和 Delphi 版本上,但作为参考,上面的代码非常适合我必须做的事情。
【解决方案2】:

没有。标准 tEdit 不支持自定义绘图或具有多种颜色的文本。作为替代方案,您可以使用带有 WantReturns=False 的 tRichEdit。

【讨论】:

  • 我同意添加自定义绘图支持是一个真正的 PITA,因为它直接来自 TWinControl,但我已经证明了它是非常可行的。
  • @user - 如果您的自定义绘图在插入符号位于控件中时有效,我会感到惊讶。是吗?
  • @SertacAkyuz,说我的尝试到目前为止“有效”将是一个非常大胆的声明:-) 但我不相信这根本不可行。
  • @Mike W,编辑控件不支持所有者绘制,但您可以通过对其进行子类化并处理WM_PAINT(以及许多其他消息)来自定义绘制它。这是可行的,但这将是一个痛苦的世界。
  • @kobik - 同意。我应该说“没有内置支持”
【解决方案3】:

对 kobik 解决方案的一些改进:

procedure TMyEdit.Paint;
var
  R: TRect;
  I: Integer;

  NewColor : TColor;
  NewBackColor : TColor;

  procedure DrawEx(S: String);
  begin
     if ((i-1)>=Self.SelStart) and ((i-1)<=(Self.SelStart+(Self.SelLength-1)))
        and (Self.SelLength>0) and (Self.focused)
       then begin
         Canvas.Font.Color  := clWhite;
         Canvas.Brush.Color := NewColor;
       end else begin
         Canvas.Font.Color  := NewColor;
         Canvas.Brush.Color := NewBackColor;
       end;
     Canvas.Brush.Style := bsSolid;
     DrawText(Canvas.Handle, PChar(S), -1, R, DT_LEFT or DT_NOPREFIX or
       DT_WORDBREAK or DrawTextBiDiModeFlagsReadingOnly);
  end;

begin
  R := ClientRect;
  Inc(R.Left, 1);
  Inc(R.Top, 1);
  Canvas.Brush.Assign(Self.Brush);
  Canvas.Font.Assign(Self.Font);

  if Self.Focused then begin
      NewBackColor       := clYellow;
      Canvas.Brush.Color := NewBackColor;
      Canvas.Brush.Style := bsSolid;
      Canvas.FillRect(ClientRect);
      Canvas.DrawFocusRect(ClientRect);
    end else NewBackColor := clWhite;

  for I:=1 to Length(Text) do begin
   if PasswordChar=#0 then begin
     if Text[I] in ['0'..'9'] then begin
       NewColor := clRed;
       DrawEx(Text[I]);
      end else begin
       NewColor := clGreen;
       DrawEx(Text[I]);
      end;
     Inc(R.Left,Canvas.TextWidth(Text[I]));
    end else begin //with passwordchar
       NewColor := clBlack;
       DrawEx(PasswordChar);
     Inc(R.Left,Canvas.TextWidth(PasswordChar));
    end;
  end;
end;

【讨论】:

    【解决方案4】:

    通过重写 CreateParams 过程的另一个小改进,该过程修复了文本选择期间的闪烁(鼠标移动同时左键按下):

    procedure TMyEdit.CreateParams(var Params: TCreateParams);
    begin
        inherited;
        if csDesigning in ComponentState then
            exit;
        Params.ExStyle := Params.ExStyle or WS_EX_COMPOSITED;
    end;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-27
      • 1970-01-01
      • 2010-12-29
      • 1970-01-01
      • 1970-01-01
      • 2017-04-09
      • 1970-01-01
      相关资源
      最近更新 更多