【问题标题】:How to draw a custom border inside the non client area of a control with scroll bars?如何在带有滚动条的控件的非客户区域内绘制自定义边框?
【发布时间】:2014-12-18 07:27:09
【问题描述】:

我有一个启用了两个滚动条的自定义控件,我想在客户区和滚动条周围绘制一个简单的红线边框,如下图所示。我该怎么做?

这是控制代码:

unit SuperList;

interface

uses Windows, Controls, Graphics, Classes, Messages, SysUtils, StdCtrls;

type

  TSuperList = class(TCustomControl) 
  protected
    procedure   Paint; override;
    procedure   CreateParams(var Params: TCreateParams); override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

implementation

procedure TSuperList.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.Style:=Params.Style or WS_VSCROLL or WS_HSCROLL;
end;

constructor TSuperList.Create(AOwner: TComponent);
begin
 inherited;
 Color:=clBlack;
 Width:=300;
 Height:=250;
end;

procedure TSuperList.Paint;
begin
 Canvas.Pen.Color:=clNavy;
 Canvas.Brush.Color:=clWhite;
 Canvas.Rectangle(ClientRect);   // a test rectangle te see the client area
end;

end.

【问题讨论】:

    标签: delphi custom-controls delphi-2009


    【解决方案1】:

    发布BorderWidth属性,并实现WM_NCPAINT消息处理程序,如this answer所示,结合this answer中的代码:

    type
      TSuperList = class(TCustomControl)
      private
        procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
        procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT;
      protected
        procedure CreateParams(var Params: TCreateParams); override;
        procedure Paint; override;
      public
        constructor Create(AOwner: TComponent); override;
      published
        property BorderWidth default 10;
      end;
    
    implementation
    
    constructor TSuperList.Create(AOwner: TComponent);
    begin
      inherited Create(Aowner);
      ControlStyle := ControlStyle - [csOpaque];
      BorderWidth := 10;
    end;
    
    procedure TSuperList.CreateParams(var Params: TCreateParams);
    begin
      inherited CreateParams(Params);
      Params.Style := Params.Style or WS_VSCROLL or WS_HSCROLL;
      Params.WindowClass.style :=
        Params.WindowClass.style and not (CS_HREDRAW or CS_VREDRAW);
    end;
    
    procedure TSuperList.Paint;
    begin
      Canvas.Brush.Color := RGB(Random(255), Random(255), Random(255));
      Canvas.FillRect(Canvas.ClipRect);
    end;
    
    procedure TSuperList.WMEraseBkgnd(var Message: TWMEraseBkgnd);
    begin
      Message.Result := 1;
    end;
    
    procedure TSuperList.WMNCPaint(var Message: TWMNCPaint);
    var
      DC: HDC;
      R: TRect;
      WindowStyle: Integer;
    begin
      inherited;
      if BorderWidth > 0 then
      begin
        DC := GetWindowDC(Handle);
        try
          R := ClientRect;
          OffsetRect(R, BorderWidth, BorderWidth);
          ExcludeClipRect(DC, R.Left, R.Top, R.Right, R.Bottom);
          WindowStyle := GetWindowLong(Handle, GWL_STYLE);
          if WindowStyle and WS_VSCROLL <> 0 then
            ExcludeClipRect(DC, R.Right, R.Top,
              R.Right + GetSystemMetrics(SM_CXVSCROLL), R.Bottom);
          if WindowStyle and WS_HSCROLL <> 0 then
            ExcludeClipRect(DC, R.Left, R.Bottom, R.Right,
              R.Bottom + GetSystemMetrics(SM_CXHSCROLL));
          SetRect(R, 0, 0, Width + BorderWidth, Height + BorderWidth);
          Brush.Color := clRed;
          FillRect(DC, R, Brush.Handle);
        finally
          ReleaseDC(Handle, DC);
        end;
      end;
      Message.Result := 0;
    end;
    

    【讨论】:

    • 不幸的是,这在 Windows 7 Home Premium、64 位、启用 Aero、Delphi 2009 中无法正常工作。如果您将控件部分移出显示器然后再移回,滚动条的部分总是可见的被涂上。
    • @And 对不起,但我(还没有)给你解决方案。 (顺便说一句,我发现故障在于TScrollBox 也有BorderWidth&gt;0,所以它可能与这段代码无关。)
    【解决方案2】:

    您正在尝试在Nonclient Area 中绘制(部分)。
    您可以将WS_DLGFRAME 添加到Params.Style 并处理消息WM_NCPaint 在窗口的 HDC 上绘画。

      TSuperList = class(TCustomControl)
      private
        procedure PaintBorder;
        procedure WMNCActivate(var Msg: TWMNCActivate); message WM_NCActivate;
        procedure WMNCPaint(var Msg: TWMNCPaint);message WM_NCPaint;
      protected
        procedure   Paint; override;
        procedure   CreateParams(var Params: TCreateParams); override;
      public
        constructor Create(AOwner: TComponent); override;
      end;
    
    procedure TSuperList.CreateParams(var Params: TCreateParams);
    begin
      inherited;
      Params.Style:=Params.Style or WS_VSCROLL or WS_HSCROLL or WS_DLGFRAME;
    end;
    
    procedure TSuperList.WMNCActivate(var Msg: TWMNCActivate);
    begin
      inherited;
      PaintBorder;
    end;
    
    procedure TSuperList.WMNCPaint(var Msg: TWMNCPaint);
    begin
      inherited;
      PaintBorder;
    end;
    
    procedure TSuperList.PaintBorder;
    begin
      Canvas.Handle := GetWindowDC(Handle);
      Canvas.Pen.Color := clNavy;
      Canvas.Pen.Width := 2;
      Canvas.Brush.Style := bsClear;
      Canvas.Rectangle( Rect(1,1,Width,Height) );
      ReleaseDC(Handle,Canvas.Handle);
    end;    
    
    constructor TSuperList.Create(AOwner: TComponent);
    begin
     inherited;
     Color:=clBlack;
     Width:=300;
     Height:=250;
    end;
    
    procedure TSuperList.Paint;
    begin
     Canvas.Brush.Color:=clWhite;
     Canvas.Pen.Style := psClear;
     Canvas.Rectangle(ClientRect);
     Canvas.Pen.Style := psSolid;
     Canvas.Ellipse(0,0,20,20);
    end;
    

    【讨论】:

    • 好的,但我希望边框的宽度是可变的,WS_DLGFRAME 只分配一个固定的 2 像素边框。
    • WS_THICKFRAME 会给你更多的空间,如果你需要一个完全由你控制的变量空间,你可以考虑创建一个以超级列表作为子组件的组件。
    猜你喜欢
    • 2020-12-10
    • 2010-09-27
    • 2013-08-01
    • 2014-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-29
    • 1970-01-01
    相关资源
    最近更新 更多