Strange effect when resizing a window with scrollbars - c

I have a window with two scrollbars, the scrollbars are repositioned when the window is resized. Inside the WM_PAINT handler, I draw a filled white rectangle just before the scrollbars:
Now, what I assumed will happen when I resize the window is the following:
First, a WM_SIZE message is sent. Inside its handler I reposition the
scrollbars.
Second, a WM_PAINT message is sent. Inside its handler I redraw
the filled white rectangle.
But when I resize the window vertically, this is what happens:
And when I resize the window horizontally, this is what happens:
This is my code:
#include <Windows.h>
HWND hHorizontalScrollbar;
HWND hVerticalScrollbar;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// Get width and height of client area for hWnd
RECT rect;
GetClientRect(hWnd, &rect);
// Draw a filled white rectangle just before the scrollbars
Rectangle(hdc, rect.left, rect.top, rect.right - 17, rect.bottom - 17);
EndPaint(hWnd, &ps);
}
break;
case WM_SIZE:
{
// Get width and height of client area for hWnd
RECT rect;
GetClientRect(hWnd, &rect);
// Change y and width of horizontal scrollbar
MoveWindow(hHorizontalScrollbar, 0, rect.bottom - 17, rect.right - 220, 17, TRUE);
// Change x and height of vertical scrollbar
MoveWindow(hVerticalScrollbar, rect.right - 17, 0, 17, rect.bottom - 220, TRUE);
}
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "WinClass";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wc);
HWND hWnd = CreateWindowEx(0, "WinClass", "", WS_OVERLAPPEDWINDOW, 600, 300, 400, 400, NULL, NULL, hInstance, NULL);
// Create horizontal Scrollbar
hHorizontalScrollbar = CreateWindowEx(0, "SCROLLBAR", NULL, WS_CHILD | WS_VISIBLE| SBS_HORZ, 0, 333, 300, 17, hWnd, NULL, hInstance, NULL);
// Create vertical Scrollbar
hVerticalScrollbar = CreateWindowEx(0, "SCROLLBAR", NULL, WS_CHILD | WS_VISIBLE| SBS_VERT, 333, 0, 17, 300, hWnd, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

You can add InvalidateRect to repaint in WM_SIZE:
case WM_SIZE:
RECT rect;
GetClientRect(hWnd, &rect);
MoveWindow(hHorizontalScrollbar, 0, rect.bottom - 17, rect.right - 220, 17, TRUE);
MoveWindow(hVerticalScrollbar, rect.right - 17, 0, 17, rect.bottom - 220, TRUE);
InvalidateRect(hWnd, 0, TRUE); //*** add this
Also, to add scroller, you may not need to create controls, you can just add WS_VSCROLL and WS_HSCROLL flag:
HWND hWnd = CreateWindowEx(0, "WinClass", "", WS_VSCROLL|WS_HSCROLL|WS_OVERLAPPEDWINDOW, 600, 300, 400, 400, NULL, NULL, hInstance, NULL);
Note, if there is a lot of painting done in WM_PAINT then consider overriding WM_ERASEBKGND and break so it doesn't do anything. Do all the background painting in WM_PAINT. You still need InvalidateRect in WM_SIZE
Edit ---------------------------------
When you resize the window, WM_ERASEBKGND is called to update the background. WM_PAINT is called next, however not all WM_PAINT changes are shown on the screen. Windows thinks that there is only a thin line next to the control's edge which needs updating so only that area of the screen is repainted.
As noted by #xMRI, you should set wc.style = CS_HREDRAW | CS_VREDRAW. This would be same as calling InvalidateRect(hWnd, 0, FALSE) in WM_SIZE.
Sometimes you need InvalidateRect(hWnd, 0, TRUE) to force erasing all of the background, but the above example doesn't need full background erase. You can also try WS_CLIPCHILDREN flag to reduce flicker and strange repaint.

Related

Win32 - Make part of window a translucent while another part opaque?

I want one part of a window to display an image with a certain opacity while the other part to work as normal. Both parts shouldn't pass clicks through.
I have tried making the main window a certain colour then using SetLayeredWindowAttributes to make that colour transparent(so only the client area is transparent). Then having a child window over it with my translucent image. However the clicks pass through the window(even though I don't have WS_EX_TRANSPARENT). Alphablend doesn't seem to works since the bitmap isn't 32bmp. So now I am trying to use updatelayeredwindows but I am having trouble setting the region to update it.
case WM_CREATE:
hbmp = (HBITMAP)LoadImageA(NULL, "courtyard.bmp", IMAGE_BITMAP, 1920, 1080, LR_LOADFROMFILE);
HDC hdc = CreateCompatibleDC(NULL);
HBITMAP hbmp_old = (HBITMAP)SelectObject(hdc, hbmp);
POINT dcOffset = {0, 0};
SIZE size = {600, 395};
BLENDFUNCTION bf = {AC_SRC_OVER, 0, 100, 0};
RECT wrect;
GetClientRect(hwnd, &wrect);
wrect.top = wrect.top + 43;
UPDATELAYEREDWINDOWINFO info = {sizeof(info), GetDC(NULL), NULL, &size, hdc, &dcOffset, 0, &bf, ULW_ALPHA, &wrect};
UpdateLayeredWindowIndirect(hwnd, &info);
SelectObject(hdc, hbmp_old);
DeleteDC(hdc);
DeleteObject(hbmp);
HWND hbutton = CreateWindowExA(0,
"BUTTON",
"X",
WS_VISIBLE | WS_CHILD | BS_FLAT,
10,
10,
100,
100,
hwnd,
(HMENU)NULL,
NULL,
(LPVOID)NULL);
break;
The parts out of wrect are just a black translucent colour.
Here is my main window:
hwnd = CreateWindowExA(WS_EX_OVERLAPPEDWINDOW | WS_EX_LAYERED,
window_name,
window_title,
WS_OVERLAPPEDWINDOW,
(monitor_dimension.width - window_width) / 2,
(monitor_dimension.height - window_height) / 2 - 75,
window_width,
window_height,
(HWND)NULL,
(HMENU)NULL,
hInstance,
(LPVOID)NULL);
I get the result using SetLayeredWindowAttributes(hWnd, RGB(255, 0, 0), 0, LWA_COLORKEY); without manifest and the clicks don't pass through.
The following code adapted from SetLayeredWindowAttributes() causes mouse clicks to go through after minimizing window | WinAPI.
// WindowsProject2.cpp : Defines the entry point for the application.
//
#include "framework.h"
#include "WindowsProject2.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
// 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);
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_WINDOWSPROJECT2, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT2));
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_WINDOWSPROJECT2));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(255, 0, 0));
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT2);
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:
{
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd, RGB(255, 0, 0), 0 /*doesn't matter*/, LWA_COLORKEY);
HWND hbutton = CreateWindowExA(0,
"BUTTON",
"X",
WS_VISIBLE | WS_CHILD | BS_FLAT,
10,
10,
100,
100,
hWnd,
(HMENU)NULL,
NULL,
(LPVOID)NULL);
/*HWND hbackground = CreateWindowEx(0,
L"STATIC",
NULL,
WS_CHILD | WS_VISIBLE | SS_BITMAP,
0,
0,
600,
400,
hWnd,
NULL,
NULL,
(LPVOID)NULL);
setImage(hbackground, L"test.bmp", 600, 400);*/
//SetWindowLong(hbackground, GWL_EXSTYLE, GetWindowLong(hbackground, GWL_EXSTYLE) | WS_EX_LAYERED);
//SetLayeredWindowAttributes(hbackground, 0, (255 * 50) / 100, LWA_ALPHA);
}
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;
}

