c++ - 制作透明标签c++ WINAPI

标签 c++ winapi gdi+ gif

有一个带有 gif 的窗口和一些使用 gdi+ 的 c++ winapi(非 MFC)元素。所以我想制作透明元素(编辑、静态、按钮)。我想制作元素的透明灰色背景。

我尝试处理 WM_CTLCOLORSTATIC 并添加样式 WS_EX_TRANSPARENT 和 m。但它没有给出正确的结果。但是当我用代码处理 WM_CTLCOLORSTATIC 时:



hdcStatic = (HDC) wParam; 
    SetTextColor(hdcStatic, RGB(0,0,0));    
    SetBkMode (hdcStatic, TRANSPARENT);

    return (LRESULT)GetStockObject(NULL_BRUSH);

它显示 STATIC 样式透明,但我可以看到 Windows 10 的背景。

最小代码是:


#include <memory>
#include "Resource.h"
#include <vector>
#include "TESTING.h"
#include "framework.h"
#include <algorithm>
#include <windows.h>
#include <objidl.h>
#include <GdiPlus.h>
#include <gdiplusimaging.h>
#include <shlwapi.h>
#include<CommCtrl.h>

using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
#pragma comment (lib,"shlwapi.lib")
#pragma comment (lib,"Comctl32.lib")
#define TIMER_ID 101
static HFONT s_hFont = NULL;
static HWND hWnd;

static HWND hwndText;
static HWND hwndButton;
static HWND hwndLabel;
static HWND hwndCode;
static HWND hwndCode2;
static HWND hwndTime;



LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK SubWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR);

int WINAPI _WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    ULONG_PTR m_gdiplusToken;
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);

    HMODULE hMod = GetModuleHandle(NULL);
    HRSRC hRes = FindResource(hMod, MAKEINTRESOURCEW(MY_GIF_ID), RT_RCDATA);
    if (!hRes) MessageBox(NULL, L"hRes!!", L"ERROR", 0);
    HGLOBAL hGlobal = LoadResource(hMod, hRes);
    if (!hGlobal)MessageBox(NULL, L"hGlobal!!", L"ERROR", 0);
    void* pResData = LockResource(hGlobal);
    if (!pResData) MessageBox(NULL, L"pResData!!", L"ERROR", 0);

    DWORD dwResData = SizeofResource(hMod, hRes);

    IStream* pStream = SHCreateMemStream((BYTE*)pResData, dwResData);
    if (!pStream) MessageBox(NULL, L"pStream!!", L"ERROR", 0);

    Image gif(pStream);
    pStream->Release();

    MSG msg = { 0 };
    WNDCLASS wc = { 0 };
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hbrBackground = NULL; // <= Do not provide a background brush.
    wc.lpszClassName = L"anim_gif_player";
    if (!RegisterClass(&wc))
        return -1;
    hWnd = CreateWindow(wc.lpszClassName,
        L"",
        WS_EX_TOPMOST | WS_CLIPCHILDREN & ~WS_CAPTION & ~WS_SYSMENU,
        0, 0, 640, 480, 0, 0, hInstance, &gif);
        if (!hWnd) {
            MessageBox(0, L"SSSSSSSSSSSSSSSSSSS", L"kkkkkkkkkkkkkkkkkkkkkk", 0);
            return -2;

        }



        hwndLabel = CreateWindowEx(WS_EX_TRANSPARENT,L"STATIC",
            NULL,
            WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPSIBLINGS,
            325, 90, 440, 45,
            hWnd,
            NULL,
            NULL,
            NULL);
        SetWindowSubclass(hwndLabel, SubWndProc, 0, 0);




        const TCHAR* fontName = TEXT("Croobie");
        const long nFontSize = 24;

        HDC hdc = GetDC(hwndLabel);

        LOGFONT logFont = { 0 };
        logFont.lfHeight = -MulDiv(nFontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
        logFont.lfWeight = FW_BOLD;
        wcscpy_s(logFont.lfFaceName, fontName);

        s_hFont = CreateFontIndirect(&logFont);








        SendMessage(hwndLabel, WM_SETFONT, (WPARAM)s_hFont, (LPARAM)MAKELONG(TRUE, 0));














    ShowWindow(hWnd, SW_SHOWMAXIMIZED);
    UpdateWindow(hWnd);



    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    //DeleteObject(wc.hbrBackground);

    return msg.wParam;
}

