【问题标题】:Windows API Subclassing - Win Docs Example not WorkingWindows API 子类化 - Win Docs 示例不起作用
【发布时间】:2020-01-16 11:19:00
【问题描述】:

我是 Windows Api 的新手,仍在学习基础知识。在我的项目中,我遇到了一个需要子类化的问题,所以我决定找一个示例代码,编译它,看看它在实践中是如何工作的。我在 Windows Docs 网页How to Subclass a Combo Box 找到了一个相对简单的示例。在创建了一个父窗口并对要编译的代码进行了一些调整后,我设法运行了它,但程序的行为似乎没有进行子类化。

这不是第一次在网页上发现示例代码不起作用。我错过了什么吗?

我的 WinMain 例程:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

WNDCLASSEX wc = { 0 };
wc.lpszClassName = "MyClassName";
wc.hInstance = hInstance;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = ToolbarWindowProc;
RegisterClassEx(&wc)

hwndMain = CreateWindow(
    "MyClassName",
    "Title",
    WS_POPUPWINDOW | WS_CAPTION,
    0,
    0,
    500,
    210,
    nullptr,
    nullptr,
    hInstance,
    NULL
);
ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);

MSG msg;
while (GetMessageW(&msg, NULL, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessageW(&msg);
}
return (int)msg.wParam;
}

【问题讨论】:

  • 您认为您在代码中的哪个位置对窗口进行了子类化?
  • 什么时候可以在 C 中进行“子类化”?
  • “这不是第一次在网页上找到的示例代码无法正常工作”您不是说您做了“一些调整”吗?该示例在调整之前是否有效?你改变了什么?
  • @foreknownas_463035818 自从设计了 Windows?

标签: c++ winapi subclassing


【解决方案1】:

你需要知道为什么我们需要子类化控件。

子类化现有控件是创建自定义控件的另一种方法 控制。子类过程可以改变选择的行为 通过处理那些影响选定对象的消息来控制 行为。所有其他消息都传递给原始窗口过程 为控制。例如,一个应用程序可以显示一个小的 只读单行编辑控件中文本旁边的位图 子类化控件并处理WM_PAINT 消息。

参考:Subclassing the Window Class of an Existing Control

从您提供的link 看来,您似乎没有按照教程学习如何对控件进行子类化。

但现在看来你需要一个完整的例子来学习。

所以第一步是使用官方模板,看下面的截图:

创建后,就可以开始继承静态控件了。

将以下消息事件添加到WndProc 函数。

case WM_CREATE:
       {
           hWndStatic = CreateWindowEx(0, L"Static", NULL, WS_CHILD | WS_VISIBLE | SS_LEFT, 10, 130, 200, 40, hWnd, (HMENU)IDC_OWNERDRAWBUTTON, hInst, NULL);
           SetWindowSubclass(hWndStatic, &OwnerDrawButtonProc, IDC_OWNERDRAWBUTTON, 0);
        return TRUE;
    }
    break;

最后,添加子类控件的回调函数。

TCHAR szText[] = _T("TestString");
LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
    LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_PAINT:
    {
        RECT rc;
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        GetClientRect(hWnd, &rc);
        SetTextColor(hdc, RGB(255, 0, 255));
        DrawText(hdc, szText, _tcslen(szText), &rc, DT_CENTER | DT_VCENTER);
        EndPaint(hWnd, &ps);
        return 0;
    }
    case WM_NCDESTROY:
        RemoveWindowSubclass(hWnd, &OwnerDrawButtonProc, 1);
        break;
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

注意:不要忘记声明新创建的函数

完整示例:

// Test_SetWindowSubclass.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "Test_SetWindowSubclass.h"
#include <commctrl.h>
#pragma comment(lib,"Comctl32.lib")

#define MAX_LOADSTRING 100
#define IDC_OWNERDRAWBUTTON 101

// 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);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
    LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

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_TESTSETWINDOWSUBCLASS, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

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

    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;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
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_TESTSETWINDOWSUBCLASS));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_TESTSETWINDOWSUBCLASS);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
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);

   if (!hWnd)
   {
      return FALSE;
   }

   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
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:
       {
           hWndStatic = CreateWindowEx(0, L"Static", NULL, WS_CHILD | WS_VISIBLE | SS_LEFT, 10, 130, 200, 40, hWnd, (HMENU)IDC_OWNERDRAWBUTTON, hInst, NULL);
           SetWindowSubclass(hWndStatic, &OwnerDrawButtonProc, IDC_OWNERDRAWBUTTON, 0);
        return TRUE;
    }
    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_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code that uses hdc here...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        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 OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
    LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_PAINT:
    {
        RECT rc;
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        GetClientRect(hWnd, &rc);
        SetTextColor(hdc, RGB(255, 0, 255));
        DrawText(hdc, szText, _tcslen(szText), &rc, DT_CENTER | DT_VCENTER);
        EndPaint(hWnd, &ps);
        return 0;
    }
    case WM_NCDESTROY:
        RemoveWindowSubclass(hWnd, &OwnerDrawButtonProc, 1);
        break;
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

调试:

在了解了控件子类化的过程后,您可以查看 Remy 给您的评论。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多