1 开发测试环境

        

IDE:VS2017

Qt:5.14.2 x64

操作系统:Win10、Win11

实现效果

2 实现

网上搜索Qt窗口的无边框化实现方案有很多,但从实现的难易程度,以及效果上来说,最优

的还是通过Windows消息来实现,即重新实现Qt窗口的nativeEvent方法。该方案不仅实现了无边

框窗口,还保留了窗口的拖动、贴边等系统特性,以下为实现代码(以QWidget为例,

QMainWindow、QDialog可以平移过去):

#include "ui/frameless_widget.h"#include <windows.h>
#include <windowsx.h>#pragma comment (lib,"user32.lib")FramelessWidget::FramelessWidget(QWidget *parent) : QWidget(parent)
, movale_(true)
, resizable_(true)
, border_width_(5)
, title_bar_(Q_NULLPTR)
, just_maximized_(false)
{this->setWindowFlags(this->windowFlags() | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint);this->installEventFilter(this);HWND hwnd = (HWND)this->winId();DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION);
}FramelessWidget::~FramelessWidget()
{
}void FramelessWidget::setTitleBar(QWidget *title_bar)
{title_bar_ = title_bar;title_bar_->installEventFilter(this);
}void FramelessWidget::showEvent(QShowEvent *event)
{this->setAttribute(Qt::WA_Mapped);QWidget::showEvent(event);
}bool FramelessWidget::eventFilter(QObject *watched, QEvent *event)
{if (watched == this){if (event->type() == QEvent::WindowStateChange){if (this->windowState() == Qt::WindowNoState){movale_ = true;resizable_ = true;}else{movale_ = false;resizable_ = false;}emit signalWindowStateChanged(!movale_);}}return QWidget::eventFilter(watched, event);
}bool FramelessWidget::nativeEvent(const QByteArray &event_type, void *message, long *result)
{MSG *msg = (MSG *)message;switch (msg->message){case WM_NCCALCSIZE:{//this kills the window frame and title bar we added with WS_THICKFRAME and WS_CAPTION*result = 0;return true;}case WM_NCHITTEST:{*result = 0;const LONG border_width = border_width_;RECT winrect;GetWindowRect(HWND(winId()), &winrect);long x = GET_X_LPARAM(msg->lParam);long y = GET_Y_LPARAM(msg->lParam);bool resize_width = minimumWidth() != maximumWidth();bool resize_height = minimumHeight() != maximumHeight();if (resize_width){//left borderif (x >= winrect.left && x < winrect.left + border_width){*result = HTLEFT;}//right borderif (x < winrect.right && x >= winrect.right - border_width){*result = HTRIGHT;}}if (resize_height){//bottom borderif (y < winrect.bottom && y >= winrect.bottom - border_width){*result = HTBOTTOM;}//top borderif (y >= winrect.top && y < winrect.top + border_width){*result = HTTOP;}}if (resize_width && resize_height){//bottom left cornerif (x >= winrect.left && x < winrect.left + border_width &&y < winrect.bottom && y >= winrect.bottom - border_width){*result = HTBOTTOMLEFT;}//bottom right cornerif (x < winrect.right && x >= winrect.right - border_width &&y < winrect.bottom && y >= winrect.bottom - border_width){*result = HTBOTTOMRIGHT;}//top left cornerif (x >= winrect.left && x < winrect.left + border_width &&y >= winrect.top && y < winrect.top + border_width){*result = HTTOPLEFT;}//top right cornerif (x < winrect.right && x >= winrect.right - border_width &&y >= winrect.top && y < winrect.top + border_width){*result = HTTOPRIGHT;}}//*result still equals 0, that means the cursor locate OUTSIDE the frame area//but it may locate in titlebar areaif (*result == 0){if (!title_bar_) return false;QPoint pos = title_bar_->mapFromGlobal(QPoint(x, y));if (!title_bar_->rect().contains(pos)) return false;QWidget* child = title_bar_->childAt(pos);if (!child){*result = HTCAPTION;return true;}else {/*if (_lpWhiteList.contains(child)){*result = HTCAPTION;return true;}*/return false;}}return true;} //end case WM_NCHITTESTcase WM_GETMINMAXINFO:{if (::IsZoomed(msg->hwnd)) {RECT frame = { 0, 0, 0, 0 };AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0);//record frame area dataframes_.setLeft(abs(frame.left));frames_.setTop(abs(frame.bottom));frames_.setRight(abs(frame.right));frames_.setBottom(abs(frame.bottom));QWidget::setContentsMargins(frames_.left() + margins_.left(), \frames_.top() + margins_.top(), \frames_.right() + margins_.right(), \frames_.bottom() + margins_.bottom());just_maximized_ = true;}else {if (just_maximized_){QWidget::setContentsMargins(margins_);//after window back to normal size from maximized state//a twinkle will happen, to avoid this twinkle//repaint() is important used just before the window back to normalrepaint();frames_ = QMargins();just_maximized_ = false;}}return false;}default:return QWidget::nativeEvent(event_type, message, result);}
}

3 扩展

以上,已经实现一个无边框窗口,可以拖动边框进行放大、缩小,以及通过点击任务栏标签

进行最小化和还原。如果想进一步实现窗口的拖动以及贴边特性,则需要实现一个窗口标题栏,并

通过setTitleBar进行设置。为了方便扩展,我们提供一个中间适配窗口,继承自

FramelessWidget,实现基础的标题栏,并对外提供设置标题、隐藏最小化、最大化按钮等接口,

当软件有多个无边框窗口需求时,可以继承适配窗口,并通过接口定制标题栏显示内容。

#include "ui/base_widget.h"BaseWidget::BaseWidget(QString title, QWidget *parent) : FramelessWidget(parent)
, layout_(Q_NULLPTR)
, title_bar_(Q_NULLPTR)
, title_bar_layout_(Q_NULLPTR)
, sys_icon_(Q_NULLPTR)
, title_(Q_NULLPTR)
, right_layout1_(Q_NULLPTR)
, right_layout2_(Q_NULLPTR)
, minimize_button_(Q_NULLPTR)
, switch_button_(Q_NULLPTR)
, close_button_(Q_NULLPTR)
, content_widget_(Q_NULLPTR)
, status_bar_(Q_NULLPTR)
, message_(Q_NULLPTR)
{initUI();title_->setText(title);this->setTitleBar(title_bar_);connect(this, &BaseWidget::signalWindowStateChanged, this, &BaseWidget::slotWindowStateChanged);connect(minimize_button_, &QToolButton::clicked, this, &BaseWidget::slotMinimizeButtonClicked);connect(switch_button_, &QToolButton::clicked, this, &BaseWidget::slotMaximizeButtonClicked);connect(close_button_, &QToolButton::clicked, this, &BaseWidget::slotCloseButtonClicked);
}BaseWidget::~BaseWidget()
{
}void BaseWidget::initUI()
{// 0layout_ = new QVBoxLayout;layout_->setContentsMargins(0, 0, 0, 0);layout_->setSpacing(0);// 1title_bar_ = new QWidget(this);title_bar_->setObjectName(QString("objTitleBar"));title_bar_->setFixedHeight(42);title_bar_layout_ = new QHBoxLayout;title_bar_layout_->setContentsMargins(5, 0, 0, 0);title_bar_layout_->setSpacing(0);sys_icon_ = new QLabel(title_bar_);title_ = new QLabel(title_bar_);title_->setObjectName(QString("objTitle"));right_layout1_ = new QVBoxLayout;right_layout1_->setContentsMargins(1, 1, 1, 1);right_layout2_ = new QHBoxLayout;right_layout2_->setContentsMargins(0, 0, 0, 0);right_layout2_->setSpacing(5);minimize_button_ = new QToolButton(title_bar_);minimize_button_->setObjectName(QString("objMinimizeButton32x32"));switch_button_ = new QToolButton(title_bar_);switch_button_->setObjectName(QString("objRestoreButton32x32"));close_button_ = new QToolButton(title_bar_);close_button_->setObjectName(QString("objCloseButton32x32"));right_layout2_->addWidget(minimize_button_);right_layout2_->addWidget(switch_button_);right_layout2_->addWidget(close_button_);right_layout1_->addLayout(right_layout2_);right_layout1_->addStretch();title_bar_layout_->addWidget(sys_icon_);title_bar_layout_->addStretch();title_bar_layout_->addWidget(title_);title_bar_layout_->addStretch();title_bar_layout_->addLayout(right_layout1_);title_bar_->setLayout(title_bar_layout_);content_widget_ = new QWidget(this);content_widget_->setObjectName("objContentWidget");status_bar_ = new QStatusBar(this);message_ = new QLabel(status_bar_);message_->setMinimumSize(message_->sizeHint());message_->setAlignment(Qt::AlignVCenter);status_bar_->addWidget(message_);layout_->addWidget(title_bar_);layout_->addWidget(content_widget_);layout_->addWidget(status_bar_);status_bar_->setMaximumHeight(30);//status_bar_->showMessage(QStringLiteral("123"));this->setLayout(layout_);
}void BaseWidget::slotWindowStateChanged(bool max)
{if (max){switch_button_->setObjectName(QString("objRestoreButton24x24"));switch_button_->setStyle(switch_button_->style());}else{switch_button_->setObjectName(QString("objMaximizeButton24x24"));switch_button_->setStyle(switch_button_->style());}
}void BaseWidget::slotMinimizeButtonClicked()
{this->showMinimized();
}void BaseWidget::slotMaximizeButtonClicked()
{if (this->isMaximized()){this->showNormal();}else{this->showMaximized();}
}void BaseWidget::slotCloseButtonClicked()
{this->close();
}

完整代码

1 自定义无边框窗体相关推荐

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

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

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

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

  3. Qt无边框窗体(Windows)

    Qt无边框窗体Windows篇 去掉标题栏和边框 实现拖拽功能 还原窗体功能 注意点 我们可以做的更好 添加阴影 亚克力面板效果 结语 去掉标题栏和边框 首先第一步我们要通过设置系统绘制的边框消失 s ...

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

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

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

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

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

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

  7. 通过调用API函数实现的无边框窗体的拖拽,比判断坐标更快捷

    在winform程序中,有时会选择边框设计会none,但是这样就不能拖拽窗体移动 解决方案有二: 1,判断坐标控制拖拽 2.利用API函数, 下面介绍利用API函数,方便,快捷 #region 移动无 ...

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

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

  9. 【QT】自定义无边框窗口分享

    [QT/天下wu双]自定义无边框窗口分享 https://www.bilibili.com/video/BV1HA411Y7d4 源码链接:https://github.com/Bili-TianX/ ...

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

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

最新文章

  1. python爬虫系列(1.3-关于cookie的认识)
  2. 22个HTML5 技巧二
  3. python 计算数字 k 在 0 到 n 中的出现的次数,k 可能是 0~9 的一个值
  4. 今天maven install时碰到的两个问题(堆溢出和编译错误)
  5. react设置static defaultProps报错问题解决
  6. 记忆化搜索(DFS+DP) URAL 1223 Chernobyl’ Eagle on a Roof
  7. python实现简单的聊天小程序
  8. 如何开发一款棋牌游戏?棋牌游戏平台搭建
  9. 腾讯地图基于 WebGL实现自定义栅格图层踩坑实录
  10. 计算机视觉基础(三)——对极几何中的基本矩阵F和本质矩阵E
  11. 计算机关机 休眠睡眠有什么区别,电脑“关机”、“睡眠”、“休眠”三者区别是什么...
  12. table表格宽带width属性研究
  13. oracle数据投毒,Oracle Database Server TNS Listener远程数据投毒漏洞(CVE-2012-1675)的完美解决方法...
  14. Vim编辑完如何保存退出
  15. 使用virtuoso和calibre对版图进行DRC LVS的检查
  16. [html] 写一个鼠标跟随的特效
  17. Bugzilla的配置和安装
  18. Intellij-出现Module ** must not contain source root **. The root already belongs to module **
  19. Linux指令用之记之-sell算数运算
  20. ❤❤全网最全-----VUE3.0最火爆的编辑器《TinyMCE》❤❤

热门文章

  1. PAT乙级——1034(模拟四则运算)
  2. org.eclipse.core.runtime.CoreException: Plug-in org.eclipse.jdt.ui was unable to load class org.ecli
  3. 云e办学习笔记(二十七)邮件自动发送功能实现
  4. 个人投资——基本原则
  5. ISO七层模型与TCP/IP四层参考模型逐层解析
  6. 在Ubuntu16.04下配置VSFTPD
  7. 华硕路由架设php,华硕 RT-AC68U 路由模式默认 VLAN
  8. 山西医科大学计算机部,山西医科大学医学计算机教学改革探讨.pdf
  9. 个性化精简掉了Win10便签顶部如何恢复
  10. 手机怎么打开f12_如何使用浏览器的F12调试页面?