std::vector<unsigned int> LoadGifFrameInfo(Image* image)
{
    // I think animated gifs will always only have 1 frame dimension...
    // the "dimension" being the frame count, but I could be wrong about this
    int count = image->GetFrameDimensionsCount();
    if (count != 1)
        return std::vector<unsigned int>();

    GUID guid;
    if (image->GetFrameDimensionsList(&guid, 1) != 0)
        return std::vector<unsigned int>();
    int frame_count = image->GetFrameCount(&guid);

    auto sz = image->GetPropertyItemSize(PropertyTagFrameDelay);
    if (sz == 0)
        return std::vector<unsigned int>();

    // copy the frame delay property into the buffer backing an std::vector
    // of bytes and then get a pointer to its value, which will be an array of 
    // unsigned ints
    std::vector<unsigned char> buffer(sz);
    PropertyItem* property_item = reinterpret_cast<PropertyItem*>(&buffer[0]);
    image->GetPropertyItem(PropertyTagFrameDelay, sz, property_item);
    unsigned int* frame_delay_array = (unsigned int*)property_item[0].value;

    // copy the delay values into an std::vector while converting to milliseconds.
    std::vector<unsigned int> frame_delays(frame_count);
    std::transform(frame_delay_array, frame_delay_array + frame_count, frame_delays.begin(),
        [](unsigned int n) {return n * 10; }
    );

    return frame_delays;
}

void GenerateFrame(Bitmap* bmp, Image* gif)
{
    Graphics dest(bmp);

    SolidBrush white(Color::White);
    dest.FillRectangle(&white, 0, 0, bmp->GetWidth(), bmp->GetHeight());

    if (gif)
        dest.DrawImage(gif, 0, 0);
}

std::unique_ptr<Bitmap> CreateBackBuffer(HWND hWnd)
{
    RECT r;
    GetClientRect(hWnd, &r);
    return std::make_unique<Bitmap>(r.right - r.left, r.bottom - r.top);
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static Image* animated_gif;
    static std::unique_ptr<Bitmap> back_buffer;
    static std::vector<unsigned int> frame_delays;
    static int current_frame;

    switch (message) {
    case WM_CREATE: {
        animated_gif = reinterpret_cast<Image*>(
            reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams
            );

        if (!animated_gif || animated_gif->GetLastStatus() != 0) {
            MessageBox(hWnd, L"Unable to load animated gif", L"error", MB_ICONERROR);
            return 0;
        }

        // Create a bitmap the size of the window's clent area
        back_buffer = CreateBackBuffer(hWnd);

        // get the frame delays and thereby test that this is really an animated gif
        frame_delays = LoadGifFrameInfo(animated_gif);
        if (frame_delays.empty()) {
            MessageBox(hWnd, L"Invalid gif or not an animated gif", L"error", MB_ICONERROR);
            return 0;
        }

        current_frame = 0;
        animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);

        GenerateFrame(back_buffer.get(), animated_gif);

        SetTimer(hWnd, TIMER_ID, frame_delays[0], nullptr);
        InvalidateRect(hWnd, nullptr, FALSE);
    }
                    break;


    case WM_TIMER: {
        KillTimer(hWnd, TIMER_ID);
        current_frame = (current_frame + 1) % frame_delays.size();
        animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);
        GenerateFrame(back_buffer.get(), animated_gif);
        SetTimer(hWnd, TIMER_ID, frame_delays[current_frame], nullptr);
        InvalidateRect(hWnd, nullptr, FALSE);
    } break;

    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);

        Graphics g(hdc);
        g.DrawImage(back_buffer.get(), 0, 0);

        EndPaint(hWnd, &ps);
    } break;



    case WM_SIZE: {
        back_buffer = CreateBackBuffer(hWnd);
        GenerateFrame(back_buffer.get(), animated_gif);
    } break;

    case WM_CLOSE:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

LRESULT CALLBACK SubWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR) {
    switch (msg) {
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        RECT rc; GetClientRect(hwnd, &rc);
        SetTextColor(hdc, Color::Yellow);
        SetBkMode(hdc, TRANSPARENT);
        wchar_t buf[256] = { 0 };
        GetWindowTextW(hwnd, buf, sizeof(buf) / sizeof(*buf));
        DrawTextW(hdc, buf, wcslen(buf), &rc, DT_LEFT | DT_TOP);
        EndPaint(hwnd, &ps);
        return 0;
    }
    case WM_NCDESTROY://safely remove subclass
        RemoveWindowSubclass(hwnd, SubWndProc, 0);
        return DefSubclassProc(hwnd, msg, wParam, lParam);
    }
    return DefSubclassProc(hwnd, msg, wParam, lParam);

    }



