Status bar control not resizing properly when using XP Visual Styles manifest - c++

In my program I created a status bar control and then set a new font for it.
The problem I'm having is that when I include the XP Visual Styles
manifest (i.e. Common controls 6.0), then the status bar is not resizing to match the new font size.
e.g.
hGiantFont = CreateFont(-48, 0, 0, 0, FW_NORMAL, FALSE, FALSE,
FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
FF_DONTCARE, L"MS Shell Dlg 2");
hStatusBar = CreateWindowEx (
0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE,
0, 0, 0, 0, hWnd, (HMENU)666, hInst, NULL
);
SendMessage(hStatusBar, WM_SETFONT, (WPARAM)hGiantFont,
(LPARAM)MAKELONG(TRUE, 0));
SendMessage(hStatusBar, WM_SIZE, 0, 0);
After executing the last line, nothing happens! It doesn't resize.
Note that if I don't include the visual styles Manifest, it works fine!
I've tried both InitCommonControls() and InitCommonControlsEX() with ICC_BAR_CLASSES to no avail.
I've also tried using MoveWindow and SetWindowPos to change the size or move the status bar. With the visual styles manifest included, the status bar does not move, it seems glued to that specific size and location.
Is this a bug in ComCtl32.dll 6.0? Or an extremely annoying intended feature. What is the work around?
Also has anoyone else tried doing the same thing with successful results?
Edit:
Ok I've decided to include the whole program below, so people can try it out and see what I mean. It's just a bare basics Win32 app with a few lines added. If you comment out the #pragma manifest line at the top you'll notice it works as expected, and with the manifest line, the status bar doesn't resize.
// StupidStatusBar.cpp : Defines the entry point for the application.
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")
// Global Variables:
HINSTANCE hInst;
wchar_t *szTitle = L"Stupid Status Bar";
wchar_t *szWindowClass = L"StupidClass";
// 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 APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
MSG msg;
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_WIN95_CLASSES;
if(!InitCommonControlsEx(&icex))
{
MessageBox(NULL, L"Error initializing common controls", L"Error", MB_OK);
return 1;
}
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
return FALSE;
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX 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(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
return RegisterClassEx(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HWND hStatusBar;
HFONT hGiantFont;
switch (message)
{
case WM_CREATE:
hGiantFont = CreateFont(-48, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"MS Shell Dlg 2");
hStatusBar = CreateWindowEx (
0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE,
0, 0, 0, 0, hWnd, (HMENU)666, hInst, NULL
);
SendMessage(hStatusBar, WM_SETFONT, (WPARAM)hGiantFont, (LPARAM)MAKELONG(TRUE, 0));
SendMessage(hStatusBar, WM_SIZE, 0, 0);
SetWindowText(hStatusBar, L"Testing Testing");
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

Solved it. Had to override the default proc for the status bar and make its WM_SIZE case return 0. Then manually resize using MoveWindow from my main window.

I had a similar problem, and I think there is no crap in the Win32 API library, it Works but a certain procedure has to be followed that is not shown so clearly in the Win32 API documentation (as of now, it is sketched at https://code.msdn.microsoft.com/CppWindowsCommonControls-9ea0de64, section L); it is as follows:
1) globally, define the variable hWndStatusbar:
HWND hWndStatusbar;
2) in the WM_CREATE of the main window procedure add the following code at the end (if "case WM_CREATE" is not already treated in the WndProc window procedure, just add it):
RECT rcStatusRect;
int iStatusBarWidths[1];
GetClientRect(hWnd, &rcStatusRect);
iStatusBarWidths[0] = rcStatusRect.right;
hWndStatusbar = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0, hWnd, NULL, hInst, NULL);
SendMessage(hWndStatusbar, SB_SETPARTS, (WPARAM)1, (LPARAM)iStatusBarWidths);
SendMessage((HWND)hWndStatusbar, (UINT)SB_SETTEXT, (WPARAM)(INT)0 | 0, (LPARAM)(LPSTR)TEXT("Hello"));
3) if "case WM_SIZE" is not already treated in your WndProc window procedure, just add it, and add the following code at the end:
int iStatusBarWidths[1];
iStatusBarWidths[0] = -1;
SendMessage(hWndStatusbar, SB_SETPARTS, (WPARAM)1, (LPARAM)iStatusBarWidths);
SendMessage(hWndStatusbar, WM_SIZE, 0, 0);
That is like everything, for me it Works like this, under Visual Studio Community 2015..

