DS_CONTROL | WS_CHILD combination causes infinite loop - c

Hello.I'm trying to create a "dialog in dialog" example using the WinAPI and C. This example consists in a child dialog with an autocheckbox, and a main dialog containing a static black rectangle which is the parent for the child dialog and a push button that shows in a message box a text with the checkbox status.When I set the flags DS_CONTROL | WS_CHILD for the child dialog, whenever I try to change the checkbox status the application enters into an infinite loop and I have to force close it. When I remove the DS_CONTROL flag, works as intended but I cannot cycle between the controls using the tab key.What can I do in order to make it work as intended using the DS_CONTROL flag?Here's the content of my main.c file:
#include <windows.h>
#pragma comment (lib, "user32")
HINSTANCE hInst;
BOOL isChecked;
const unsigned char checkedStr[] = "Checkbox is checked";
const unsigned char notCheckedStr[] = "Checkbox is not checked";
BOOL CALLBACK ChildDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case 21:
isChecked = IsDlgButtonChecked(hwndDlg, 21);
return TRUE;
}
return FALSE;
}
return FALSE;
}
BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
HWND hContainer, hChilddDlg;
hContainer = GetDlgItem(hwndDlg, 11);
hChilddDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(20), hContainer, ChildDlgProc, 0);
ShowWindow(hChilddDlg, SW_SHOW);
return TRUE;
}
case WM_COMMAND:
switch (LOWORD(wParam))
{
case 12:
{
const unsigned char *ptr;
if (isChecked)
{
ptr = checkedStr;
}
else
{
ptr = notCheckedStr;
}
MessageBox(hwndDlg, ptr, TEXT("Checkbox status"), MB_OK | MB_ICONINFORMATION);
return TRUE;
}
}
return FALSE;
case WM_CLOSE:
EndDialog(hwndDlg, 0);
return TRUE;
}
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hInst = hInstance;
isChecked = FALSE;
return DialogBoxParam(hInstance, MAKEINTRESOURCE(10), NULL, DialogProc, 0);
}
And here's the content of my rsrc.rcfile:
#include <windows.h>
10 DIALOGEX 0, 0, 130, 47
STYLE DS_CENTER | DS_SETFONT | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU
CAPTION "Checkbox status"
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
FONT 9, "Segoe UI"
{
CONTROL "", 11, STATIC, SS_BLACKRECT | WS_CHILD | WS_VISIBLE, 5, 5, 120, 20
CONTROL "&Status", 12, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 75, 30, 50, 12
}
20 DIALOGEX 0, 0, 120, 20
STYLE DS_SETFONT | DS_CONTROL | WS_CHILD
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
FONT 9, "Segoe UI"
{
CONTROL "Checkbox", 21, BUTTON, BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 5, 50, 10
}
I compile it in the Visual C++ command prompt with: cl /c main.c && rc rsrc.rc && link /SUBSYSTEM:WINDOWS /OUT:test.exe main.obj rsrc.res.Thanks in advance for your help.

Found the solution in the NSIS source code. The problem was that I was putting the child dialog as a child of the blackrect so it was out of the event loop of the main dialog, causing the hang. In order to solve this I had to put it as a child of the main dialog and move it over the blackrect.Here's the updated code of the WM_INITDIALOG case in the main dialog proc:
// ...
case WM_INITDIALOG:
{
HWND hChilddDlg;
hChilddDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(20), hwndDlg, ChildDlgProc, 0);
if (hChilddDlg)
{
RECT rect;
GetWindowRect(GetDlgItem(hwndDlg, 11), &rect);
ScreenToClient(hwndDlg, (LPPOINT)&rect);
SetWindowPos(hChilddDlg, 0, rect.left, rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
ShowWindow(hChilddDlg, SW_SHOWNA);
}
return TRUE;
}
// ...
With this I was able to use the DS_CONTROL flag and tab cycle between controls.

Related

How do I make a tab control with children elements?