最佳答案

The WS_EX_TRANSPARENT style doesn’t mean “transparent”; it means “paint over siblings.”

The style is called “transparent” not because it makes the window transparent but because it makes transparency possible.

请引用:Why isn't my transparent static control transparent?

我发现唯一可靠地做到这一点的方法是对静态控件进行子类化并手动绘制背景。

您可以捕获 WM_ERASEBKGND 消息并绘制底层位图的适当部分。

请引用:

Is it possible to make a Static control transparent?

C++ Win32 Static Control Transparent Background

此外,如果你想去除灰色背景,你可以改变静态控件背景并使其透明。

自定义静态控件的恶魔:

#include "stdafx.h"
#include "Test_WM_CTLCOLORSTATIC.h"

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name
HWND hWndStatic;
// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK    WndProcPanel(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.

    // Initialize global strings
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_TESTWMCTLCOLORSTATIC, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);
    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTWMCTLCOLORSTATIC));

    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTWMCTLCOLORSTATIC));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_TESTWMCTLCOLORSTATIC);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}


BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
WNDPROC StaticWndProc = NULL;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:
    {   LRESULT lRes = DefWindowProc(hWnd, message, wParam, lParam);
        hWndStatic = CreateWindowEx(0, L"Static", NULL, WS_CHILD | WS_VISIBLE | SS_LEFT, 10, 130, 200, 40, hWnd, NULL, hInst, NULL); //v2 deleted HWND
        StaticWndProc = (WNDPROC)SetWindowLong(hWndStatic, GWL_WNDPROC, (LPARAM)WndProcPanel);
        return lRes;
    }
    break;
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_ERASEBKGND: 
    {    
         HBRUSH brush;   
         RECT rect;    
         brush = CreateSolidBrush(RGB(0, 255, 0));    
         SelectObject((HDC)wParam, brush);    
         GetClientRect(hWnd, &rect);   
         Rectangle((HDC)wParam, rect.left, rect.top, rect.right, rect.bottom); 
    }
    break;
    case WM_DESTROY:
        SetWindowLong(hWndStatic, GWL_WNDPROC, (LPARAM)StaticWndProc);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}


TCHAR szText[] = _T("TestString");;
LRESULT CALLBACK WndProcPanel(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{   
        if (message == WM_PAINT)
        {
            RECT rc;
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            GetClientRect(hWnd, &rc);
            SetBkMode(hdc, TRANSPARENT);
            DrawText(hdc, szText, _tcslen(szText), &rc, DT_CENTER | DT_VCENTER);
            EndPaint(hWnd, &ps);
            return 0;
        }
        return CallWindowProc(StaticWndProc, hWnd, message, wParam, lParam); 
}

thx @IInspectable 的提醒,子类控件最好的方法是使用SetWindowsSubclass , 请参阅 Subclassing Controls

稍后我会更新新的代码,还请见谅。

更新:

case WM_TIMER: {
        KillTimer(hWnd, TIMER_ID);
        current_frame = (current_frame + 1) % frame_delays.size();
        animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);
        GenerateFrame(back_buffer.get(), animated_gif);
        SetTimer(hWnd, TIMER_ID, frame_delays[current_frame], nullptr);     
        InvalidateRect(hWnd, nullptr, FALSE);
        InvalidateRect(hwndLabel, nullptr, FALSE); //Here add new code

    }

调试结果:

3

关于c++ - 制作透明标签c++ WINAPI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57169804/

相关文章:

C++ - 将二进制文件的一部分读入位集

c++ - 动态创建、命名和放置队列

c++ - SndVol 如何改变给定 Audio Session 的音量级别?

C++重绘窗口的一部分

c# - 为什么绘制到 OnPaint 图形比图像图形更快?

c# - LinearGradientBrush 无法正确呈现

C++ move 语义和异常

c++ - 有什么理由去寻找继承/多态性的替代方案吗?

c++ - 如何将视频从我的应用程序流式传输到 Web?

c# - Image.Save(..) 抛出 GDI+ 异常,因为内存流已关闭