如何在Delphi 10.2中实现类似Office2016的带BorderIcons的无边框窗体
实现Delphi 10.2窗体类似Office 2016外观的方案
我之前折腾过类似的需求,给你分享几个在Delphi 10.2里实现Office 2016风格窗体的靠谱方案,分标题栏自定义和阴影效果两部分来说:
一、自定义无标题栏但保留最小化/最大化/关闭按钮
方法1:自绘标题栏与按钮(无需第三方组件)
如果你不想依赖第三方样式或组件,纯自绘是最灵活的方式:
隐藏默认标题栏:
直接设BorderStyle为bsNone会连系统按钮一起丢,更稳妥的是用API修改窗口样式,保留系统按钮但隐藏标题栏:procedure TMainForm.FormCreate(Sender: TObject); var WinStyle: DWORD; begin WinStyle := GetWindowLongPtr(Handle, GWL_STYLE); // 去掉标题栏,保留最小化、最大化、系统菜单按钮 WinStyle := WinStyle and not WS_CAPTION or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or WS_SYSMENU; SetWindowLongPtr(Handle, GWL_STYLE, WinStyle); // 调整客户区大小,把原来标题栏的空间利用起来 AdjustClientRect; end;做可拖动的自定义标题栏:
拖个TPanel到窗体顶部,设Align=alTop,高度30左右(和Office 2016标题栏差不多)。然后重写WM_NCHITTEST消息,让这个Panel区域能拖动窗体:procedure TMainForm.WMNCHitTest(var Message: TWMNCHitTest); var Pt: TPoint; begin inherited; Pt := ScreenToClient(Point(Message.XPos, Message.YPos)); // 判断点击位置是否在标题栏Panel内 if (Pt.Y >= 0) and (Pt.Y <= pnlTitleBar.Height) then Message.Result := HTCAPTION; end;自绘带hover效果的系统按钮:
别用图片切换状态了,自绘按钮更流畅还没闪烁。比如继承TButton写个关闭按钮:type TOfficeCloseButton = class(TButton) private FIsHovered: Boolean; procedure WMMouseEnter(var Message: TMessage); message WM_MOUSEENTER; procedure WMMouseLeave(var Message: TMessage); message WM_MOUSELEAVE; protected procedure Paint; override; end; procedure TOfficeCloseButton.WMMouseEnter(var Message: TMessage); begin inherited; FIsHovered := True; Invalidate; end; procedure TOfficeCloseButton.WMMouseLeave(var Message: TMessage); begin inherited; FIsHovered := False; Invalidate; end; procedure TOfficeCloseButton.Paint; var Canvas: TCanvas; Rect: TRect; begin Canvas := Self.Canvas; Canvas.Brush.Style := bsClear; Canvas.Pen.Style := psClear; Rect := ClientRect; // Hover时显示Office风格的红色背景,正常用默认按钮色 if FIsHovered then Canvas.Brush.Color := $FFE81123 else Canvas.Brush.Color := clBtnFace; Canvas.FillRect(Rect); // 绘制白色叉号 Canvas.Pen.Color := clWhite; Canvas.Pen.Width := 2; Canvas.MoveTo(Rect.Left + 8, Rect.Top + 8); Canvas.LineTo(Rect.Right - 8, Rect.Bottom - 8); Canvas.MoveTo(Rect.Right - 8, Rect.Top + 8); Canvas.LineTo(Rect.Left + 8, Rect.Bottom - 8); end;最大化、最小化按钮同理,换成对应的图标(横线、方框)就行,hover状态也可以用类似的逻辑改背景色。
方法2:用VCL Styles快速搞定
Delphi 10.2自带VCL Styles支持,找个适配Office 2016的样式文件(.vsf)导入项目,然后:
- 把窗体
Caption设为空,调整ClientHeight去掉默认标题栏的空间。 - 样式自带的系统按钮已经做好了hover状态,不用自己写任何绘制代码,效果还很贴合Office风格,省事儿得很。
二、实现Office 2016风格的窗体阴影
Office 2016的阴影是系统级的柔和阴影,用Windows的DWM API就能实现:
先检查DWM是否可用:
毕竟Win7以下系统不支持DWM,加个判断:function IsDWMEnabled: Boolean; var Enabled: BOOL; begin Result := DwmIsCompositionEnabled(Enabled) = S_OK and Enabled; end;启用阴影效果:
在窗体OnCreate事件里调用API:procedure TMainForm.FormCreate(Sender: TObject); var DWMAttribute: DWORD; ExStyle: DWORD; begin if IsDWMEnabled then begin // 开启DWM渲染 DWMAttribute := 1; DwmSetWindowAttribute(Handle, DWMA_NCRENDERING_ENABLED, @DWMAttribute, SizeOf(DWMAttribute)); // 设置主窗口类型的阴影,和Office风格一致 DWMAttribute := DWMSBT_MAINWINDOW; DwmSetWindowAttribute(Handle, DWMA_SHADOW, @DWMAttribute, SizeOf(DWMAttribute)); end; // 如果是bsNone样式,加个WS_EX_WINDOWEDGE扩展样式,不然阴影可能不显示 ExStyle := GetWindowLongPtr(Handle, GWL_EXSTYLE); ExStyle := ExStyle or WS_EX_WINDOWEDGE; SetWindowLongPtr(Handle, GWL_EXSTYLE, ExStyle); end;小提示:
- 阴影会自动适配系统主题,和Office 2016的阴影效果一致。
- 如果是高DPI环境,记得开启Delphi的PerMonitorV2 DPI感知,避免阴影位置偏移。
额外注意
- 自绘按钮时记得把
DoubleBuffered设为True,能有效避免闪烁。 - 测试时多切换系统主题,确保样式在不同主题下都正常显示。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