I'm learning WINAPI, I read there's no children control in the sense a C#'s TabControl control does, so you have to create elements and show/hide by yourself. I read it may be done by drawing a dialog box inside tab page's area so I went to create a borderless dialog box being tab control's child, to make it have effect like C#'s. But I still couldn't make it. My dialog box is floating rather being tab control's child. I don't know to make it inside the tab page, I've tried setting the hWndParent to tab control's HWND and WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT flags in dwExStyle but still is floating over the tab control. Different approaches to solve this are welcome. I'm creating the tab control like this:
void AddTabControl(HWND hwnd)
{
hTab = CreateWindowW(WC_TABCONTROLW, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_TABSTOP,
0, 30, 250, 250,
hwnd,
(HMENU) 9,
NULL,
NULL);
InsertTabItem(hTab, 10, L"A");
InsertTabItem(hTab, 11, L"B");
}
void InsertTabItem(HWND tabHwnd, UINT id, LPWSTR text)
{
TCITEMW tci = {0};
tci.mask = TCIF_TEXT;
tci.pszText = text;
tci.cchTextMax = lstrlenW(text);
if(SendMessage(tabHwnd, TCM_INSERTITEMW, id, (LPARAM) &tci) == -1) {
MessageBox(NULL,
L"couldn't create the new tab page",
L"tab errror",
MB_OK | MB_ICONERROR);
}
}
And the dialog box like this:
void CreateDialogBox(HWND hwnd)
{
CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT, // WS_EX_TOOLWINDOW to hide window from ALT+TAB
L"DialogClass", L"Dialog Box",
WS_VISIBLE | WS_POPUP | WS_SYSMENU,
100, 100, 400, 150,
hTab, NULL, ghInstance, NULL
);
}
the result is:
the expected result (made from C#, just for example, ignore the color differences, I'll fix this later):
here's the full code:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#define UNICODE
#include <windows.h>
#include <Commctrl.h>
#include <strsafe.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DialogProc(HWND, UINT, WPARAM, LPARAM);
void CreateDialogBox(HWND);
void RegisterDialogClass(HWND);
void AddTabControl(HWND hwnd);
void InsertTabItem(HWND tabHwnd, UINT id, LPWSTR text);
HINSTANCE ghInstance;
HWND mainWindow;
HWND hTab;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR pCmdLine, int nCmdShow) {
MSG msg = {0};
HWND hwnd;
WNDCLASSW wc = {0};
wc.lpszClassName = L"Window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
RegisterClassW(&wc);
hwnd = CreateWindowW(wc.lpszClassName, L"Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 500, 350, NULL, NULL, hInstance, NULL);
mainWindow = hwnd;
ghInstance = hInstance;
while( GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
RegisterDialogClass(hwnd);
AddTabControl(hwnd);
CreateDialogBox(hwnd);
break;
case WM_COMMAND:
CreateDialogBox(hwnd);
break;
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg) {
case WM_CREATE:
CreateWindowW(L"button", L"A",
WS_VISIBLE | WS_CHILD ,
50, 50, 80, 25, hwnd, (HMENU) 1, NULL, NULL);
CreateWindowW(L"button", L"B",
WS_VISIBLE | WS_CHILD ,
150, 50, 80, 25, hwnd, (HMENU) 2, NULL, NULL);
CreateWindowW(L"button", L"C",
WS_VISIBLE | WS_CHILD ,
250, 50, 80, 25, hwnd, (HMENU) 3, NULL, NULL);
break;
case WM_COMMAND:
DestroyWindow(hwnd);
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
}
return (DefWindowProcW(hwnd, msg, wParam, lParam));
}
void RegisterDialogClass(HWND hwnd)
{
WNDCLASSEXW wc = {0};
wc.cbSize = sizeof(WNDCLASSEXW);
wc.lpfnWndProc = (WNDPROC) DialogProc;
wc.hInstance = ghInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpszClassName = L"DialogClass";
RegisterClassExW(&wc);
}
void CreateDialogBox(HWND hwnd)
{
CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT, // WS_EX_TOOLWINDOW to hide window from ALT+TAB
L"DialogClass", L"Dialog Box",
WS_VISIBLE | WS_POPUP | WS_SYSMENU,
100, 100, 400, 150,
hTab, NULL, ghInstance, NULL
);
}
void AddTabControl(HWND hwnd)
{
hTab = CreateWindowW(WC_TABCONTROLW, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_TABSTOP,
0, 30, 250, 250,
hwnd,
(HMENU) 9,
NULL,
NULL);
InsertTabItem(hTab, 10, L"A");
InsertTabItem(hTab, 11, L"B");
}
void InsertTabItem(HWND tabHwnd, UINT id, LPWSTR text)
{
TCITEMW tci = {0};
tci.mask = TCIF_TEXT;
tci.pszText = text;
tci.cchTextMax = lstrlenW(text);
if(SendMessage(tabHwnd, TCM_INSERTITEMW, id, (LPARAM) &tci) == -1) {
MessageBox(NULL,
L"couldn't create the new tab page",
L"tab errror",
MB_OK | MB_ICONERROR);
}
}
When you created the dialog box control, you lacked the WS_CHILD style and added the WS_POPUP style, which caused the dialog box you generated to float.
You only need to modify it to the WS_CHILD style, and appropriately modify the size of the control to achieve your desired effect.
void CreateDialogBox(HWND hwnd)
{
CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT, // WS_EX_TOOLWINDOW to hide window from ALT+TAB
L"DialogClass", L"Dialog Box",
WS_VISIBLE | WS_SYSMENU | WS_CHILD ,
10, 30, 350, 150,
hTab, NULL, ghInstance, NULL
);
}
void AddTabControl(HWND hwnd)
{
hTab = CreateWindowW(WC_TABCONTROLW, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_TABSTOP,
10, 30, 400, 250,
hwnd,
(HMENU)9,
NULL,
NULL);
InsertTabItem(hTab, 10, L"A");
InsertTabItem(hTab, 11, L"B");
}
It works like this:

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).

