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.
Related
This question already has answers here:
Normal looking button with c++ / win32
(4 answers)
Programming In C + Win API: How To Get Windows 7 Look For Controls?
(3 answers)
Closed 2 years ago.
I'm working on a program that disables Windows Explorer. I did it before in Python, but when I build it in C it is faster to start the program.
I want the buttons to look elegant, like the title text style and font. How do I make the text of the buttons look nice? I hope you can help me.
My code:
#include <windows.h>
#define ID_BTNHI0 0
#define ID_BTNHI1 1
#define ID_BTNHI2 2
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
WNDCLASSEX class_;
int WINAPI WinMain(HINSTANCE hInstanciaAct, HINSTANCE hInstanciaPrev, LPSTR IpCmdLine, int iCmdShow){
ShowWindow(GetConsoleWindow(), SW_HIDE);
HWND hWnd;
MSG msg;
class_.cbSize = sizeof(WNDCLASSEX);
class_.cbWndExtra = 0;
class_.cbClsExtra = 0;
class_.style = CS_HREDRAW|CS_VREDRAW;
class_.lpfnWndProc = WndProc;
class_.hInstance = hInstanciaAct;
class_.hIcon = LoadImage(NULL, "icoff.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
class_.hIconSm = LoadImage(NULL, "icoff.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
class_.hCursor = LoadCursor(NULL, IDC_ARROW);
class_.lpszClassName = "MICLASE";
class_.lpszMenuName = NULL;
class_.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
if(!RegisterClassEx(&class_)){
MessageBox(NULL, "NON", "ERROR", MB_ICONERROR);
return EXIT_FAILURE;
}
hWnd = CreateWindowEx(0, "MICLASE", "Windows Explorer Toggle", WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT, 340, 140, HWND_DESKTOP, NULL, hInstanciaAct, NULL);
if(hWnd == NULL){
MessageBox(NULL, "NON2", "ERROR", MB_ICONERROR);
return EXIT_FAILURE;
}
ShowWindow(hWnd, iCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0) > 0){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
HWND hBtn0;
HWND hBtn1;
HWND hBtn2;
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch(msg){
case WM_CREATE:{
hBtn0 = CreateWindow("BUTTON", "Disable Windows Explorer", WS_VISIBLE | WS_CHILD | BS_FLAT, 82.5, 24.5, 175, 22.5, hWnd, (HMENU)ID_BTNHI0, GetModuleHandle(NULL), NULL);
//SetFocus(hBtn0);
hBtn1 = CreateWindow("BUTTON", "Enable Windows Explorer", WS_VISIBLE | WS_CHILD | BS_FLAT, 82.5, 24.5, 175, 22.5, hWnd, (HMENU)ID_BTNHI1, GetModuleHandle(NULL), NULL);
ShowWindow(hBtn1, SW_HIDE);
hBtn2 = CreateWindow("BUTTON", "Open Folder", WS_VISIBLE | WS_CHILD | BS_FLAT, 82.5, 48, 175, 22.5, hWnd, (HMENU)ID_BTNHI2, GetModuleHandle(NULL), NULL);
break;
}
case WM_COMMAND:{
switch(LOWORD(wParam)){
case ID_BTNHI0:{
system("TASKKILL /F /IM explorer.exe");
//clase.hIcon = LoadImage(NULL, "icoff.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
//clase.hIconSm = LoadImage(NULL, "icoff.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
ShowWindow(hBtn0, SW_HIDE);
ShowWindow(hBtn1, SW_SHOW);
//SetFocus(hBtn1);
break;
}
case ID_BTNHI1:{
system("start %SystemRoot%\\explorer.exe");
//clase.hIcon = LoadImage(NULL, "icon.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
//clase.hIconSm = LoadImage(NULL, "icon.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
ShowWindow(hBtn1, SW_HIDE);
ShowWindow(hBtn0, SW_SHOW);
//SetFocus(hBtn0);
break;
}
case ID_BTNHI2:{
system("start .");
//SetFocus(hBtn0);
break;
}
}
break;
}
case WM_DESTROY:{
PostQuitMessage(0);
break;
}
default:{
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
return 0;
}
You need to get rid of the BS_FLAT window style on the buttons, and also enable ComCtrl32 v6.0 Visual Styles for your program, if you haven't already. Then the buttons will have a modern look.
I'd like to set a specific window as the parent of a button, but I'm getting an expected behavior.
When the hMenu value on CreateWindow() is set, the button don't show up:
CreateWindow(L"Button",
L"Click me!",
WS_TABSTOP,
20,
50,
60,
90,
NULL,
(HMENU)1,
NULL, NULL);
But when I remove the hMenu parameter, the button does show up, but in a odd style:
CreateWindow(L"Button",
L"Click me!",
WS_TABSTOP,
20,
50,
60,
90,
NULL,
NULL,
NULL, NULL);
I'm not passing the HWND returned by the CreateWindow() of the main window into hwndParent of the button because they aren't called in that order.
The below code is a reduced version from my real one.
What am I missing?
Full code:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR pCmdLine, int nCmdShow) {
MSG msg = {0};
WNDCLASSW wc = {0};
wc.lpszClassName = L"Window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
HWND
hwndMainWindow = CreateWindow(wc.lpszClassName, L"Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 350,
NULL, NULL, hInstance, NULL);
HWND hButton = CreateWindow(L"Button",
L"Click me!",
WS_TABSTOP,
20,
50,
60,
90,
NULL,
NULL,
NULL, NULL);
SetParent(hButton, hwndMainWindow);
ShowWindow(hButton, SW_SHOWNORMAL);
UpdateWindow(hwndMainWindow);
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)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
You are not specifying the WS_CHILD style on the button. SetParent() does not set this style for you.
When you set the "parent" of a window that does not have WS_CHILD, you are actually setting its "owner" instead. Owner and Parent are not the same thing. Also see A window can have a parent or an owner but not both.
Your button does not appear in the main window because the button is not a child of the main window.
So, when creating the button, you need to include the WS_CHILD style. You should also include the WS_VISIBLE style instead of using ShowWindow() (unless you want to create the button hidden initially). And you should specify the parent window at the time of creation, instead of using SetParent().
You should also move your button creation into the WM_CREATE handler of your main window's WndProc().
Try this:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR pCmdLine, int nCmdShow) {
MSG msg = {0};
WNDCLASS wc = {0};
wc.lpszClassName = L"Window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&wc);
HWNDhwndMainWindow = CreateWindow(wc.lpszClassName, L"Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 350,
NULL, NULL, hInstance, NULL);
ShowWindow(hwndMainWindow, SW_SHOWNORMAL);
UpdateWindow(hwndMainWindow);
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)
{
switch(msg)
{
case WM_CREATE:
CreateWindow(L"Button",
L"Click me!",
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
20,
50,
60,
90,
hwnd,
(HMENU)1,
NULL, NULL);
break;
case WM_COMMAND:
if ((LOWORD(wParam) == 1) && (HIWORD(wParam) == BN_CLICKED)) {
MessageBox(hwnd, L"Yea, you clicked me!", L"Window", MB_OK);
return 0;
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
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:
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.
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).