代码思路
1.首先是处理WM_NCHITTEST消息:支持调整大小和自动排列。
2.其次是WM_NCCALCSIZE消息:返回0,去除非客户区。
wParam为真时,窗口被调整,需要重新计算大小,返回WVR_REDRAW,放弃复制原客户区,绘制新客户区。(拖影)
若为最大化窗口,那窗口位置为{-8,-8,x+8,y+8},窗口外边距超出屏幕,所以修正NCCALCSIZE_PARAMS结构。(双屏)
问题:最大化和自动排列伴有透明闪烁,调整大小时窗口强烈抖动。
3.再然后经过大量尝试,解决问题:
处理WM_ERASEBKGND消息:返回true,防止背景色重绘,调整大小时窗口不再抖动。
处理WM_NCPAINT消息:返回0,防止透明闪烁。
处理WM_NCACTIVATE消息:返回0,防止处理WM_NCPAINT消息时引入的默认边框。
在WM_CREATE消息里:设置窗口区域,防止处理WM_NCPAINT消息时引入的圆角和黑色背景和小几率抖动。
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
{
HRGN rgn = CreateRectRgn(0, 0, 1000000, 1000000);
SetWindowRgn(hWnd, rgn, 0);//设置窗口区域,防止圆角和黑色背景和小几率抖动
DeleteObject(rgn);
break;
}
case WM_NCPAINT:return 0;//防止透明闪烁
case WM_NCACTIVATE:return 0;//防止默认边框
case WM_ERASEBKGND:return true;//防止背景重绘导致的抖动
case WM_NCCALCSIZE:
{
if (wParam)
{
NCCALCSIZE_PARAMS* lp = (LPNCCALCSIZE_PARAMS)lParam;
if (IsZoomed(hWnd))//最大化时修正窗口外边距
{
lp->rgrc[0].left += 8;
lp->rgrc[0].top += 8;
lp->rgrc[0].right -= 8;
lp->rgrc[0].bottom -= 8;
}
return WVR_REDRAW;//防止BitBlt复制导致的拖影和抖动
}
return 0;
}
case WM_NCHITTEST:
{
RECT rc; GetClientRect(hWnd, &rc);//客户区矩形
POINT pt; GetCursorPos(&pt); ScreenToClient(hWnd, &pt);//鼠标位置
if (!IsZoomed(hWnd))//最大化时不用调整大小
{
int x = 8;//边框宽度
if (pt.x < rc.left + x)
{
if (pt.y < rc.top + x)return HTTOPLEFT;//左上角
if (pt.y >= rc.bottom - x)return HTBOTTOMLEFT;//左下角
return HTLEFT;//左边
}
if (pt.x >= rc.right - x)//坐标从0开始,所以使用>=
{
if (pt.y < rc.top + x)return HTTOPRIGHT;//右上角
if (pt.y >= rc.bottom - x)return HTBOTTOMRIGHT;//右下角
return HTRIGHT;//右边
}
if (pt.y < rc.top + x)return HTTOP;//上边
if (pt.y >= rc.bottom - x)return HTBOTTOM;//下边
}
return HTCAPTION;//标题栏
}
case WM_DESTROY:PostQuitMessage(0);
}
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
WNDCLASS wc = {
0 };
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = L"music";
RegisterClass(&wc);
HWND hWnd = CreateWindow(wc.lpszClassName, nullptr, WS_OVERLAPPEDWINDOW, 183, 84, 1000, 600, nullptr, nullptr, hInstance, nullptr);
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
实现阴影以及半透明
用Windows.UI.Composition实现阴影以及半透明并解决初始透明。
这时用FillRect处理设备上下文又会开始抖动,所以不要用gdi了
先空着,以后写
再更,dwm的问题,用direct3d 12的窗口不会闪烁。
再更,Windows.UI.Composition也会带来窗口闪烁,所以直接用direct3d 12贴个阴影图好了,窗口透明又不知道如何实现了
这篇文章是个大坑,等我慢慢填吧!
还有一件事,窗口抖动和闪烁都是绘制引起的,不是位置计算不正确引起的.(验证过)
本该绘制的区域估计是被dwm填上了透明色,或者干脆没来的及绘制.
下面先贴一段代码,还未实现半透明,等以后更。(用winrt代替了WRL)
用Direct3D 12的无边框窗口不会闪烁抖动
#include <windows.h>
#include <d3d12.h>
#include "d3dx12.h"
#include <dxgi1_6.h>
#include <winrt/base.h>
#include <D3Dcompiler.h>
#include <DirectXMath.h>
#pragma comment(lib,"dxgi.lib")
#pragma comment(lib,"d3d12.lib")
#pragma comment(lib,"windowsapp.lib")
#pragma comment(lib,"d3dcompiler.lib")
using namespace winrt;
using namespace DirectX;
static const UINT FrameCount = 2;
struct Vertex
{
XMFLOAT3 position;
XMFLOAT4 color;
};
// Pipeline objects.
CD3DX12_VIEWPORT m_vie