Related

WinAPI rebar control not showing up

I'm working on a pure WinAPI application in C++ and I want to add a toolbar that lives inside/on a rebar control. I can create and add the toolbar but I can't get the rebar to show up (there are no grippers even though I set the RBBS_GRIPPERALWAYS style):
I took the code from the MSDN pages about toolbar/rebar controls and assembled a minimal sample but still no success. Return codes of CreateWindow and SendMessage etc. look okay to me. I also tried the solution of this question but I can't get it working anyways. Any help would be greatly appreciated.. here's the complete source code:
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <CommCtrl.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include "Resource.h"
#pragma comment(lib, "comctl32.lib")
// Enable visual styles
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// Toolbar defines
#define IDM_NEW 100
#define IDM_OPEN 101
#define IDM_SAVE 102
#define NUM_TBBUTTONS 3
// Global variables
HINSTANCE g_hInst;
HIMAGELIST g_hImageList = NULL;
// Forward declarations
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HWND CreateSimpleToolbar(HWND hwndParent);
HWND CreateSimpleRebar(HWND hwndParent, HWND hwndToolbar);
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) {
// Initialize common controls.
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_COOL_CLASSES | ICC_BAR_CLASSES;
InitCommonControlsEx(&icex);
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_REBARTEST));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_REBARTEST);
wcex.lpszClassName = _T("RebarTest");
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassExW(&wcex);
HWND hwndMainWindow = CreateWindowW(_T("RebarTest"), _T("AppTitle"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
nullptr, nullptr, hInstance, nullptr);
if (!hwndMainWindow)
return FALSE;
ShowWindow(hwndMainWindow, nCmdShow);
UpdateWindow(hwndMainWindow);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
static HWND hwndToolbar, hwndRebar;
switch (message)
{
case WM_CREATE:
{
hwndToolbar = CreateSimpleToolbar(hwnd);
hwndRebar = CreateSimpleRebar(hwnd, hwndToolbar);
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Menüauswahl analysieren:
switch (wmId)
{
case IDM_EXIT:
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
HWND CreateSimpleToolbar(HWND hwndParent) {
// Create the toolbar.
HWND hwndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | TBSTYLE_WRAPABLE, 0, 0, 0, 0, hwndParent, NULL, g_hInst, NULL);
if (hwndToolbar == NULL)
return NULL;
// Create the image list.
g_hImageList = ImageList_Create(16, 16, ILC_COLOR16 | ILC_MASK, NUM_TBBUTTONS, 0);
// Set the image list and add buttons
const int imageListID = 0;
SendMessage(hwndToolbar, TB_SETIMAGELIST, (WPARAM)imageListID, (LPARAM)g_hImageList);
SendMessage(hwndToolbar, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);
// Initialize button info.
TBBUTTON tbButtons[NUM_TBBUTTONS] =
{
{ MAKELONG(STD_FILENEW, imageListID), IDM_NEW, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)L"New" },
{ MAKELONG(STD_FILEOPEN, imageListID), IDM_OPEN, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)L"Open"},
{ MAKELONG(STD_FILESAVE, imageListID), IDM_SAVE, 0, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)L"Save"}
};
// Add buttons.
SendMessage(hwndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(hwndToolbar, TB_ADDBUTTONS, (WPARAM)NUM_TBBUTTONS, (LPARAM)&tbButtons);
// Resize the toolbar, and then show it.
SendMessage(hwndToolbar, TB_AUTOSIZE, 0, 0);
ShowWindow(hwndToolbar, TRUE);
return hwndToolbar;
}
HWND CreateSimpleRebar(HWND hwndParent, HWND hwndToolbar) {
// Check parameters.
if (!hwndParent || !hwndToolbar)
return NULL;
// Create the rebar.
HWND hwndRebar = CreateWindowEx(WS_EX_TOOLWINDOW,
REBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
RBS_VARHEIGHT | CCS_NODIVIDER | RBS_BANDBORDERS,
0, 0, 0, 0,
hwndParent, NULL, g_hInst, NULL);
if (!hwndRebar)
return NULL;
// Get the height of the toolbar.
DWORD dwBtnSize = (DWORD)SendMessage(hwndToolbar, TB_GETBUTTONSIZE, 0, 0);
REBARBANDINFO rbBand;
rbBand.cbSize = sizeof(REBARBANDINFO);
rbBand.fMask =
RBBIM_STYLE // fStyle is valid.
| RBBIM_TEXT // lpText is valid.
| RBBIM_CHILD // hwndChild is valid.
| RBBIM_CHILDSIZE // child size members are valid.
| RBBIM_SIZE; // cx is valid
rbBand.fStyle = RBBS_CHILDEDGE | RBBS_GRIPPERALWAYS;
rbBand.lpText = (LPWSTR)_T("");
rbBand.hwndChild = hwndToolbar;
rbBand.cyChild = LOWORD(dwBtnSize);
rbBand.cxMinChild = NUM_TBBUTTONS * HIWORD(dwBtnSize);
rbBand.cyMinChild = LOWORD(dwBtnSize);
rbBand.cx = 0; // The default width is the width of the buttons.
// Add the band with the toolbar.
SendMessage(hwndRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);
return hwndRebar;
}
As described in the comments on this question, you need to have a _WIN32_WINNT defined, which I assume you may have in your "targetver.h" header. Since you are declaring common-controls version 6, I think you need to target WS-2003/Vista at a minimum, according to this. I first tried #define WINVER 0x0601 and #define _WIN32_WINNT_WIN7 0x0601 with your code and it worked on my machine.
I think the question you linked is a little out of date, this table says the structure size for common-controls version 6 should be REBARBANDINFO_V6_SIZE. However, I encountered the 4 pixel height issue from your linked question when using that structure size. Keeping your sizeof code as-is worked for me.
I think the real issue you have is your toolbar is handling its own resizing and positioning. As described on this About Toolbar Control page, you need to change this
HWND hwndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | TBSTYLE_WRAPABLE, 0, 0, 0, 0, hwndParent, NULL, g_hInst, NULL);
to this
HWND hwndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | TBSTYLE_WRAPABLE | CCS_NORESIZE | CCS_NOPARENTALIGN, 0, 0, 0, 0, hwndParent, NULL, g_hInst, NULL);
in your CreateSimpleToolbar function so the host rebar can take control of the toolbar sizing and positioning.

