Qt无边框窗体Windows篇

  • 去掉标题栏和边框
  • 实现拖拽功能
  • 还原窗体功能
  • 注意点
  • 我们可以做的更好
    • 添加阴影
    • 亚克力面板效果
  • 结语

去掉标题栏和边框

首先第一步我们要通过设置系统绘制的边框消失

setWindowFlags(Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint);

通过上述设置我们可以看见原来一个带标题栏的窗体变成了一个白块,这算是无边框窗体的第一步,但是一个窗体需要有窗体应有的功能,比如可以拖拽,我们还需要再进行几步。

实现拖拽功能

一个白色的框框,我们发现不能像原来那样拖动了,相信大家在网上也看过许多如何实现无边框窗体的拖拽方法,现在我给出这样一个方法,重写鼠标事件

void mousePressEvent(QMouseEvent* event)
{#ifdef Q_OS_WINif (ReleaseCapture())SendMessage(HWND(winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);event->ignore();
#else
#endif}

还原窗体功能

首先我们得设置成这样

HWND hWnd = (HWND)this->winId();
DWORD style = ::GetWindowLong(hWnd, GWL_STYLE);
SetWindowLong(hWnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION | CS_DBLCLKS);

我们可以看见在Windows上你最大化窗口,或者说是最小化窗口,拖拽到边缘停靠,会有动画的,我们设置了无边框以后,这些动画也就随之消失了,通过上述的设置,可以让这些动画重新出现。

接下来就是可以拉伸窗体的部分了,首先我们要了解一个事件,nativeEvent(),我们要做的就是重写它。

bool BaseView::nativeEvent(const QByteArray & eventType, void * message, long * result)
{Q_UNUSED(eventType)MSG* msg = reinterpret_cast<MSG*>(message);  switch (msg->message) {case WM_NCHITTEST:{       *result = 0;const LONG border_width = 5;RECT winrect;GetWindowRect(reinterpret_cast<HWND>(winId()), &winrect);long x = GET_X_LPARAM(msg->lParam);long y = GET_Y_LPARAM(msg->lParam);auto resizeWidth = minimumWidth() != maximumWidth();auto resizeHeight = minimumHeight() != maximumHeight();if (resizeWidth){//左边if (x >= winrect.left && x < winrect.left + border_width){*result = HTLEFT;}//右边if (x < winrect.right && x >= winrect.right - border_width){*result = HTRIGHT;}}if (resizeHeight){//底边if (y < winrect.bottom && y >= winrect.bottom - border_width){*result = HTBOTTOM;}//top borderif (y >= winrect.top && y < winrect.top + border_width){*result = HTTOP;}}if (resizeWidth && resizeHeight){//左底边if (x >= winrect.left && x < winrect.left + border_width &&y < winrect.bottom && y >= winrect.bottom - border_width){*result = HTBOTTOMLEFT;}//右底边if (x < winrect.right && x >= winrect.right - border_width &&y < winrect.bottom && y >= winrect.bottom - border_width){*result = HTBOTTOMRIGHT;}//左上边if (x >= winrect.left && x < winrect.left + border_width &&y >= winrect.top && y < winrect.top + border_width){*result = HTTOPLEFT;}//右上边if (x < winrect.right && x >= winrect.right - border_width &&y >= winrect.top && y < winrect.top + border_width){*result = HTTOPRIGHT;}}if (*result != 0)return true;break;}default:return QWidget::nativeEvent(eventType, message, result);}return QWidget::nativeEvent(eventType, message, result);
}

其实上述方法在MSDN里也是可以查到的,只是在这里我们用到了Qt里边。

注意点

到这里其实这个窗体已经做到了一个窗体该有的功能,当然一些功能性的按钮还是要自定义的,但是它已经相当于一个很好的白板,可以提供给你自由发挥。但是仔细的人会发现,这样的窗体在最大化之后,会出现一点像素点偏移的情况。
经过查找资料得知,这是因为一个窗体在最大化的时候,系统会把边框的宽度考虑进去,经过计算再最大化,但是现在我们去掉了边框,可系统还是把边框的宽度考虑进去,所以会出现偏移几个像素点的情况,现在给出一个本人认为不错的解决方案。

在头文件里写下几个函数

private:auto composition_enabled() -> bool {auto composition_enabled = FALSE;auto success = ::DwmIsCompositionEnabled(&composition_enabled) == S_OK;return composition_enabled && success;}auto ifMaximized(HWND hwnd) -> bool{WINDOWPLACEMENT placement{};if (!::GetWindowPlacement(hwnd, &placement)) { return false; }return placement.showCmd == SW_MAXIMIZE;}auto adjust_maximized_client_rect(HWND window, RECT& rect) -> void{if (!ifMaximized(window)) { return; }auto monitor = ::MonitorFromWindow(window, MONITOR_DEFAULTTONULL);if (!monitor) { return; }MONITORINFO monitor_info{};monitor_info.cbSize = sizeof(monitor_info);if (!::GetMonitorInfoW(monitor, &monitor_info)) { return; }rect = monitor_info.rcWork;}

对于这些函数的应用,我们还是要回到**nativeEvent()**里面。

case WM_GETMINMAXINFO: {MINMAXINFO* mmi = reinterpret_cast<MINMAXINFO*>(msg->lParam);if (ifMaximized(msg->hwnd)) {RECT window_rect{};if (!GetWindowRect(msg->hwnd, &window_rect)) {return false;}HMONITOR monitor = MonitorFromRect(&window_rect, MONITOR_DEFAULTTONULL);if (!monitor) {return false;}MONITORINFO monitor_info = { 0 };monitor_info.cbSize = sizeof(monitor_info);GetMonitorInfo(monitor, &monitor_info);RECT work_area = monitor_info.rcWork;RECT monitor_rect = monitor_info.rcMonitor;mmi->ptMaxPosition.x = abs(work_area.left - monitor_rect.left);mmi->ptMaxPosition.y = abs(work_area.top - monitor_rect.top);mmi->ptMaxSize.x = abs(work_area.right - work_area.left);mmi->ptMaxSize.y = abs(work_area.bottom - work_area.top);mmi->ptMaxTrackSize.x = mmi->ptMaxSize.x;mmi->ptMaxTrackSize.y = mmi->ptMaxSize.y;*result = 1;return true;}}case WM_NCACTIVATE: {if (!composition_enabled()) {*result = 1;return true;}break;}case WM_SIZE: {RECT winrect;GetClientRect(msg->hwnd, &winrect);WINDOWPLACEMENT wp;wp.length = sizeof(WINDOWPLACEMENT);GetWindowPlacement(msg->hwnd, &wp);if (this){if (wp.showCmd == SW_MAXIMIZE){::SetWindowPos(reinterpret_cast<HWND>(winId()), Q_NULLPTR, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);}}}

关于Windows平台上的这些消息,不在本文的内容里,如果想要了解更多,可以自己去搜索相关方面的资料。

我们可以做的更好

添加阴影

窗体的阴影让窗体的辨识度更高,窗体也更好看,既然在Windows平台下,我们就使用Windows平台的解决方案。
Windows画出阴影,使用的是DWM,我们也是要借助它。
首先添加

#pragma comment (lib,"Dwmapi.lib")

接着添加如下代码

BOOL bEnable = false;
::DwmIsCompositionEnabled(&bEnable);
if (bEnable)
{DWMNCRENDERINGPOLICY ncrp = DWMNCRP_ENABLED;::DwmSetWindowAttribute((HWND)winId(), DWMWA_NCRENDERING_POLICY, &ncrp, sizeof(ncrp));MARGINS margins = { -1 };::DwmExtendFrameIntoClientArea((HWND)winId(), &margins);
}

亚克力面板效果

我们很喜欢的一个效果有毛玻璃效果,也叫高斯模糊的效果,关于Windows7开启毛玻璃效果在Qt的实例里面已经有了,本文也不再赘述。Windows10下的叫亚克力面板效果,但是通常在一些UWP软件里才看到这种效果的运用,其实我们在Qt里也能运用。
首先要自定义一个这样的头文件。
WindowCompositionAttribute.h

#pragma once
#include <QtWinExtras>
#include <Windows.h>
#include <dwmapi.h>
typedef enum _WINDOWCOMPOSITIONATTRIB
{WCA_UNDEFINED = 0,WCA_NCRENDERING_ENABLED = 1,WCA_NCRENDERING_POLICY = 2,WCA_TRANSITIONS_FORCEDISABLED = 3,WCA_ALLOW_NCPAINT = 4,WCA_CAPTION_BUTTON_BOUNDS = 5,WCA_NONCLIENT_RTL_LAYOUT = 6,WCA_FORCE_ICONIC_REPRESENTATION = 7,WCA_EXTENDED_FRAME_BOUNDS = 8,WCA_HAS_ICONIC_BITMAP = 9,WCA_THEME_ATTRIBUTES = 10,WCA_NCRENDERING_EXILED = 11,WCA_NCADORNMENTINFO = 12,WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,WCA_VIDEO_OVERLAY_ACTIVE = 14,WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,WCA_DISALLOW_PEEK = 16,WCA_CLOAK = 17,WCA_CLOAKED = 18,WCA_ACCENT_POLICY = 19,WCA_FREEZE_REPRESENTATION = 20,WCA_EVER_UNCLOAKED = 21,WCA_VISUAL_OWNER = 22,WCA_LAST = 23
} WINDOWCOMPOSITIONATTRIB;typedef struct _WINDOWCOMPOSITIONATTRIBDATA
{WINDOWCOMPOSITIONATTRIB Attrib;PVOID pvData;SIZE_T cbData;
} WINDOWCOMPOSITIONATTRIBDATA;typedef enum _ACCENT_STATE
{ACCENT_DISABLED = 0,ACCENT_ENABLE_GRADIENT = 1,ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,ACCENT_ENABLE_BLURBEHIND = 3,ACCENT_INVALID_STATE = 4
} ACCENT_STATE;typedef struct _ACCENT_POLICY
{ACCENT_STATE AccentState;DWORD AccentFlags;DWORD GradientColor;DWORD AnimationId;
} ACCENT_POLICY;WINUSERAPI
BOOL
WINAPI
GetWindowCompositionAttribute(_In_ HWND hWnd,_Inout_ WINDOWCOMPOSITIONATTRIBDATA* pAttrData);typedef BOOL(WINAPI*pfnGetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);WINUSERAPI
BOOL
WINAPI
SetWindowCompositionAttribute(_In_ HWND hWnd,_Inout_ WINDOWCOMPOSITIONATTRIBDATA* pAttrData);typedef BOOL(WINAPI*pfnSetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);

这使用的是为未公开的api,具体运用是这样的

HWND hWnd = HWND(winId());
HMODULE hUser = GetModuleHandle(L"user32.dll");if (hUser){pfnSetWindowCompositionAttribute setWindowCompositionAttribute =(pfnSetWindowCompositionAttribute)GetProcAddress(hUser, "SetWindowCompositionAttribute");if (setWindowCompositionAttribute){ACCENT_POLICY accent = { ACCENT_ENABLE_BLURBEHIND, 0, 0, 0 };WINDOWCOMPOSITIONATTRIBDATA data;data.Attrib = WCA_ACCENT_POLICY;data.pvData = &accent;data.cbData = sizeof(accent);setWindowCompositionAttribute(hWnd, &data);}}

结语

本文零零碎碎给出了一些代码片段,如果想具体知道如何运用在项目里,欢迎来我的仓库:翟长腿的小仓库
如果觉得好可以点个星星

Qt无边框窗体(Windows)相关推荐

  1. QT无边框窗体改变大小 很顺畅

    近期,做项目用到无边框窗体,令人蛋疼的是无边框窗体大小的改变要像右边框那样,上下左右四周,而且要流畅. 网上也找了些代码,发现居然还要连接到windows事件,这显然不合常理,后来自己新建了demo, ...

  2. 01.WPF中制作无边框窗体

    [引用:]http://blog.csdn.net/johnsuna/article/details/1893319 众所周知,在WinForm中,如果要制作一个无边框窗体,可以将窗体的FormBor ...

  3. 【C#】使用DWM实现无边框窗体阴影或全透窗体

    1.无边框窗体阴影,win7(需要开启Aero效果)及以上系统 public class LdwmForm : Form{public LdwmForm(){Initialize();}/// < ...

  4. 拖动无边框窗体(VB6代码)

    简单代码片段,记录一下备用. Option Explicit Dim mX As Long, mY As Long Private Sub Form_MouseDown(Button As Integ ...

  5. C# WinForm 无边框窗体,加阴影、拖动、改变大小等功能完美实现(自认为是完美的 ^=^)

    关于Winform的无边框窗体实现,网络上有很多大牛文章,这里不赘述.我也是参考网络上的思路,在使用别人的代码基础上,发现和遇到了很多小问题,所以做了改造,以下做个记录,也是给需要的人提供一点思路,如 ...

  6. WPF 调用API修改窗体风格实现真正的无边框窗体

    原文:WPF 调用API修改窗体风格实现真正的无边框窗体 WPF中设置无边框窗体似乎是要将WindowStyle设置为None,AllowTransparency=true,这样才能达到WinForm ...

  7. 无边框窗体和用户控件以及权限

    无边框窗体: 就是吧窗体的边框去掉,然后自己做按钮设置功能. 无边框窗体的移动: 将下面代码直接复制粘贴,将窗体的鼠标按下事件的方法改成下面方法的名字就可以直接使用 1 //窗体移动API 2 [Dl ...

  8. 再谈无边框窗体的操作

    本文介绍操作无边框窗体的其他几个方面的技巧. 设置浮动菜单 通常情况下,在无边框窗体中不能设置菜单.如果在无边框窗体中设置了菜单,运行时窗体上就会出现标题栏.那么,是否在无边框窗体中就不能使用菜单了呢 ...

  9. 无边框窗体移动的方法

    文章目录 1 无边框窗体移动的方法 1.1 无边框窗体移动的方法 1 无边框窗体移动的方法 1.1 无边框窗体移动的方法 当我们把窗体设置为无边框后,会发现用鼠标拖动窗体时是没有反应的,我们需要在窗体 ...

最新文章

  1. early EOF fatal: index-pack failed
  2. 03 Java程序员面试宝典视频课程之常用类
  3. python文件输出-Python 文件和输入输出小结
  4. C语言实现malloc_dbg,calloc_dbg,free_dbg和printLeaks(附完整源码)
  5. Yii2的MVC新特性
  6. 成为优秀程序员应该具备的8个特质
  7. (34)css光标属性cursor
  8. 计算机病毒小学教师资格证面试,小学信息技术人教版四年级上册第15课《病毒防治及时做》优质课公开课教案教师资格证面试试讲教案...
  9. Airflow 中文文档:集成
  10. ubuntu PPA
  11. C++中const使用总结
  12. JMS学习十一(ActiveMQ Consumer高级特性之独有消费者(Exclusive Consumer))
  13. 【生信进阶练习1000days】day6-OrganismDb packages
  14. kali wifi 破解 字典制作
  15. 那些开挂的人,如何打败50%的竞争者?
  16. STM32CubeMonitor使用教程
  17. Android显示——一帧的渲染过程(VSYNC)
  18. 优卡仕广告一体机——商用显示设备专家
  19. 北航计算机考博经验,北航考博经验总结和感受
  20. 【AP】Robust multi-period portfolio selection(3)

热门文章

  1. FileZilla Server源码分析
  2. 设计模式之门面设计模式
  3. Linux 环境下部署Hexagon SDK 开发环境
  4. MCE公司:药研发的守护天使-MCE化合物库之先导篇
  5. 为了给YiYi节省时间,写了个能自动拼图贴水印的机器人,很多bug,能用就行。
  6. .NET经典图书推荐(下)
  7. 汽车UDS诊断之通过标识符写入数据服务(0x2E)深度剖析
  8. 小学英语阅读促进学生思维品质发展及其策略应用的综述
  9. 艺术字体生成器v1.0绿色免费版
  10. 一些实用的安卓UI设计工具