【问题标题】:How to get a windows 10 style transparent border如何获得 Windows 10 风格的透明边框
【发布时间】:2019-07-05 11:10:47
【问题描述】:

我一直在试验,看看我是否可以在没有运气的情况下使用自定义控件获得相同的效果。

问题是,我想要制作一个可调整大小的面板,例如从 Tcustomcontrol 派生的组件。

我可以使用 WS_BORDER 创建单个像素边框,然后使用 WMNCHitTest 检测边缘。但是如果该控件包含另一个与 alclient 对齐的控件,则鼠标消息会转到该包含的组件而不是包含面板。所以充其量,调整大小的光标仅在它们恰好位于单个像素边框上时才起作用。

更改为 WS_THICKFRAME 显然可行,但会产生丑陋的可见边框。

我注意到 WIN10 表单有一个不可见的粗边框,内部边缘只有一条像素线。因此,调整大小的光标在可见框架之外工作约 6 到 8 个像素,使其更容易选择。

关于他们如何实现这种效果的任何想法以及是否可以在 delphi vcl 控件中轻松复制?

【问题讨论】:

  • @SertacAkyuz:OP 不是在谈论表单上的子控件吗?
  • @AndreasRejbrand:是的,wiindows 10 表单边框与以前的版本不同,您不能使用 Createparams 样式参数复制它。显然如果你制作一个新的vcl表单,你会得到一个Win10的表单。挑战是在自定义组件上复制样式。
  • @Andreas - 也许我对可调整大小的边框感到困惑..
  • 那么你为什么不先处理 WM_NCCALCSIZE 然后再处理 WM_NCHITTEST 呢? ……也许我还是不明白……
  • @SertacAkyuz:我不知道该怎么做。我想知道是否更简单的是无边框并在控件边缘内创建一个透明区域,然后手动绘制边框。

标签: delphi delphi-10.3-rio


【解决方案1】:

您不需要与顶级窗口一起使用的边框,处理WM_NCCALCSIZE 来缩小您的客户区:

procedure TSomeControl.WMNCCalcSize(var Message: TWMNCCalcSize);
begin
  inherited;
  InflateRect(Message.CalcSize_Params.rgrc0, -FBorderWidth, -FBorderWidth);
end;

其中FBorderWidth 是控件周围的假定填充。

句柄 WM_NCHITTEST 用鼠标从边框调整大小。

procedure TSomeControl.WMNCHitTest(var Message: TWMNCHitTest);
var
  Pt: TPoint;
begin
  inherited;
  Pt := ScreenToClient(Point(Message.XPos, Message.YPos));
  if Pt.X < 0 then
    Message.Result := HTLEFT;
  ...

当然,你必须根据自己的喜好绘制边框。


这是我的完整测试单元:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  extctrls;

type
  TSomeControl = class(TCustomControl)
  private
    FBorderWidth: Integer;
  protected
    procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
    procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
    procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT;
  public
    constructor Create(AOwner: TComponent); override;
  end;

{ TSomeControl }

constructor TSomeControl.Create(AOwner: TComponent);
begin
  inherited;
  FBorderWidth := 5;
  ControlStyle := ControlStyle + [csAcceptsControls];
end;

procedure TSomeControl.WMNCCalcSize(var Message: TWMNCCalcSize);
begin
  inherited;
  InflateRect(Message.CalcSize_Params.rgrc0, -FBorderWidth, -FBorderWidth);
end;

procedure TSomeControl.WMNCHitTest(var Message: TWMNCHitTest);
var
  Pt: TPoint;
begin
  inherited;
  Pt := ScreenToClient(Point(Message.XPos, Message.YPos));
  if Pt.X < 0 then
    Message.Result := HTLEFT;
  if Pt.Y < 0 then
    Message.Result := HTTOP;
  if Pt.X > ClientWidth then
    Message.Result := HTRIGHT;
  if Pt.Y > ClientHeight then
    Message.Result := HTBOTTOM;
end;

procedure TSomeControl.WMNCPaint(var Message: TWMNCPaint);
var
  DC: HDC;
begin
  DC := GetWindowDC(Handle);
  SelectClipRgn(DC, 0);
  SelectObject(DC, GetStockObject(BLACK_PEN));
  SelectObject(DC, GetStockObject(GRAY_BRUSH));
  Rectangle(DC, 0, 0, Width, Height);
  ReleaseDC(Handle, DC);
end;

//---------------------------------------

procedure TForm1.Button1Click(Sender: TObject);
var
  C: TSomeControl;
  P: TPanel;
begin
  C := TSomeControl.Create(Self);
  C.SetBounds(30, 30, 120, 80);
  C.Parent := Self;

  P := TPanel.Create(Self);
  P.Parent := C;
  P.Align := alClient;
end;

end.

【讨论】:

  • 感谢 Sertac。我想我需要以某种方式获取控件下方内容的位图并将其绘制到边框以使其看起来透明。
  • @Andy - 您在寻找完全透明还是某种光泽效果?
  • 我希望获得完全透明的 Windows 10 表单。我确实尝试设置一个区域,它产生了效果,但是尺寸大小的光标停止在该区域工作,所以它打败了对象。我已将边框宽度设置为 3 像素,并以中途房子的父颜色着色。我认为谷歌浏览器是完全无边界的,它的所有边界都在里面绘制,因为它对 Windows 7 和 10 都有影响。
  • 要拥有完全透明的边框,您必须具有 WS_EX_TRANSPARENT 窗口样式,否则您将无法说服兄弟姐妹或父母绘制重叠区域。通过覆盖 CreateParams 来做到这一点。然后在 WM_NCPAINT 处理程序中什么也不做,或者根本不处理消息。不要忘记绘制客户区域,否则内部区域也将保持透明。
  • 顺便说一句,我没有看到 W7 上的 chrome 有任何异常,除了它似乎在玻璃框架上绘制。我无权访问任何 W10 系统,我也不喜欢访问它..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-12
  • 2011-08-17
  • 2015-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多