WS_TABSTOP in winapi on edit controls of child windows

In my WinAPI application, I have a series of edit controls in a child window. I would like the user to be able to move between them by pressing on the tab key to go forward and shift-tab to go back, but I can't seem to figure out how to use WS_TABSTOP with child windows. What I intend to have happen is that when the user clicks the tab key, the subsequent edit control is selected. However, when I click the tab in the window of the following code the cursor simply disappears.
Here is a minimal reproducible example:
//libraries
#pragma comment ("lib", "Comctl32.lib")
#pragma comment ("lib", "d2d1.lib")
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
#include <CommCtrl.h>
// C RunTime Header Files
#include <vector>
#include <string>
#define IDS_APP_TITLE 103
#define IDI_PRACTICE 107
#define IDI_SMALL 108
#define IDC_PRACTICE 109
#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
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProcChild(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
ATOM MyRegisterClass(HINSTANCE hInstance);
HWND childHWND;
HWND InitInstance(HINSTANCE hInstance, int nCmdShow);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_PRACTICE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
// Perform application initialization:
HWND hWnd = InitInstance(hInstance, nCmdShow);
if(!hWnd)
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PRACTICE));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
if (!IsDialogMessage(hWnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProcChild(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
HWND edit1 = CreateWindow(WC_EDIT, L"", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP, 100, 100, 100, 100, hWnd, (HMENU)1, hInst, NULL);
HWND edit2 = CreateWindow(WC_EDIT, L"", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP, 300, 100, 100, 100, hWnd, (HMENU)2, hInst, NULL);
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
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_PRACTICE));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(GetStockObject(WHITE_BRUSH));
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_PRACTICE);
wcex.lpszClassName = L"Parent";
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
//Child wnd class
WNDCLASSEXW wcexChild;
wcexChild.cbSize = sizeof(WNDCLASSEX);
wcexChild.style = CS_HREDRAW | CS_VREDRAW;
wcexChild.lpfnWndProc = WndProcChild;
wcexChild.cbClsExtra = 0;
wcexChild.cbWndExtra = 0;
wcexChild.hInstance = hInstance;
wcexChild.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PRACTICE));
wcexChild.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcexChild.hbrBackground = (HBRUSH)(GetStockObject(WHITE_BRUSH));
wcexChild.lpszMenuName = MAKEINTRESOURCEW(IDC_PRACTICE);
wcexChild.lpszClassName = L"Child";
wcexChild.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcexChild) && RegisterClassExW(&wcex);
}
HWND InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(L"Parent", L"PARENT", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);
childHWND = CreateWindowW(L"Child", L"", WS_CHILD | WS_VISIBLE | WS_EX_CONTROLPARENT,
0, 0, 700, 700, hWnd, nullptr, hInstance, nullptr);
if (!hWnd)
{
return NULL;
}
ShowWindow(childHWND, nCmdShow);
UpdateWindow(childHWND);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return hWnd;
}
Problem here is that if (!IsDialogMessage(hWnd, &msg)) gets called on the wrong window.
Replacing the line with if (!IsDialogMessage(childHWND, &msg)) gets TAB navigation to work.
From the IsDialogMessage documentation:
Although the IsDialogMessage function is intended for modeless dialog boxes, you can use it with any window that contains controls, enabling the windows to provide the same keyboard selection as is used in a dialog box.
In the posted code, the "window that contains controls" is the direct parent of the edit controls, which is childHWND.
[ EDIT ]   One other problem pointed out in the comments (thanks #IInspectable) is that the extended style WS_EX_CONTROLPARENT is mistakenly passed with the style flags, instead of the extended style flags. To fix that, the call to childHWND = CreateWindowW(L"Child", L"", WS_CHILD | WS_VISIBLE | WS_EX_CONTROLPARENT, ... should be changed to childHWND = CreateWindowExW(WS_EX_CONTROLPARENT, L"Child", L"", WS_CHILD | WS_VISIBLE, ... instead.

Adding Different Components in Tab Control Win32

I am creating a Windows desktop application using win32 api in visual studio 2019. I know there are many other options avialable to build UI like MFC, XAMAL and C#, but i needed to build it in win32. I have learnt some basics in win32 api but recently i was working on tabControll and there i got an issue or i missed some thing. I am creating two tabs and want to add different content withing them. My current code is working and creating the tabs but it is adding same content in both tabs. How should i define each tab's content differently.
void createTabView(HWND hWnd) {
RECT rcClient;
INITCOMMONCONTROLSEX icex;
static HWND hwndTab_1_1_1;
HWND hwndTab;
TCITEM tie;
int i;
TCHAR achTemp[256];
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
GetClientRect(hWnd, &rcClient);
hwndTab = CreateWindow(WC_TABCONTROL, L"",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, rcClient.right, rcClient.bottom,
hWnd, NULL, hInst, NULL);
// Add tabs for each day of the week.
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.iImage = -1;
tie.pszText = tabLBL1;
TabCtrl_InsertItem(hwndTab, 1, &tie);
tie.pszText = tabLBL2;
TabCtrl_InsertItem(hwndTab, 2, &tie);
SendMessage(hwndTab, WM_SETFONT,
reinterpret_cast<WPARAM>(GetStockObject(DEFAULT_GUI_FONT)), 0);
HWND hwndStatic = CreateWindow(WC_STATIC, L"",
WS_CHILD | WS_VISIBLE | WS_BORDER,
200, 200, 100, 100, // Position and dimensions; example only.
hwndTab, NULL, hInst, // g_hInst is the global instance handle
NULL);}
The tab control triggers the WM_NOTIFY signal when the tab page is switched, so as long as we process the WM_NOTIFY message, we can control the tab control message.
In the WM_NOTIFY message:
wParam: a control ID that identifies the WM_NOTIFY message sent.
lParam: a pointer to the NMHDR structure.
Therefore, we can determine the notification code sent by the Tab control in the WM_NOTIFY message processing program by judging the code value in the NMHDR structure.
We can use TCN_SELCHANGE to handle the operation when the Tab tab changes, and use TabCtrl_GetCurSel to get the index of the current tab and define the content of the current tab.
Here is the sample:
#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 , hwndStatic = 0;
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, "",
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, "tab1");
tie.pszText = tabLBL1;
TabCtrl_InsertItem(hwndTab, 0, &tie);
wsprintf(tabLBL1, "tab2");
TabCtrl_InsertItem(hwndTab, 1, &tie);
SendMessage(hwndTab, WM_SETFONT,
reinterpret_cast<WPARAM>(GetStockObject(DEFAULT_GUI_FONT)), 0);
hwndStatic = CreateWindow(WC_STATIC, "",
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);
ShowWindow(hwndStatic,TRUE);
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(hwndStatic, TRUE);
break;
case 1:
ShowWindow(hwndStatic, FALSE);
break;
default:
break;
}
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
Not sure if this is the solution, but the iItem parameter to TabCtrl_InsertItem is zero-based. Try:
tie.pszText = tabLBL1;
TabCtrl_InsertItem(hwndTab, 0, &tie);
tie.pszText = tabLBL2;
TabCtrl_InsertItem(hwndTab, 1, &tie);
Also (and I'm not sure if this is relevant), you look like you intended to call InitCommonControlsEx in your code but fail to do so.

How to keep edit control from showing up when covered by another control?

I have created an edit box and a listbox. When they overlap, with the listbox on top, the mouse cursor changes to an I-Beam when it is over the part of the listbox that is covering part of the edit box and an outline of the edit box appears:
BEFORE mouse over:
AFTER mouse over:
Code used to create the controls:
HWND hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "xxx", WS_CHILD | WS_VISIBLE| ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
50, 100, 200, 100, hWnd, (HMENU)IDC_MAIN_EDIT, GetModuleHandle(NULL), NULL);
HWND hList = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, _T(""), WS_VISIBLE | WS_CHILD | LVS_REPORT | LVS_EDITLABELS,
70, 150, 200, 100, hWnd, (HMENU)ID_LIST_VIEW, GetModuleHandle(NULL), NULL);
I have tried fiddling with the x-order using SetWindowPos but it doesn't change the behaviour. In any case, I think the control order creation should place the listbox above the edit control.
Why is it doing this and more importantly, how do I fix it so the edit stays covered?
To reproduce, create a win32 project in VS2015. Add the above 2 lines to WM_CREATE along with the appropriate includes defines etc. That's pretty much it. The code below is without any items added to the listview.
UPDATE: By popular demand here is the complete code:
#include "stdafx.h"
#include "Win32Project3.h"
#include <commctrl.h>
#define MAX_LOADSTRING 100
#define IDC_MAIN_EDIT 140
#define ID_LIST_VIEW 143
#pragma comment( lib, "comctl32.lib")
#pragma comment(linker, "\"/manifestdependency:type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#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 APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WIN32PROJECT3, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance,MAKEINTRESOURCE(IDC_WIN32PROJECT3));
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_WIN32PROJECT3));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WIN32PROJECT3);
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);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
HWND hList = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTBOX, _T(""), WS_VISIBLE | WS_CHILD | LVS_REPORT | LVS_EDITLABELS,
70, 150, 200, 100, hWnd, (HMENU)ID_LIST_VIEW, GetModuleHandle(NULL),NULL);
HWND hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, _T("xxx"), WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
50, 100, 200, 160, hWnd, (HMENU)IDC_MAIN_EDIT, GetModuleHandle(NULL), NULL);
}
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);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
The effect is caused by one control rendering into the client area of another control, as it's handling WM_PAINT messages.
To prevent this from happening, rendering needs to be clipped, so that it excludes regions that are occupied by other controls. Setting the WS_CLIPSIBLINGS window style on controls instructs the system to apply an appropriate clipping region:
Clips child windows relative to each other; that is, when a particular child window receives a WM_PAINT message, the WS_CLIPSIBLINGS style clips all other overlapping child windows out of the region of the child window to be updated. If WS_CLIPSIBLINGS is not specified and child windows overlap, it is possible, when drawing within the client area of a child window, to draw within the client area of a neighboring child window.

