背景
因为自己协同工具用win32开发,自己想加载一个?图标,如果直接加载bitmap非常简单,直接添加资源,然后设置bitmap的资源id即可,但我想加载一个png,因为Png可以支持透明,而且显示png也是一个非常常见的需求
解决思路
- google一下基本全部都是用gdi+ 渲染到子控件中
- 直接渲染的话,如果控件被遮挡是不会显示的,所以这个必须控件子类化,在wm_paint进行渲染png
代码
简单封装一个基类,方便渲染子类化
#include "pch.h"
#include "CBaseControl.h"
#include "commctrl.h"
int CBaseControl::global_sub_id = 1000;
CBaseControl::~CBaseControl()
{
RemoveSubClass();
}
void CBaseControl::Init(HWND hw)
{
m_hwnd = hw;
m_subclass_id = global_sub_id++;
this->SubClass();
}
LRESULT CBaseControl::Subclassproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
if (dwRefData) {
CBaseControl* ptr = (CBaseControl*)dwRefData;
return ptr->ControlProc(hWnd, uMsg, wParam, lParam, uIdSubclass, dwRefData);
}
else {
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
}
LRESULT CBaseControl::ControlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
if (uMsg == WM_PAINT) {
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(m_hwnd, &ps);
this->OnPaint(hdc);
EndPaint(m_hwnd, &ps);
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
void CBaseControl::SetHandMouse()
{
HCURSOR hcur = LoadCursor(NULL, IDC_HAND); //加载系统自带鼠标样式
SetClassLong(m_hwnd, GCL_HCURSOR, (long)hcur);
}
void CBaseControl::SubClass()
{
if (!m_is_subclass) {
m_is_subclass = true;
::SetWindowSubclass(m_hwnd, CBaseControl::Subclassproc, m_subclass_id, (DWORD_PTR)this);
}
}
void CBaseControl::RemoveSubClass()
{
if (m_subclass_id != -1) {
m_subclass_id = -1;
RemoveWindowSubclass(m_hwnd, CBaseControl::Subclassproc, m_subclass_id);
}
}
subclass 子类化,使用SetWindowSubclass 替换窗口事件处理函数。
if (uMsg == WM_PAINT) {
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(m_hwnd, &ps);
this->OnPaint(hdc);
EndPaint(m_hwnd, &ps);
}
渲染就界面,this->onPaint 子类继承
picture contronl 加载
#include "pch.h"
#include "CPngControl.h"
#include "commctrl.h"
#include <comdef.h>
#include <gdiplus.h>
#include "utils.h"
#include "resource.h"
CPngControl::CPngControl()
{
m_hwnd = 0;
}
CPngControl::CPngControl(int width, int height)
{
m_width = width;
m_height = height;
}
CPngControl::~CPngControl()
{
}
void CPngControl::Init(HWND hw, int res_id)
{
CBaseControl::Init(hw);
m_res_id = res_id;
::SetWindowPos(m_hwnd, 0, 0, 0, this->m_width, this->m_height, SWP_NOMOVE | SWP_NOOWNERZORDER);
}
void CPngControl::OnPaint(HDC hdc)
{
DrawPng(hdc);
}
void CPngControl::DrawPng(HDC hdc)
{
Gdiplus::Graphics graphics(hdc); // Create a GDI+ graphics object
auto r = LoadImageFromResource(GetModuleHandleW(NULL), MAKEINTRESOURCE(m_res_id), L"PNG");
if (r != nullptr) {
graphics.DrawImage(r, 0, 0, 24, 24);
delete r;
}
}
子类实现 CPngControl::OnPaint,进行画png图片,这里记得的picture control 类型设置Enhanced Metafile
原理
- gdi+ 加载Png资源进行渲染,其实也可以直接加载磁盘的文件
- 子类化控件接手控件的渲染
自定义控件
- 子类化
- 超类化
- Owner-Drawn 创建自绘控件
子类化:最简单,这个可以用SetWindowSubclass 或者Setwindowlong GWL_WNDPROC 来替换出窗口处理函数,接管所有消息,这样子可以复用对话框控件,不用代码写代码创建window,比较直观。
超类化:其实本质跟子类化差不多,获取原来控件class 信息然后获取所有信息和窗口处理函数,然后注册新类,然后创建新window,这样子和子类化进行复用自带的控件。
Owner-Drawn: 窗口增加一个属性,然后处理自绘消息
自定义控件 - Win32 apps | Microsoft Learn
可以看这个介绍
ui
win32 渲染最小单位是窗口,html 最小单位是div(可以任何元素),所以win32开发界面不太方便,比较难自定义,要接管各种消息,而html 根本不用管这么多,可以简单通过样式直接显示各种,可以直接通过css进行修改属性,而win32 只能通过消息设置,一点都不直观,好多属性都不知道,还要看官方的文档,看怎么设置,感觉必须自己画,这样子加大很大难度,所以现代扩展性强的界面库,都很容易描述界面属性,所以最容易写界面就html这类了,无论direct ui 其实都是一个思想