Related
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.
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.
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.
When sliding the trackbar and release the mouse button, the whole window flashes and the tabs dissappear.
When I use the old version, everything works properly!
When I use the new Microsoft Windows Common Controls ver.6.0, this problem is observed !!!
#include <windows.h>
#include <commctrl.h>
#include <tchar.h>
#pragma comment(lib,"comctl32.lib")
HWND hWin, hTab;
#if defined _M_X64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_IA64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_IX86
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
LRESULT CALLBACK WndProcedure( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ) {
switch( Msg ) {
case WM_CREATE:{
HINSTANCE hInst = GetModuleHandle( NULL );
RECT rc;
int dx, dy;
GetClientRect( hWnd, &rc );
dx = rc.right - rc.left;
dy = rc.bottom - rc.top;
TCITEM tie = { TCIF_TEXT | TCIF_IMAGE, 0, 0, NULL, 0, -1, 0 };
hTab = CreateWindowEx( NULL, WC_TABCONTROL, _T(""), WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS, 0, 0, dx, dy, hWnd, ( HMENU )( DWORDLONG )1001, hInst, NULL );
tie.pszText = _T("Tab One");
TabCtrl_InsertItem( hTab, 0, &tie );
tie.pszText = _T("Tab Two");
TabCtrl_InsertItem( hTab, 1, &tie );
CreateWindowEx( NULL, TRACKBAR_CLASS, _T(""), WS_VISIBLE | WS_CHILD | WS_TABSTOP, 50, 50, 200, 40, hTab, ( HMENU )1002, hInst, NULL );
}
break;
case WM_NOTIFY: {
LPNMHDR ns = (LPNMHDR)lParam;
if( (ns->idFrom == 1001) && (ns->code == TCN_SELCHANGING) )
return FALSE;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, Msg, wParam, lParam));
}
return FALSE;
}
INT WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) {
INITCOMMONCONTROLSEX icc = { sizeof( INITCOMMONCONTROLSEX ), ICC_WIN95_CLASSES };
WNDCLASSEX WndClsEx;
MSG Msg;
ZeroMemory( &WndClsEx, sizeof( WNDCLASSEX ) );
WndClsEx.cbSize = sizeof(WNDCLASSEX);
WndClsEx.style = CS_HREDRAW | CS_VREDRAW;
WndClsEx.lpfnWndProc = WndProcedure;
WndClsEx.hIcon = LoadIcon( NULL, IDI_APPLICATION );
WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW );
WndClsEx.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
WndClsEx.lpszClassName = _T("Trackbar_Tester");
WndClsEx.hInstance = hInstance;
RegisterClassEx(&WndClsEx);
InitCommonControlsEx( &icc );
if( !(hWin = CreateWindow( WndClsEx.lpszClassName, _T("TB_Tester"), WS_OVERLAPPEDWINDOW, 0, 0, 600, 400, NULL, NULL, hInstance, NULL )) )
return 0;
ShowWindow( hWin, SW_SHOWNORMAL );
UpdateWindow( hWin );
while( GetMessage(&Msg, NULL, 0, 0) ) {
TranslateMessage( &Msg );
DispatchMessage( &Msg );
}
return (int)Msg.wParam;
}
That's the whole program.
I give a compact example.
If you run it, you will see the problem.
If you comment the new controls, the problem will disappear.
The tab control common control is not going to manage separate content panes for you, so if you want that track bar to be a child of the tab control that works in the way we expect a tab control to work, you need to manage child panes yourself.
You can use "TabCtrl_AdjustRect" to figure out how big the child panes need to be. See the following modification of your code, for example:
#define TAB_ID 2000
HWND hWin, hTab;
HWND g_tabPanes[2];
HWND CreateTabPane(HWND tabctrl, int id, HINSTANCE instance)
{
RECT rcTab;
GetClientRect(tabctrl, &rcTab);
TabCtrl_AdjustRect(tabctrl, FALSE, &rcTab);
int wd = rcTab.right - rcTab.left;
int hgt = rcTab.bottom - rcTab.top;
return CreateWindow(
L"static", L"",
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
rcTab.left, rcTab.top, wd, hgt,
tabctrl,
(HMENU) id,
instance,
NULL
);
}
LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_CREATE: {
HINSTANCE hInst = GetModuleHandle(NULL);
RECT rc; int dx, dy;
GetClientRect(hWnd, &rc);
dx = rc.right - rc.left;
dy = rc.bottom - rc.top;
TCITEM tie = {
TCIF_TEXT | TCIF_IMAGE,
0, 0,
NULL,
0, -1, 0
};
hTab = CreateWindowEx(NULL, WC_TABCONTROL, _T(""),
WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
0, 0, dx, dy, hWnd,
(HMENU)1001, hInst, NULL
);
tie.pszText = (LPWSTR)_T("Tab One");
TabCtrl_InsertItem(hTab, 0, &tie);
tie.pszText = (LPWSTR)_T("Tab Two");
TabCtrl_InsertItem(hTab, 1, &tie);
for (int i = 0; i < 2; i++)
g_tabPanes[i] = CreateTabPane(hTab, TAB_ID + i, hInst);
CreateWindowEx(NULL, TRACKBAR_CLASS, _T(""), WS_VISIBLE | WS_CHILD | WS_TABSTOP,
50, 50, 200, 40, g_tabPanes[0], (HMENU)1002, hInst, NULL);
}
break;
case WM_NOTIFY: {
LPNMHDR ns = (LPNMHDR)lParam;
if ((ns->idFrom == 1001) && (ns->code == TCN_SELCHANGE)) {
int pane = TabCtrl_GetCurSel(hTab);
for (int i = 0; i < 2; i++)
if (pane == i)
ShowWindow(g_tabPanes[i], SW_SHOW);
else
ShowWindow(g_tabPanes[i], SW_HIDE);
}
} break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default: return (DefWindowProc(hWnd, Msg, wParam, lParam));
}
return FALSE;
}
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
INITCOMMONCONTROLSEX icc = {
sizeof(INITCOMMONCONTROLSEX),
ICC_WIN95_CLASSES
};
WNDCLASSEX WndClsEx;
MSG Msg;
ZeroMemory(&WndClsEx, sizeof(WNDCLASSEX));
WndClsEx.cbSize = sizeof(WNDCLASSEX);
WndClsEx.style = CS_HREDRAW | CS_VREDRAW;
WndClsEx.lpfnWndProc = WndProcedure;
WndClsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClsEx.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
WndClsEx.lpszClassName = _T("Trackbar_Tester");
WndClsEx.hInstance = hInstance;
RegisterClassEx(&WndClsEx);
InitCommonControlsEx(&icc);
if (!(hWin = CreateWindow(WndClsEx.lpszClassName, _T("TB_Tester"), WS_OVERLAPPEDWINDOW| WS_CLIPSIBLINGS, 0, 0, 600, 400, NULL, NULL, hInstance, NULL)))
return 0;
ShowWindow(hWin, SW_SHOWNORMAL);
UpdateWindow(hWin);
while (GetMessage(&Msg, NULL, 0, 0)) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return (int)Msg.wParam;
}
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..