How to duplicate a window's clientarea to another window?

I want to create a simple project (the language doesn't really matter/but I prefer C++) which simply takes a window title as it's input and duplicate it's visual part, bit by bit, to a new window. just like a mirror better I'd say.
As far as I remember there was a win32 API for this but I can't remember, so would you please tell me how can I achieve this?
And please tell me, will your answers work with DirectX/Open-GL applications as well or not?
You can get DC of first window, and get DC of second window. And after this do BitBlt or StretchBlt. It has to work... But I don't know what about DirectX/Open-Gl... I think it has to work too. But anyway. It won't take much time to check it.
Here is the source code for a program that will mirror other apps, you can use StretchBlt() instead of BitBlt(), this will not copy Menubars or popups.
Some apps like chrome, calculator, and camera use child windows for the visual part, and FindWindow() only scans through top-level windows for the title, so you can use FindWindowEx() with it to make that work!
#include <windows.h>
HWND sourceWindow;
void Mirror(HWND hwndSource, HWND hwndDst)
{
HDC source = GetDC(hwndSource);
HDC dst = GetDC(hwndDst);
RECT srcSiz;
GetClientRect(hwndSource, &srcSiz);
SetWindowPos(hwndDst, NULL, 0, 0, srcSiz.right, srcSiz.bottom, SWP_NOMOVE | SWP_NOZORDER);
BitBlt(dst, 0, 0, srcSiz.right, srcSiz.bottom, source, 0, 0, SRCCOPY);
ReleaseDC(hwndSource, source);
ReleaseDC(hwndDst, dst);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_CREATE:
SetTimer(hwnd, 1, 500, (TIMERPROC) NULL);//This will refresh after every 500 ms
break;
case WM_TIMER:
Mirror(sourceWindow, hwnd);
break;
case WM_DESTROY:
KillTimer(hwnd, 1);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(0, 0, 0)));
wc.lpszMenuName = NULL;
wc.lpszClassName = "MyWindow";
wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "MyWindow", "MyTitle", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 500,
NULL, NULL, hInstance, NULL);
if (!hwnd)
{
MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
sourceWindow = FindWindowA(NULL, "<WindowTitle>");//or you can use class name
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}