No LVN_GETINFOTIP notifications in a LVS_REPORT Listview of text cells

I have a listview of texts and have total control over it, works like a dream.
Except I don't get any LVN_GETINFOTIP notifications by WM_NOTIFY. I get every other notifications message but not LVN_GETINFOTIP.
I have spent some time been Googling and trying whatever I can find. Some sites say that from Vista it works different but not what different. I don’t care if it works before Win10 (can live with it) and not at all if it works before Win7.
I really wonder what prevents the LVN_GETINFOTIP messages?
switch (((LPNMHDR)lParam)->code)
{
case LVN_GETINFOTIP:
CreateListView(
0,
LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP | LVS_EX_LABELTIP,
0,
3 * TAB_HIGHT,
OwnWindowWidth,
OwnWindowHight - (OBJ_WINDOW_HIGHT + (3 * TAB_HIGHT)),
hWnd,
(HMENU)IDC_MAIN_LISTVIEW,
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE));
static HWND CreateListView(
DWORD dwAddStyle,
DWORD dwAddExtendedListViewStyle,
int X,
int Y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU IdListWiew,
HINSTANCE hInst)
{
HWND hWndListView;
INITCOMMONCONTROLSEX icex;
memset(&icex, 0, sizeof(INITCOMMONCONTROLSEX));
// Ensure that the common control DLL is loaded.
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&icex);
// Create the list-view window in report view with label
// editing enabled.
hWndListView = CreateWindowEx(WS_EX_ACCEPTFILES, WC_LISTVIEW, L"",
WS_CHILD | LVS_REPORT | WS_VISIBLE | WS_BORDER | WM_NOTIFY | dwAddStyle,
X, Y, nWidth, nHeight,
hWndParent, IdListWiew, hInst, NULL);
if (hWndListView == NULL)
return NULL;
ListView_SetExtendedListViewStyle(hWndListView, dwAddExtendedListViewStyle);
return hWndListView;
}
Found the error myself!
The sample code for ListViews I once used hides the first column and so I can't get any TVN_GETINFOTIP because there is nothing to hover (Width of column 0 was 0). I managed to find this by reduction of code in a test program and read it real carefully. A bit embarrassed I asked. But this is coding life.
I have now a very compact sample code 160lines of a report ListView if it comes to use. (I rather this question to be removed).
#include <windows.h>
#include <CommCtrl.h>
#include <wchar.h>
#define IDC_MAIN_LISTVIEW 100
#define MAIN_LISTFIELDS 9
#define MAIN_LISTLINES 78
int iMainFieldsWidth[MAIN_LISTFIELDS] = { 50, 60, 78, 70, 100, 50, 50, 121, 112 };
WORD wMainFieldsAlign[MAIN_LISTFIELDS] = { LVCFMT_RIGHT,LVCFMT_LEFT, LVCFMT_LEFT,LVCFMT_LEFT,LVCFMT_LEFT, LVCFMT_RIGHT, LVCFMT_RIGHT, LVCFMT_LEFT, LVCFMT_LEFT };
WCHAR *pMainFieldsHeader[MAIN_LISTFIELDS] = { L"zero",L"one",L"Two",L"Three",L"Four",L"five",L"six",L"seven",L"eight" };
LRESULT WINAPI MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
CREATESTRUCT MainWindowCreateStruct;
WNDCLASS wc;
MSG msg;
HWND hWnd;
if (!hPrevInstance)
{
wc.lpszClassName = L"AppClass";
wc.lpfnWndProc = MainWndProc;
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, L"1");
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_ACTIVEBORDER + 1);
wc.lpszMenuName = L"AppMenu";
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
RegisterClass(&wc);
}
memset(&MainWindowCreateStruct, 0, sizeof(CREATESTRUCT));
hWnd = CreateWindowEx(WS_EX_ACCEPTFILES, L"AppClass",
L"LvTest",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
&MainWindowCreateStruct
);
ShowWindow(hWnd, nCmdShow);
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT WINAPI MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
// Create the list-view window in report view with label
// editing enabled.
HWND hLvWnd = CreateWindowEx(WS_EX_ACCEPTFILES, WC_LISTVIEW, L"",
WS_CHILD | LVS_REPORT | WS_VISIBLE | WS_BORDER,
0, 0, 1000, 500,
hWnd, (HMENU)IDC_MAIN_LISTVIEW, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL);
if (hLvWnd == NULL)
return 0;
ListView_SetExtendedListViewStyle(hLvWnd, LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP | LVS_EX_LABELTIP);
ListView_DeleteAllItems(hLvWnd);
{
LVCOLUMN lvc;
int iCol = 0;
memset(&lvc, 0, sizeof(LVCOLUMN));
while (ListView_DeleteColumn(hLvWnd, 0)); // Nonvisual left column
// Initialize the LVCOLUMN structure.
// The mask specifies that the format, width, text, and
// subitem members of the structure are valid.
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
// Add the columns.
for (iCol = 0; iCol < MAIN_LISTFIELDS; iCol++)
{
lvc.iSubItem = iCol;
lvc.pszText = pMainFieldsHeader[iCol];
lvc.cchTextMax = MAX_PATH;
lvc.iImage = 0;
lvc.iOrder = 0;
lvc.cx = iMainFieldsWidth[iCol]; // width of column in pixels
lvc.fmt = wMainFieldsAlign[iCol]; // alignment of column
if (ListView_InsertColumn(hLvWnd, lvc.iSubItem, &lvc) == -1)
return 0;
}
}
{
LVITEM lvI;
DWORD index = 0;
memset(&lvI, 0, sizeof(LVITEM));
// Some code to create the list-view control.
// Initialize LVITEM members that are common to all
// items.
lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE | LVIF_INDENT;
lvI.state = 0;
lvI.stateMask = 0;
// Initialize LVITEM members that are different for each item.
for (index = 0; index < MAIN_LISTLINES; index++)
{
lvI.iItem = index;
lvI.iImage = 0; // Icon
lvI.iIndent = 0;
lvI.iSubItem = 0;
// lvI.lParam = (LPARAM) &rgPetInfo[index];
lvI.pszText = LPSTR_TEXTCALLBACK; // sends an
// LVN_GETDISPINFO
// message.
if (ListView_InsertItem(hLvWnd, &lvI) == -1)
return 0;
}
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code)
{
case LVN_GETINFOTIP:
if (lParam)
{ // This is only working it looks, or actually the LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP | LVS_EX_LABELTIP of the CreateWindowEx isn't, would be nice to have
LPNMLVGETINFOTIP pGetInfoTip = (LPNMLVGETINFOTIP)lParam;
wcscpy(pGetInfoTip->pszText, L"This is an InfoTip Text\nand it takes newlines", pGetInfoTip->cchTextMax);
}
break;
case LVN_GETDISPINFO:
if (lParam)
{
NMLVDISPINFO *plvdi = ((NMLVDISPINFO *)lParam);
if ((plvdi->hdr.hwndFrom == GetDlgItem(hWnd, IDC_MAIN_LISTVIEW)) && (plvdi->item.iSubItem<MAIN_LISTFIELDS) && plvdi->item.cchTextMax)
wcsncpy(plvdi->item.pszText, pMainFieldsHeader[plvdi->item.iSubItem], plvdi->item.cchTextMax);
}
break;
default:
break;
}
break;
default:
return(DefWindowProc(hWnd, msg, wParam, lParam));
}
return 0;
}

