使用SetClassLongPtr改变窗口背景

Change window background using SetClassLongPtr

本文关键字:背景 窗口 改变 SetClassLongPtr 使用      更新时间:2023-10-16

我有一个下拉菜单,我想用它来改变我的窗口的背景;窗口默认设置为"Winter background"。

wClass.hbrBackground = CreatePatternBrush(LoadBitmap(hInst, MAKEINTRESOURCE(WinterBG)));

每当选择下拉菜单中的项时,它都会设置CBN_SELCHANGE,我在其中获取所选项的长度和字符串。我想让背景在此基础上改变。

case WM_COMMAND:
    {
        switch(HIWORD(wParam))
        {
        case CBN_SELCHANGE:
            {
                ItemIndex = SendMessage((HWND)lParam, (UINT)CB_GETCURSEL, (WPARAM)0, (LPARAM)0);
                int ItemLen = SendMessage((HWND)lParam, (UINT)CB_GETLBTEXTLEN, (WPARAM)0, (LPARAM)0);
                char* ListItem = (char*)malloc(ItemLen+1);
                (char)SendMessageA((HWND)lParam, (UINT)CB_GETLBTEXT, (WPARAM)ItemIndex, (LPARAM)ListItem);
                //////////////////////////////////////////////////////////////////////////////////////////////////
                // I am certain this can be optimized
                if ( ItemLen == 5 && ListItem[0] == 'S' ) // Spring
                {
                    MessageBox(NULL, L"Spring chosen", L"Confirmed", MB_ICONINFORMATION | MB_OK);
                    HBRUSH brush = CreatePatternBrush(LoadBitmap(wClass.hInstance, MAKEINTRESOURCE(SpringBG)));
                    SetClassLongPtr(hWnd, GCLP_HBRBACKGROUND, (LONG)brush);
                    InvalidateRect(hWnd, NULL, TRUE);
                    UpdateWindow(hWnd);
                }
.....

SetClassLongPtr不是我想要的工作方式。更新窗口后(我读InvalidateRect &UpdateWindow实现),没有什么变化——背景仍然在我的"WinterBG"。

我使用SetClassLongPtr正确吗?如果没有,我还可以如何更改背景?

我已经尝试了你想要在一个简单的Win32项目中完成的工作,并设法使其工作。为了使下面的代码工作,您需要确保这两个位图在vc++项目中作为资源存在。我在我的vc++项目中定义了两个位图作为资源。我没有像你一样使用菜单。我正在处理WM_KEYDOWN消息,但想法是一样的,这也应该在你的WM_COMMAND消息下工作。下面的代码做了以下事情:

  1. 从exe文件的资源部分加载位图。它需要一个HINSTANCE,在你的windowproc中,你可以使用GetWindowLongPtr(使用这个代替GetWindowLong,特别是当你为64位环境编码时,参见https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongw中微软对此的说明)。
  2. 创建一个新的画笔,使用相同的功能:'CreatePatternBrush'。
  3. 使用此笔刷更改窗口背景并存储返回的旧笔刷,以便您可以随后删除它。你可以使用SetClassLongPtr(同样的64位注意事项适用于这里)来做到这一点。令人困惑的部分可能是最后一个参数,它是一个LONG_PTR。这可能看起来像一个指向阅读器的指针,但实际上它是一个__int64(64位整数)。
  4. 删除旧的刷(以防止内存泄漏,特别是如果您希望在会话中多次执行此代码)。
  5. 通过使整个客户端区域(参数NULL)无效并将repaint参数设置为TRUE来刷新窗口。

应该可以了。下面是我的WndProc函数的这一部分:

    case WM_KEYDOWN:
    switch (wParam)
    {
    case VK_F1:
        HINSTANCE hInst2 = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
        HBITMAP newBmp = LoadBitmapW(hInst2, MAKEINTRESOURCE(IDB_BITMAP2));
        HBRUSH newBrush = (HBRUSH)CreatePatternBrush(newBmp);
        HBRUSH oldBrush = (HBRUSH)SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)newBrush);
        DeleteObject(oldBrush);
        InvalidateRect(hwnd, NULL, TRUE);
        break;
    }
    break;

小注释:上面代码中的最后一个'break'只是因为在我的windowproc的底部调用了DefWindowProc。当在你的windowproc中处理WM_KEYDOWN消息时,它应该总是传递给DefWindowProc,这样它就可以被Windows正确处理。在您的菜单情况下,最后的'break'语句可能会被'return 0'取代。