Hiding and showing container windows based on tab

I have created a tab control that contains two tabs. Inside each tab there will be a container window to hold other controls (in the code example, a static control for instance). The idea is that when a new tab is selected, it will hide/show the correct container window that holds a bunch of controls. However I am struggling to get the container windows holding the static controls to show. This is the code so far:
#include <windows.h>
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")
#define ID_TABCTRL 1
#define ID_STATIC0 2
#define ID_STATIC1 3
#define ID_TAB0 4
#define ID_TAB1 5
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HWND hTab, hTab0, hTab1;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASS wc = { 0 };
wc.lpszClassName = TEXT("Tab control");
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&wc);
CreateWindow(wc.lpszClassName, TEXT("Tab control"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 250, 200, 0, 0, hInstance, 0);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
TCITEM tie;
INITCOMMONCONTROLSEX icex;
switch (msg)
{
case WM_CREATE:
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&icex);
tie.mask = TCIF_TEXT;
///// Create Tab Control /////
hTab = CreateWindow(WC_TABCONTROL, NULL, WS_CHILD | WS_VISIBLE, 0, 0, 200, 150, hwnd, (HMENU)ID_TABCTRL, NULL, NULL);
///// Create Individual Tabs /////
tie.pszText = TEXT("First");
SendMessage(hTab, TCM_INSERTITEM, 0, (LPARAM)(LPTCITEM)&tie);
tie.pszText = TEXT("Second");
SendMessage(hTab, TCM_INSERTITEM, 1, (LPARAM)(LPTCITEM)&tie);
///// Create Container windows for each tab /////
hTab0 = CreateWindow(0, NULL, WS_CHILD | WS_VISIBLE, 0, 0, 200, 150, hTab, (HMENU)ID_TAB0, NULL, NULL);
hTab1 = CreateWindow(0, NULL, WS_CHILD, 0, 0, 200, 150, hTab, (HMENU)ID_TAB1, NULL, NULL);
///// Add example control to one of the tab container windows /////
CreateWindow(TEXT("Static"), TEXT("Yay!"), WS_CHILD | WS_VISIBLE | SS_LEFT, 20, 30, 50, 25, hTab0, (HMENU)ID_STATIC0, NULL, NULL);
CreateWindow(TEXT("Static"), TEXT("It appears to be working"), WS_CHILD | WS_VISIBLE | SS_LEFT, 20, 30, 100, 50, hTab1, (HMENU)ID_STATIC1, NULL, NULL);
break;
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code)
{
case TCN_SELCHANGE:
switch (TabCtrl_GetCurSel(hTab))
{
///// Show or Hide the appropriate tabs /////
case 0:
ShowWindow(hTab1, SW_HIDE);
ShowWindow(hTab0, SW_SHOW);
case 1:
ShowWindow(hTab0, SW_HIDE);
ShowWindow(hTab1, SW_SHOW);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return(DefWindowProc(hwnd, msg, wParam, lParam));
}
Is it just a case of the container windows hTab0 and hTab1 being stuck behind the tab window (hTab)?
First, you need to change the position of the tab and static form, otherwise it will block the generated content.
Then you can define the generated static text directly through CreateWindow, by using WC_STATIC.
#include <Windows.h>
#include <commctrl.h>
LRESULT CALLBACK WndProc(HWND, UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("windows");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
}
hwnd = CreateWindow(szAppName,
TEXT("the hello program"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
while (GetMessageW(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message,WPARAM wParam,LPARAM lParam)
{
static HINSTANCE hInstance;
static HWND hwndTab = 0 , hwndStatic1,hwndStatic2;
TCITEM tie;
RECT rcClient;
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
TCHAR tabLBL1[256];
GetClientRect(hwnd, &rcClient);
switch (message)
{
case WM_CREATE:
{
hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
hwndTab = CreateWindow(WC_TABCONTROL, L"",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, rcClient.right, rcClient.bottom,
hwnd, NULL, hInstance, NULL);
//Add tabs for each day of the week.
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.iImage = -1;
wsprintf(tabLBL1, L"tab1");
tie.pszText = tabLBL1;
TabCtrl_InsertItem(hwndTab, 0, &tie);
wsprintf(tabLBL1, L"tab2");
TabCtrl_InsertItem(hwndTab, 1, &tie);
hwndStatic1 = CreateWindow(WC_STATIC, L"123",
WS_CHILD | WS_VISIBLE | WS_BORDER,
200, 200, 100, 100, // Position and dimensions; example only.
hwndTab, NULL, hInstance, // g_hInst is the global instance handle
NULL);
hwndStatic2 = CreateWindow(WC_STATIC, L"456",
WS_CHILD | WS_VISIBLE | WS_BORDER,
400, 200, 100, 100, // Position and dimensions; example only.
hwndTab, NULL, hInstance, // g_hInst is the global instance handle
NULL);
ShowWindow(hwndStatic1, TRUE);
ShowWindow(hwndStatic2, FALSE);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_NOTIFY:
if (((LPNMHDR)lParam)->code == TCN_SELCHANGE)
{
int tabID = TabCtrl_GetCurSel(hwndTab);
switch (tabID)
{
case 0:
ShowWindow(hwndStatic1, TRUE);
ShowWindow(hwndStatic2, FALSE);
break;
case 1:
ShowWindow(hwndStatic1, FALSE);
ShowWindow(hwndStatic2, TRUE);
break;
default:
break;
}
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
Zhu Song's answer helped solved the problem (the container windows needing to be a static control, and needing to be positioned inside the tab control's space, below the clickable tabs).
Here is the result, swapping the two container window creation lines:
hTab0 = CreateWindow(TEXT("Static"), NULL, WS_CHILD | WS_VISIBLE, 1, 25, 197, 123, hTab, (HMENU)ID_TAB0, NULL, NULL);
hTab1 = CreateWindow(TEXT("Static"), NULL, WS_CHILD, 1, 25, 197, 123, hTab, (HMENU)ID_TAB1, NULL, NULL);
Note how the container window sizes and position are small enough to just fit inside the main tab control window (you can add WS_BORDER to the style to see where exactly it fits).

How to hide the cursor completely?

When I call ShowCursor(0) (which by the way, does not take an HWND, this is weird), the cursor is hidden, but when I do a special action, for example resizing the window or drag-and-drop a file, the cursor corresponding to this action appears. I don't want to see the cursor whatever action the user is doing. If you wonder why, it's because I'd like to draw it with OpenGL.
I have a popup and layered window:
#define UNICODE
#include <windows.h>
#include <dwmapi.h>
LRESULT CALLBACK wnd_proc(HWND hWnd, UINT uMsg, WPARAM wp, LPARAM lp){
switch(uMsg){
case WM_CREATE: return 0;
case WM_NCHITTEST: return HTBOTTOM;
case WM_DESTROY: PostQuitMessage(0); return 0;
default: return DefWindowProc(hWnd, uMsg, wp, lp);
}
}
HDC hDC; HGLRC hRC;
int main(void){
WNDCLASSEX wcx = {};
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.lpfnWndProc = wnd_proc;
wcx.lpszClassName = L"Win32Class";
wcx.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wcx.hbrBackground = CreateSolidBrush(0x00000000);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClassEx(&wcx);
HWND hWnd = CreateWindowEx(WS_EX_ACCEPTFILES | WS_EX_LAYERED, wcx.lpszClassName,
L"Win32Window", WS_POPUP | WS_VISIBLE, 50, 50, 800, 400, NULL, NULL, NULL, NULL);
SetLayeredWindowAttributes(hWnd, RGB(200, 0, 200), 0, LWA_COLORKEY);
ShowCursor(0);
/*DWM_BLURBEHIND blur = {0};
blur.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
blur.fEnable = 1;
blur.hRgnBlur = CreateRectRgn(0, 0, -1, -1);
DwmEnableBlurBehindWindow(hWnd, &blur);*/
MSG msg; while(GetMessage(&msg, NULL, 0, 0)){
DispatchMessage(&msg);
}
}
This is a truncated part of my test file. OpenGL is not included, but I just need to know how to delete the cursor.
If you compile and execute it, you can see that when your cursor is on the window, it is not visible, but when you resize the window the cursor appears, same as when you drag-and-drop a file.

Treeview changes color of Item Image

So I've been trying to add images to my WinAPI TreeView Common Control items. However, I have encountered something that has really been bothering me and I have no clue why it is happening. For some reason, the Image in a TreeView has a different color than the actual bitmap does. I took a screenshot of a test program I made that paints a BMP image file along with its TreeView counterpart. (It is the exact same image yet both drawings yield different results).
As you can see the image on the left is what it should look like and the image on the right is what the TreeView is drawing. Is this just the way TreeView works or something? Or is there something in my code that I'm doing wrong? It would be much appreciated if anyone could point this out because out of personal taste, the TreeView image looks visually disgusting and I would like the TreeView Control to draw my image correctly :P
The following is the code I am using to create the Treeview:
//Load image from relative file path
g_hBmp = LoadImage(GetModuleHandle(NULL), "image.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
//Create Treeview
HWND treeview = CreateWindowEx(0, WC_TREEVIEW, NULL,
WS_VISIBLE | WS_CHILD | TVS_FULLROWSELECT,
250, 100, 500, 300,
hwnd, NULL, GetModuleHandle(NULL), NULL);
//Add single treeview item
TVITEM tvi = {0};
TVINSERTSTRUCT tvins = {0};
HIMAGELIST himl = NULL;
himl = ImageList_Create(90, 90, 0, 1, 0);
int image = ImageList_Add(himl, g_hBmp, NULL);
SendMessage(treeview, TVM_SETIMAGELIST, (WPARAM) TVSIL_NORMAL, (LPARAM) himl);
tvi.mask = TVIF_TEXT | TVIF_IMAGE;
//Set Text
tvi.pszText = "Some Item";
tvi.cchTextMax = sizeof("Some Item") - 1;
tvi.iImage = image;
tvins.item = tvi;
tvins.hInsertAfter = TVI_FIRST;
SendMessage(treeview, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT) &tvins);
And here is my entire code in case you want to copy it:
#include <windows.h>
#include <commctrl.h>
HBITMAP g_hBmp = NULL;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: {
//Load Image
g_hBmp = LoadImage(GetModuleHandle(NULL), "image.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
//Create Treeview
HWND treeview = CreateWindowEx(0, WC_TREEVIEW, NULL,
WS_VISIBLE | WS_CHILD | TVS_FULLROWSELECT,
250, 100, 500, 300,
hwnd, NULL, GetModuleHandle(NULL), NULL);
//Add single treeview item
TVITEM tvi = {0};
TVINSERTSTRUCT tvins = {0};
HIMAGELIST himl = NULL;
himl = ImageList_Create(90, 90, 0, 1, 0);
int image = ImageList_Add(himl, g_hBmp, NULL);
SendMessage(treeview, TVM_SETIMAGELIST, (WPARAM) TVSIL_NORMAL, (LPARAM) himl);
tvi.mask = TVIF_TEXT | TVIF_IMAGE;
//Set Text
tvi.pszText = "Some Item";
tvi.cchTextMax = sizeof("Some Item") - 1;
tvi.iImage = image;
tvins.item = tvi;
tvins.hInsertAfter = TVI_FIRST;
SendMessage(treeview, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT) &tvins);
break;
}
case WM_PAINT: {
PAINTSTRUCT ps = {0};
//Setup
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcPaint = CreateCompatibleDC(hdc);
HBITMAP hBmpOld = (HBITMAP) SelectObject(hdcPaint, g_hBmp);
//Painting
BitBlt(hdc, 100, 100, 90, 90, hdcPaint, 0, 0, SRCCOPY);
//Cleanup
SelectObject(hdcPaint, hBmpOld);
DeleteDC(hdcPaint);
EndPaint(hwnd, &ps);
break;
}
case WM_DESTROY: {
DeleteObject(g_hBmp);
PostQuitMessage(0);
break;
}
default: return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
InitCommonControls();
HWND hwnd = NULL;
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH) COLOR_WINDOW;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Parent Window";
wc.style = CS_VREDRAW | CS_HREDRAW;
RegisterClassEx(&wc);
hwnd = CreateWindowEx(0, wc.lpszClassName, "Render Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 1000, 1000 / 16 * 9,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nShowCmd);
UpdateWindow(hwnd);
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
As mentioned above by 'Swordfish', what I was missing was the proper flag when calling the ImageList_Create() function. As you can see in my call to ImageList_Create(90, 90, 0, 1, 0); I specify '0' as the third parameter (which represents the Image List Creation Flag). As stated in the documentation, the ILC_COLOR flag has a value of zero; when this flag is set it uses the 'default behavior' which is often ILC_COLOR4 (i.e. 4 bits per channel).
To fix this all I needed to do was simply call the function using the ILC_COLOR24 flag, indicating that my images have a 24-bit depth.
ImageList_Create(90, 90, ILC_COLOR24, 1, 0);

Why is the clipping rectangle of the parent window the same as the size of the child window?

I have an app that has a parent window and a tab control child window. The tab control has the CS_HREDRAW and CS_VREDRAW class styles. When I resize the parent window, the clipping rect of the parent in WM_PAINT is the same as the size of the tab control. This does not happen with child windows that don't have the CS_HREDRAW and CS_VREDRAW class styles. Normally when you resize the parent window the clipping rect of the parent window is equal to the uncovered part of the parent window. Why does this happen?
EDIT: Why is the clipping rect for the parent window the size of the whole client area of the parent? I have not specified CS_HREDRAW/CS_VREDRAW for the parent class. I want the clipping rect of the parent window to be only the uncovered area when I resize it. From what I understand the child window inherits CS_HREDRAW/CS_VREDRAW if the parent window has those class styles. The documentation doesn't say that the parent inherits those styles from the child. If the parent window doesn't have the CS_HREDRAW/CS_VREDRAW class styles, but the child does, then does the parent inherit those styles from the child?
#include <windows.h>
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")
HINSTANCE g_hInst;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND hTab;
static RECT rc;
switch(msg)
{
case WM_CREATE:
hTab = CreateWindowEx(0, WC_TABCONTROL, 0, WS_VISIBLE | WS_CHILD, 0, 0, 0, 0, hwnd, 0, g_hInst, 0);
break;
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
WCHAR text[70];
hdc = BeginPaint(hwnd, &ps);
wsprintf(text, L"%d, %d, %d, %d", ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)text);
EndPaint(hwnd, &ps);
}
break;
case WM_SIZE:
GetClientRect(hwnd, &rc);
SetWindowPos(hTab, 0, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
const TCHAR szClassName[] = L"Appppppppppppp";
WNDCLASSEX wc = { 0 };
HWND hwnd;
MSG msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = szClassName;
if(!RegisterClassEx(&wc)) return 0;
g_hInst = hInstance;
InitCommonControls();
hwnd = CreateWindowEx(0, szClassName, L"App", WS_OVERLAPPEDWINDOW, 40, 40, 420, 200, 0, 0, hInstance, 0);
if(!hwnd) return 0;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
The issue is that your WM_SIZE handler is too late, parts of the main window are already marked dirty. You'd have to do it earlier, at WM_SIZING time, before calling DefWindowProc(). Which works fine, but with the gritty problem that it is hard to calculate the client area from the proposed new window size.
The simple workaround is to mark the part of the window occupied by the child control as validated:
case WM_SIZE:
GetClientRect(hwnd, &rc);
SetWindowPos(hTab, 0, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER);
ValidateRect(hwnd, &rc); // <== added
break;

Resources