小鱼塘--自说自话的地方

  • 小玩意
  • 小想法
记录自己技术和想法地方
  1. 首页
  2. c++
  3. 正文

win32 picture control 怎么加载Png图片

8 10 月, 2023 1038点热度 0人点赞 0条评论
内容目录

背景

因为自己协同工具用win32开发,自己想加载一个?图标,如果直接加载bitmap非常简单,直接添加资源,然后设置bitmap的资源id即可,但我想加载一个png,因为Png可以支持透明,而且显示png也是一个非常常见的需求

解决思路

  1. google一下基本全部都是用gdi+ 渲染到子控件中
  2. 直接渲染的话,如果控件被遮挡是不会显示的,所以这个必须控件子类化,在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

原理

  1. gdi+ 加载Png资源进行渲染,其实也可以直接加载磁盘的文件
  2. 子类化控件接手控件的渲染

自定义控件

  1. 子类化
  2. 超类化
  3. 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 其实都是一个思想

标签: 暂无
最后更新:8 10 月, 2023

小鱼儿

爱研究技术,爱玩LOL

点赞
< 上一篇
下一篇 >

COPYRIGHT © 2022 小鱼塘. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

湘ICP备18005349号