DialogBox not rendering correctly?

I'm not sure what I'm doing wrong, but here's how it looks (there's no close button, no caption bar):
It looks as if it's not updating/ticking/repainted.
Here's my Resource.rc file:
#include "resource.h"
#include "windows.h"
#define IDC_STATIC -1
ID_ICON_MAIN ICON "Smile.ico"
ID_DIALOG_ABOUT DIALOG 0, 0, 240, 70
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_BORDER
CAPTION "About"
FONT 8, "Terminal"
{
DEFPUSHBUTTON "&OK", IDOK, 174, 18, 50, 14
PUSHBUTTON "&Cancel", IDCANCEL, 174, 36, 50, 14
GROUPBOX "About this program...", IDC_STATIC, 7, 7, 225, 52
CTEXT "An example program\r\nBy vexe", IDC_STATIC, 16, 18, 144, 33
}
Changing the styles, caption, font size, etc doesn't change anything. Note that I'm using Windows Classic Theme, changing to the standard Windows 7 doesn't help either so it's not a theme issue.
Here's my window code:
#include <Windows.h>
#include "resource.h"
INT_PTR WINAPI DialogWndProc(HWND Window, UINT Message,
WPARAM WParam, LPARAM LParam)
{
INT_PTR Result = TRUE;
switch (Message)
{
case WM_INITDIALOG:
{
// UpdateWindow(Window); // Doesn't really help. Taking out the case didn't do anything either
}
break;
case WM_COMMAND:
{
int Command = LOWORD(WParam);
switch(Command)
{
case IDOK:
{
EndDialog(Window, IDOK);
}
break;
case IDCANCEL:
{
EndDialog(Window, IDCANCEL);
}
break;
default:
{
Result = FALSE;
}
break;
}
}
break;
}
return(Result);
}
LRESULT WINAPI MainWndProc(HWND Window, UINT Message,
WPARAM WParam, LPARAM LParam)
{
LRESULT Result = 0;
switch (Message)
{
// (Causes GetMessage to return false)
case WM_CLOSE:
{
DestroyWindow(Window);
}
break;
case WM_DESTROY:
{
PostQuitMessage(0);
}
break;
case WM_COMMAND:
{
int Command = LOWORD(WParam);
switch (Command)
{
case ID_MENU_FILE_EXIT:
{
PostMessage(Window, WM_QUIT, 0, 0);
}
break;
case ID_MENU_HELP_ABOUT:
{
int Choice = DialogBox(0, MAKEINTRESOURCE(ID_DIALOG_ABOUT), Window, DialogWndProc);
switch (Choice)
{
case IDOK: MessageBox(Window, "Okay!", "OK", MB_OK | MB_ICONINFORMATION); break;
case IDCANCEL: MessageBox(Window, "Canceled!", "Cancel", MB_OK | MB_ICONINFORMATION); break;
}
}
break;
}
}
break;
case WM_CREATE:
{
HMENU Menu;
HMENU SubMenu;
// Create the main menu
Menu = CreateMenu();
// Create submenus
{
SubMenu = CreatePopupMenu();
AppendMenu(SubMenu, MF_STRING, ID_MENU_FILE_EXIT, "E&xit");
AppendMenu(Menu, MF_STRING | MF_POPUP, (UINT_PTR)SubMenu, "&File");
SubMenu = CreatePopupMenu();
AppendMenu(SubMenu, MF_STRING, ID_MENU_HELP_ABOUT, "&About");
AppendMenu(Menu, MF_STRING | MF_POPUP, (UINT_PTR)SubMenu, "&Help");
}
// Set the menu on the window
SetMenu(Window, Menu);
}
break;
default:
{
Result = DefWindowProc(Window, Message, WParam, LParam);
}
}
return(Result);
}
int CALLBACK WinMain(HINSTANCE Instance, HINSTANCE Previous, LPSTR CommandLine, int CmdShow)
{
// Create window class
WNDCLASSEX WindowClass = {0};
WindowClass.cbSize = sizeof(WNDCLASSEX);
WindowClass.style = CS_HREDRAW | CS_VREDRAW;
WindowClass.lpfnWndProc = (WNDPROC)MainWndProc;
WindowClass.hInstance = Instance;
WindowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
WindowClass.lpszMenuName = MAKEINTRESOURCE(ID_MENU_MAIN);
WindowClass.lpszClassName = "SaedoGames_0";
WindowClass.hIcon = LoadIcon(Instance, MAKEINTRESOURCE(ID_ICON_MAIN));
WindowClass.hIconSm = (HICON)LoadImage(0, "Smile.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
RegisterClassEx(&WindowClass);
// Create window
HWND Window = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "SaedoGames_0", "Test Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
640, 480,
0, 0,
Instance, 0
);
// Show window
ShowWindow(Window, CmdShow);
UpdateWindow(Window);
// Message loop
MSG Msg;
while (GetMessage(&Msg, Window, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return(0);
}
Pretty sure I'm missing a simple function call but just not sure what it is. What am I missing?
Your dialog procedure unconditionally returns TRUE to every message, which means "I handled the message, you don't have to do anything else". Your dialog procedure does so by setting Result to TRUE at the top of the function and then never changing it (unless you get a WM_COMMAND from an unknown control). This is wrong; you need to return FALSE if you don't handle a message yourself.
WM_INITDIALOG is one of a handful of messages that return their values directly to the dialog system instead of through DWLP_MSGRESULT. In that case and that case only, you do need to return TRUE (unless you manually adjusted tab stops).

C winapi tab on dialog based tabcontrol

I working on dialog with TabControl based on child dialog.
Tab key work well on parent but never focus controls inside the child dialogs.
I create a simple sample to replicate the problem.
I work on Window 7, i use MinGW as compiler and ResEdit/Notepad++ for resource edition. Can someone help me ?
C Source file "tabdlg.c" :
#include <windows.h>
#include <commctrl.h>
#include "resource.h"
HANDLE hInstance;
HWND hDlgMain;
HWND hDlgPage1;
HWND hDlgPage2;
HWND hTabCtrlMain;
BOOL CALLBACK DlgPage1Fn(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
EndDialog(hWnd, 0);
return TRUE;
case WM_INITDIALOG:
return TRUE;
}
return FALSE;
}
BOOL CALLBACK DlgPage2Fn(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
EndDialog(hWnd, 0);
return TRUE;
case WM_INITDIALOG:
return TRUE;
}
return FALSE;
}
BOOL CALLBACK DlgMainFn(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg){
case WM_CLOSE:
EndDialog(hWnd, 0);
return TRUE;
case WM_INITDIALOG:
{
hInstance=GetModuleHandle(NULL);
hDlgMain=hWnd;
TCITEM tcBtn;
hTabCtrlMain=GetDlgItem(hDlgMain,IDT_TAB_CTRL_MAIN);
memset(&tcBtn,0x0,sizeof(TCITEM));
tcBtn.mask = TCIF_TEXT;
tcBtn.pszText = "Page 1";
SendMessage(hTabCtrlMain, TCM_INSERTITEM, 0, (LPARAM)&tcBtn);
hDlgPage1=CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DLG_PAGE1), hTabCtrlMain, DlgPage1Fn);
tcBtn.pszText = "Page 2";
SendMessage(hTabCtrlMain, TCM_INSERTITEM, 1, (LPARAM)&tcBtn);
hDlgPage2=CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DLG_PAGE2), hTabCtrlMain, DlgPage2Fn);
ShowWindow(hDlgPage2, SW_HIDE);
}
return TRUE;
case WM_NOTIFY:
{
INT nSel;
UINT uNotify=((LPNMHDR)lParam)->code;
switch(uNotify){
case TCN_SELCHANGE:
nSel=SendMessage(hTabCtrlMain, TCM_GETCURSEL, 0, 0);
if (nSel==0){
ShowWindow(hDlgPage1, SW_SHOW);
ShowWindow(hDlgPage2, SW_HIDE);
}else{
ShowWindow(hDlgPage1, SW_HIDE);
ShowWindow(hDlgPage2, SW_SHOW);
}
break;
}
}
return TRUE;
}
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DLG_MAIN), NULL, &DlgMainFn);
return 0;
}
Resource include file "resource.h" :
#ifndef IDC_STATIC
#define IDC_STATIC (-1)
#endif
#define IDD_DLG_MAIN 103
#define IDD_DLG_PAGE1 105
#define IDD_DLG_PAGE2 107
#define IDB_BTN_PAGE1 40000
#define IDB_BTN_PAGE2 40000
#define IDE_EDIT_MAIN 40000
#define IDB_BTN_MAIN_B 40001
#define IDE_EDIT_PAGE2 40001
#define IDB_BTN_MAIN_A 40003
#define IDT_TAB_CTRL_MAIN 40004
Resource file "tabdlg.rc" :
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DLG_MAIN DIALOG 0, 0, 327, 207
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
CAPTION "Main Dialog"
FONT 8, "Ms Shell Dlg"
{
PUSHBUTTON "Main Button A", IDB_BTN_MAIN_A, 7, 7, 140, 14, 0, WS_EX_LEFT
PUSHBUTTON "Main Button B", IDB_BTN_MAIN_B, 175, 7, 145, 14, 0, WS_EX_LEFT
CONTROL "", IDT_TAB_CTRL_MAIN, WC_TABCONTROL, WS_TABSTOP, 7, 30, 313, 142, WS_EX_LEFT
EDITTEXT IDE_EDIT_MAIN, 7, 177, 313, 14, ES_AUTOHSCROLL, WS_EX_LEFT
}
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DLG_PAGE1 DIALOG 0, 20, 186, 95
STYLE DS_3DLOOK | DS_SHELLFONT | WS_VISIBLE | WS_CHILDWINDOW
FONT 8, "Ms Shell Dlg"
{
PUSHBUTTON "Page1 Button", IDB_BTN_PAGE1, 67, 37, 48, 14, 0, WS_EX_LEFT
}
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DLG_PAGE2 DIALOG 0, 20, 186, 95
STYLE DS_3DLOOK | DS_SHELLFONT | WS_VISIBLE | WS_CHILDWINDOW
FONT 8, "Ms Shell Dlg"
{
DEFPUSHBUTTON "Page2 Button", IDB_BTN_PAGE2, 67, 32, 48, 14, 0, WS_EX_LEFT
EDITTEXT IDE_EDIT_PAGE2, 72, 57, 40, 14, ES_AUTOHSCROLL, WS_EX_LEFT
}
Simple build script "make.bat" :
gcc -c tabdlg.c -o tabdlg.o
windres tabdlg.rc -O coff -o tabdlg.res
gcc tabdlg.o tabdlg.res -o tabdlg.exe -mwindows -lcomctl32
I try different things without success (like : DS_CONTROL or WS_EX_CONTROLPARENT), i think i missed something.
I see two problems here.
In resources, add DS_CONTROL bit to IDD_DLG_PAGE1 and IDD_DLG_PAGE2.
In code, in your WM_INITDIALOG handler, that CreateDialog calls say your child dialogs are created children of hTabCtrlMain. Make them children of hWnd instead.

Resources