DialogEx with Controls: Resizing? - c

The aim is to resize the Window of the DialogEx that best fit the configured Systemmetrics screenheight and screendepth on the object machine:
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
return DialogBoxW(hInstance, MAKEINTRESOURCEW(IDD_MAIN), nullptr, DlgProc);
}
IDD_MAIN is setup as the default in 768p. Let's call it IDD_760Pinstead and use its resource file config as the base to work on.
IDD_768P DIALOGEX 0, 0, 701, 191
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "MahProject"
FONT 8, "MS Sans Serif", 0, 0, 0x0
BEGIN
LTEXT "Add",IDC_STATIC,506,10,14,8
EDITTEXT IDC_TEXT,528,7,120,14,ES_AUTOHSCROLL
EDITTEXT IDC_NUMBER,647,7,21,14,ES_NUMBER
LTEXT "times.",IDC_STATIC,671,10,23,8
LISTBOX IDC_LIST,7,22,641,148,LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP //principal, main, or chief control on form
PUSHBUTTON "&Add",IDC_ADD,650,30,46,14
PUSHBUTTON "&Up",IDC_UP,650,47,47,14
PUSHBUTTON "&Down",IDC_DOWN,650,63,47,14
PUSHBUTTON "&Sideways",IDC_CREATE,650,80,47,14
PUSHBUTTON "&UpsideDown",IDC_REMOVE,650,97,47,14
PUSHBUTTON "&Less",IDC_CLEAR,650,114,47,14
PUSHBUTTON "&More",IDC_LOGON,650,131,47,14
PUSHBUTTON "&NoMore",IDC_NOLOGON,650,148,47,14
LTEXT "Great",IDC_STATIC_ONE,530,180,70,8
CTEXT "-",IDC_SHOWCOUNT,600,180,25,8
LTEXT "Fantastic",IDC_STATIC_TWO,625,180,30,8
END
These controls can be separately recalibrated with CreateWindow according to this post but that's a lot of code. Is there a way of subclassing the form to use extra resource file presets based on the above for IDD_1080P, IDD_2160P, IDD_4320P& beyond?
Where in the code would we place the GetSystemMetrics(SM_CXSCREEN) and GetSystemMetrics(SM_CYSCREEN) functions?

You can resize the dialog in WM_INITDIALOG
GetSystemMetrics(SM_CXSCREEN)/GetSystemMetrics(SM_CYSCREEN) gives the full screen width/height. This would try to overlap the toolbar. You probably don't want that.
SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcDesktop, NULL); will get the rectangle for desktop, or you can just show maximized window
BOOL CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
if (msg == WM_INITDIALOG)
{
ShowWindow(hwnd, SW_MAXIMIZE);
return 0;
}
...
return FALSE;
}
You should change the dialog style to the following:
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MAXIMIZEBOX | WS_POPUP | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
Note, you will have to move/resize all the child controls. When moving child controls, not that you have to its position in screen coordinates, then convert to client coordinate's of parent window.
Example
#include <Windows.h>
#include "resource.h"
#define rcwidth(rc) (rc.right - rc.left)
#define rcheight(rc) (rc.bottom - rc.top)
void move_resize(HWND child, int dx, int dy, int dw, int dh)
{
if (!child) return;
if (!IsWindow(child)) return;
if (!GetParent(child)) return;
//find child window's coordinates relative to top-left of parent:
RECT rc;
GetWindowRect(child, &rc);
//rc is now child control's rectangle in screen coordinates
POINT pt = { 0 };
ScreenToClient(GetParent(child), &pt);
OffsetRect(&rc, pt.x, pt.y);
//rc is now child control's rectangle relative to parent window
//prevent negative size
if ((rcwidth(rc) + dw) < 0) dw = -rcwidth(rc);
if ((rcheight(rc) + dh) < 0) dh = -rcheight(rc);
MoveWindow(child, rc.left + dx, rc.top + dy, rcwidth(rc) + dw, rcheight(rc), TRUE);
}
BOOL CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static RECT save_rect;
switch (msg)
{
case WM_INITDIALOG:
GetClientRect(hwnd, &save_rect);
ShowWindow(hwnd, SW_MAXIMIZE);
break;
case WM_SIZE:
if (lParam)
{
int cx = LOWORD(lParam);
int cy = HIWORD(lParam);
int dx = cx - save_rect.right;
int dy = cy - save_rect.bottom;
//change x/y position of OK/Cancel button
move_resize(GetDlgItem(hwnd, IDCANCEL), dx, dy, 0, 0);
move_resize(GetDlgItem(hwnd, IDOK), dx, dy, 0, 0);
//change width/height of listbox:
move_resize(GetDlgItem(hwnd, IDC_LIST1), 0, 0, dx, dy);
GetClientRect(hwnd, &save_rect);
}
break;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK)
EndDialog(hwnd, wParam);
if (LOWORD(wParam) == IDCANCEL)
EndDialog(hwnd, wParam);
break;
}
return FALSE;
}
int WINAPI wWinMain(HINSTANCE hinst, HINSTANCE, LPTSTR, int)
{
DialogBox(hinst, MAKEINTRESOURCE(IDD_DIALOG1), 0, DlgProc);
return 0;
}

In the "old" days there were avid discussions about the use of static data in resource files as opposed to the then "limited" processing power. Doesn't appear to be such an issue these days, however in this case the processing for huge DPIs might be an issue.
I believe Barmak's answer is the way to go, but added this as an expedient, but cheaper and less precise alternative.
Unfortunately we cannot use the preprocessor for the different presets, so the idea is to enumerate lpTemplate values for LPCDLGTEMPLATE just after the entry point:
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
Create_Temporary_hwnd_here
...
lpTemplate = DoSystemParametersInfoStuff(hwnd);
...
Destroy__Temporary_hwnd
...
return DialogBoxW(hInstance, MAKEINTRESOURCEW(lpTemplate), nullptr, DlgProc);
}
DoSystemParametersInfostuff returns the appropriate strings IDD_1080P, IDD_2160P etc. Provided the template structure and associated data for each template in the resource file is not changed there shouldn't be a problem.
Edit1: Checking for primary/secondary display might be a little awkward if calling the function MonitorFromWindow before we get a handle for our DlgProc window using the alternative method to Barmak's. Well it appears that it is not possible to ever return to the wWinMain routine once having left it, so the next option is to create another ephemeral "cheaper" hwnd from wWinMain to get the appropriate templates.
Edit 2: The form templates for the different resolutions are all set up in a spreadsheet downloadable here thanks to AOO.

Related

Strange coordinates in `WM_MOUSEMOVE`

MSDN describes that lParam of WM_MOUSEMOVE is 2 shorts due to it needing to be compatible with virtual coordinates because it acts as a redirected event if capture is set, which is clear. However, negative coordinates are still received under normal circumstances when moving the mouse slightly outside of the window, to be exact a bonus 5 pixels in all directions except up (which is where the caption is, and it'd go to WM_NCMOUSEMOVE instead).
I initially suspected this was to do with the drop shadow technically being part of the window (like with the output of AdjustWindowRectEx's rectangle including it) since you can receive events in some of the shadow, but the values of that don't match up, and clamping the values to the client area's size doesn't feel intended. Where do the bonus 5 pixels (on my system, Windows 10 Education 2004) come from, especially considering that shouldn't even be a part of the client area to my knowledge, and is there a clean/intended way to dodge unwanted values?
I've seen some discussion about the area you can grab to resize the window potentially being related, but my window isn't resizable (doesn't have a thick frame).
Edit: After tinkering, it seems this is related to the window style. Here's a reproducible sample (hopefully on other machines, too):
#include <Windows.h>
#include <stdio.h>
// Remove `^ WS_THICKFRAME` and the bug vanishes!
#define WINDOW_STYLE ((WS_OVERLAPPEDWINDOW | WS_VISIBLE) ^ WS_THICKFRAME)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_MOUSEMOVE) {
POINTS mouse = MAKEPOINTS(lParam);
printf("WM_MOUSEMOVE # x=%hd, y=%hd\n", mouse.x, mouse.y);
} else if (message == WM_DESTROY) {
PostQuitMessage(0);
}
return DefWindowProcW(hWnd, message, wParam, lParam);
}
int main(void) {
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = GetModuleHandle(NULL);
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"SO_Example";
wcex.hIconSm = NULL;
RECT windowRect = { 0, 0, 600, 600 };
AdjustWindowRectEx(&windowRect, WINDOW_STYLE, FALSE, 0);
ATOM windowClass = RegisterClassExW(&wcex);
HWND hWnd = CreateWindowExW(
0,
(LPCWSTR)windowClass,
L"hello stackoverflow!",
WINDOW_STYLE,
CW_USEDEFAULT, CW_USEDEFAULT,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
MSG msg;
while (GetMessageW(&msg, NULL, 0, 0) != 0) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
Here's that sample in action: https://i.imgur.com/LfCe9od.mp4
It appears 5 is the border size (1) and the border padding (4) added up (aka the area the user can use to resize the window), so it's safe to discard values outside of the client rectangle, even if it's a little strange that Win32 is reporting events in that area.

Magnification API: flickering during repaint

I created an application by slightly modifying the Magnification API sample provided by Microsoft to implement a custom transformation of each frame captured and displayed in the Magnifier window.
I used the MagSetImageScalingCallback to set the my function as a callback. The callback is invoked without problem and the source and destination image can be easily manipulated because the raw bits are passed to the callback as pointers (srcdata and destdata).
The window is refreshed with a timer which is set to 16 ms (~60Hz). The refresh is invoked by using the InvalidateRect API. The problem is that when the magnifier window suffer from flickering. This happens especially when the start menu appears, if "Peek" is enabled or every time there is a window on foreground which has dynamic content.
I tried to intercept the WM_ERASEBKGND and invoke the InvalidateRect with FALSE as third parameter but this didn't help. I tried to add an UpdateWindow invocation before the invalidate but nothing changed.
The Magnifier application provided with Windows 10 doesn't have the same problem. I'm wondering why this is happening and how can I get rid of flickering.
In order to reproduce che problem download the Magnification API sample from the link above then replace the content in the file MagnifierSample.cpp with this source code:
// Ensure that the following definition is in effect before winuser.h is included.
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#include <windows.h>
#include <wincodec.h>
#include <magnification.h>
// For simplicity, the sample uses a constant magnification factor.
#define RESTOREDWINDOWSTYLES WS_POPUP
// Global variables and strings.
HINSTANCE hInst;
const TCHAR WindowClassName[]= TEXT("MagnifierWindow");
const TCHAR WindowTitle[]= TEXT("Screen Magnifier Sample");
const UINT timerInterval = 16; // close to the refresh rate #60hz
HWND hwndMag;
HWND hwndHost;
RECT magWindowRect;
RECT hostWindowRect;
// Forward declarations.
ATOM RegisterHostWindowClass(HINSTANCE hInstance);
BOOL SetupMagnifier(HINSTANCE hinst);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CALLBACK UpdateMagWindow(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
BOOL isFullScreen = FALSE;
//
// FUNCTION: WinMain()
//
// PURPOSE: Entry point for the application.
//
int APIENTRY WinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE /*hPrevInstance*/,
_In_ LPSTR /*lpCmdLine*/,
_In_ int nCmdShow)
{
if (FALSE == MagInitialize())
{
return 0;
}
if (FALSE == SetupMagnifier(hInstance))
{
return 0;
}
ShowWindow(hwndHost, nCmdShow);
UpdateWindow(hwndHost);
// Create a timer to update the control.
UINT_PTR timerId = SetTimer(hwndHost, 0, timerInterval, UpdateMagWindow);
// Main message loop.
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Shut down.
KillTimer(NULL, timerId);
MagUninitialize();
return (int) msg.wParam;
}
//
// FUNCTION: HostWndProc()
//
// PURPOSE: Window procedure for the window that hosts the magnifier control.
//
LRESULT CALLBACK HostWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_ERASEBKGND:
return TRUE;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
//
// FUNCTION: RegisterHostWindowClass()
//
// PURPOSE: Registers the window class for the window that contains the magnification control.
//
ATOM RegisterHostWindowClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex = {};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = HostWndProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(1 + COLOR_BTNFACE);
wcex.lpszClassName = WindowClassName;
return RegisterClassEx(&wcex);
}
static BOOL CALLBACK myCallBack(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader, void *destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty) {
UINT row;
UINT column;
BYTE *matrix;
memset(destdata, 0, destheader.cbSize);
matrix = (BYTE *) destdata;
//Set alpha bits for the resulting image to 255 (fully opaque)
for (row = 0; row < destheader.height; row++) {
for (column = 3; column < destheader.width; column += 4) {
matrix[(row * destheader.width) + column] = 0xFF;
}
}
Sleep(20);
return TRUE;
}
//
// FUNCTION: SetupMagnifier
//
// PURPOSE: Creates the windows and initializes magnification.
//
BOOL SetupMagnifier(HINSTANCE hinst)
{
// Set bounds of host window according to screen size.
hostWindowRect.top = 0;
hostWindowRect.bottom = GetSystemMetrics(SM_CYSCREEN);
hostWindowRect.left = 0;
hostWindowRect.right = GetSystemMetrics(SM_CXSCREEN);
// Create the host window.
RegisterHostWindowClass(hinst);
hwndHost = CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED, WindowClassName, WindowTitle, RESTOREDWINDOWSTYLES, 0, 0, hostWindowRect.right, hostWindowRect.bottom, NULL, NULL, hInst, NULL);
if (!hwndHost)
{
return FALSE;
}
// Make the window opaque.
SetLayeredWindowAttributes(hwndHost, 0, 255, LWA_ALPHA);
SetWindowLong(hwndHost, GWL_EXSTYLE, GetWindowLong(hwndHost, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_NOACTIVATE);
// Create a magnifier control that fills the client area.
magWindowRect = hostWindowRect;
hwndMag = CreateWindow(WC_MAGNIFIER, TEXT("MagnifierWindow"), WS_CHILD | WS_VISIBLE, magWindowRect.left, magWindowRect.top, magWindowRect.right, magWindowRect.bottom, hwndHost, NULL, hInst, NULL );
if (!hwndMag)
{
return FALSE;
}
MagSetImageScalingCallback(hwndMag, &myCallBack);
// Set the source rectangle for the magnifier control.
MagSetWindowSource(hwndMag, magWindowRect);
return 1;
}
//
// FUNCTION: UpdateMagWindow()
//
// PURPOSE: Sets the source rectangle and updates the window. Called by a timer.
//
void CALLBACK UpdateMagWindow(HWND /*hwnd*/, UINT /*uMsg*/, UINT_PTR /*idEvent*/, DWORD /*dwTime*/)
{
// Reclaim topmost status, to prevent unmagnified menus from remaining in view.
SetWindowPos(hwndHost, HWND_TOPMOST, 0, 0, magWindowRect.right, magWindowRect.bottom, SWP_SHOWWINDOW | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
// Force redraw.
InvalidateRect(hwndMag, NULL, FALSE);
}
Notice that I added the code to set the callback
BOOL ret = MagSetImageScalingCallback(hwndMag, &myCallBack);
then I created this callback:
static BOOL CALLBACK myCallBack(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader, void *destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty)
The Sleep statement "simulate" the time taken by my custom transformation.
If you open a youtube video with Edge and then you run the MagnifierSample.exe executable you should see a black screen flickering sometime and when the screen flickers you should see the content behind the magnifier window (the black screen). This is exactly what is happening in my application. The sleep value is set to 20 but I don't known how long the callback is actually taking. I just guessed that it might take more than 16 ms.
To reproduce the problem perform the following steps:
Run the MagnifierSample.exe manually (don't use the debugger to run it)
You should see a black screen
Press the window key
Set a window with a video or a dynamic content in foreground
Click on the black window to let the taskbar disappear
You should see the screen flashing sometime
Is it possible to set a callback similar to the MagSetImageScalingCallback for a normal window handle ? I know I can use the WindowProc callback to intercept the messages sent to the window however I don't have access to the rawbits unless I use CreateDIBSection, SelectObject etc ...but at that time the image has been already sent to the destination window and I don't have the chance to transform it.

How to add tooltip to menu item

I'm looking for a solution to display a tooltip for a single menu item in my WinAPI program.
ID_MAIN_MENU MENU
BEGIN
...
END
MENUITEM "?", ID_RIGHT_BUTTON, HELP
END
I'm able to add tooltip for a control with TOOLINFO structure and TTM_ADDTOOL message, but this seems not apply to HMENU handle.
I found some C++ libraries that add tooltips to menu items, but I'm using C.
Moreover here it's not possible to use the WM_MENUSELECT message, which is sent only clicking on the menu, not hovering over.
An hypothetical image of what I want.
Any suggestion is appreciated.
Finally come to a solution good enough to make the tooltip appear on mouse over and disappear on mouse out (not perfect because has no initial nor autopop delay time).
#define _WIN32_IE 0x0300 // to reach TTM_TRACKACTIVATE in <commctrl.h>
#include <windows.h>
#include <commctrl.h>
HWND hToolTip;
TOOLINFO ti = {0};
// WndProc
case WM_CREATE: {
HMENU hmenu = CreateMenu();
AppendMenu( hmenu, MF_HELP, 123, "?" );
SetMenu( hWnd, hmenu );
hToolTip = CreateWindow( TOOLTIPS_CLASS, 0, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, hWnd, 0, 0, 0);
ti.cbSize = sizeof(TOOLINFO);
ti.hwnd = hWnd;
ti.uId = (UINT)hToolTip;
ti.lpszText = "My tooltip";
SendMessage( hToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti );
break;
}
case WM_NCMOUSEMOVE: {
RECT rect;
GetMenuItemRect( hWnd, GetMenu(hWnd), 0, &rect );
// ^ zero-based position of the menu item
int x = LOWORD(lParam);
int y = HIWORD(lParam);
if ( x>=rect.left && y>=rect.top && x<rect.right && y<rect.bottom )
SendMessage( hToolTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti );
else
SendMessage( hToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)&ti );
break;
}
case WM_MOUSEMOVE:
SendMessage( hToolTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)&ti );
break;
Why I did use TTM_TRACKACTIVATE instead of TTM_POPUP?
Oddly TTM_POPUP is not defined in commctrl.h by MinGW (gcc 5.3.0).
TTM_POPUP needs Visual Styles enabled with manifest.
Eventually for me TTM_POPUP is ineffective in this scenario.
I distilled a simpler and more efficient solution to create a tooltip for a single menu item:
#include <windows.h>
#include <commctrl.h>
#define ID_BUTTON 123
HWND hToolTip;
TOOLINFO ti = {0};
// WndProc
case WM_CREATE: {
// create menu
HMENU hmenu = CreateMenu();
AppendMenu( hmenu, MF_HELP, ID_BUTTON, "?" );
SetMenu( hWnd, hmenu );
InitCommonControls(); // necessary in Windows XP to register TOOLTIPS_CLASS
// create tooltip
hToolTip = CreateWindow( TOOLTIPS_CLASS, 0, WS_POPUP, 0, 0, 0, 0, hWnd, 0, 0, 0 );
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_SUBCLASS;
ti.hwnd = hWnd; // main window handle
ti.uId = (UINT)ID_BUTTON; // distinguish it from eventual other tooltips
ti.lpszText = "My tooltip";
SendMessage(hToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
break;
}
case WM_SIZE:
ti.uId = (UINT)ID_BUTTON;
// get new coordinates of item
GetMenuItemRect( hWnd, GetMenu(hWnd), 0, &ti.rect );
// ^ zero-based position of the menu item
// convert from screen to client coordinates
MapWindowPoints( NULL, hWnd, (LPPOINT)&ti.rect, 2 );
// ^ desktop handle ^ POINT structures that is one RECT
SendMessage(hToolTip, TTM_NEWTOOLRECT, 0, (LPARAM)&ti);
break;
I used InitCommonControls because is shorter and works, but is also obsolete and InitCommonControlsEx should be used instead.
Above code was tested also on Windows XP: it works, but GetLastError after CreateWindow returns a 1309 ERROR_NO_IMPERSONATION_TOKEN.
In order to solve it, create a manifest file and in resource (.rc) file add the line:
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "YourApp.exe.manifest"

Win32: Toolbar dialog seems to never get focus and causes the main window to process slow?

I'm writing an application following this tutorial. I'm aware that this tutorial dates, and as such, I have adapted the code to take into consideration the unicode.
I have a main window which looks like an MDI. Then, I have a View menu which toggles a Toolbar dialog as to be shown and hidden.
When I show the dialog, it is displayed, but the PUSHBUTTONs are not displayed correctly. They only appear when I click my main window again.
Plus, I don't seem to be able to click neither of the PUSHBUTTONs into my toolbar dialog.
The resources (resource.h) are defined as follows (only showing what is relevant to this question):
#define IDD_TOOLBAR 102
#define IDC_PRESS 1000
#define IDC_OTHER 1001
#define ID_VIEW_SHOWTOOLBAR 40002
#define ID_VIEW_HIDETOOLBAR 40003
And the dialog as follows in my .rc file:
IDD_TOOLBAR DIALOGEX 0, 0, 85, 50
STYLE DS_FIXEDSYS | DS_MODALFRAME | WS_CAPTION | WS_POPUP
EXSTYLE WS_EX_TOOLWINDOW
CAPTION L"Toolbar"
FONT 8, "MS Shell Dlg"
BEGIN
PUSHBUTTON L"&Press this button", IDC_PRESS, 7, 7, 70, 14
PUSHBUTTON L"&Or this one", IDC_OTHER, 7, 28, 70, 14
END
And showing it as follows in my WndProc function:
// As a global variable I have my toolbar handler.
HWND g_hToolbar = NULL;
BOOL CALLBACK ToolbarDlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case IDC_OTHER:
MessageBoxW(hWnd, L"You just clicked IDC_OTHER!", L"Information", MB_OK | MB_ICONINFORMATION);
break;
case IDC_PRESS:
MessageBoxW(hWnd, L"You just clicked ODC_PRESS!", L"Information", MB_OK | MB_ICONINFORMATION);
break;
default:
return FALSE;
}
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_VIEW_HIDETOOLBAR:
ShowWindow(g_hToolbar, SW_HIDE);
break;
case ID_VIEW_SHOWTOOLBAR:
if (NULL == g_hToolbar)
g_hToolbar = CreateDialogW(GetModuleHandle(NULL)
, MAKEINTRESOURCE(IDD_TOOLBAR)
, hWnd
, ToolbarDlgProc);
ShowWindow(g_hToolbar, SW_SHOW);
break;
}
break;
default:
return DefWindowProcW(hWnd, Msg, wParam, lParam);
}
}
And here's the way I handle the different messages for my main window and my dialog in my message loop in my WinMain function.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
// Declaring, registring and creating my main window to hWnd here...
MSG Msg;
ShowWindow(hWnd, nShowCmd);
UpdateWindow(hWnd);
while (GetMessageW(&Msg, hWnd, 0, 0) > 0) {
if (!IsDialogMessageW(g_hToolbar, &Msg)) {
TranslateMessage(&Msg);
DispatchMessageW(&Msg);
}
}
}
My problem is:
I don't seem to be able to click on my dialog's buttons.
When I attempt to click on my dialog's buttons, my main window becomes very slow to respond to its own messages.
That is, when I want to show my Toolbar dialog as a modeless dialog, because when I show it modal, it works perfectly!
Any clue to solve this issue?
Thanks!
The problem is, as DReJ said in the above comment, in my message pump.
The trouble is that I write:
while (GetMessageW(&Msg, hWnd, 0, 0) > 0) {
// Processing message here...
}
And that I shall write:
while (GetMessageW(&Msg, NULL, 0, 0) > 0) {
// Processing message here...
}
So, because I was getting the messages for a given window, the hWnd instance, my ToolbarDialog seemed to lack of time to draw itself completely or something like it. Replacing hWnd for NULL in that scenario solved the problem entirely.

Win32: How to create a ListBox control using the CreateWindowExW() function?

I've been through multiple sites, documents and tutorials and they all say the same, that is, any control is nothing more than a window in Win32's API, hence one is able to use the CreateWindowExW() function to create a ListBox control/window over the main application window.
Though I get the concepts of all controls being windows with different dwStyle, I have a hard time finding out how to instantiate, to say so, the ListBox control.
I encountered a tutorial where a dialog is written to have a LISTBOX specified in its declaration as follows:
// resource.h
#define IDD_MAIN 101
#define IDC_TEXT 1000
#define IDC_NUMBER 1001
#define IDC_LIST 1002
#define IDC_ADD 1003
#define IDC_CLEAR 1004
#define IDC_REMOVE 1005
#define IDC_SHOWCOUNT 1006
// .rc resource file
IDD_MAIN DIALOG DISCARDABLE 0, 0, 207, 156
STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Controls One"
FONT 8, "MS Sans Serif"
BEGIN
LTEXT "Add",IDC_STATIC,7,10,14,8
EDITTEXT IDC_TEXT,25,7,120,14,ES_AUTOHSCROLL
EDITTEXT IDC_NUMBER,150,7,21,14,ES_NUMBER
LTEXT "times.",IDC_STATIC,177,10,23,8
LISTBOX IDC_LIST,7,25,138,106,LBS_NOINTEGRALHEIGHT |
LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "&Add",IDC_ADD,150,30,50,14
PUSHBUTTON "&Remove",IDC_REMOVE,150,47,50,14
PUSHBUTTON "&Clear",IDC_CLEAR,150,63,50,14
LTEXT "This item was added",IDC_STATIC,7,141,66,8
CTEXT "-",IDC_SHOWCOUNT,77,141,32,8
LTEXT "times",IDC_STATIC,114,141,17,8
END
And using it in his C program like so:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
return DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DlgProc);
}
Now, this I am able to do and fully understand the concepts. Aside, I would like to be able to create and design my main application window to add a ListBox control to. This tutorial example doesn't use the CreateWindowExW() function to create the control, instead, it creates a dialog that will actually be the main application window.
1 - Any clue on how to add a ListBox control to the main window in code?
I thought about creating it while handling the WM_CREATE message.
2 - Is this a good idea?
3 - What is the best practice/approach in this scenario?
In order to dynamically create a control in Win32's you need the following code:
HWND hBtn, hLabel, hListbox, hTextBox;
void InitializeComponent(HWND hWnd) {
HINSTANCE hInstance = GetModuleHandle(NULL);
// Adding a Button.
hBtn = CreateWindowExW(WS_EX_APPWINDOW,
L"BUTTON", NULL,
WS_CHILD | WS_VISIBLE,
327, 7, 70, 21,
hWnd, NULL, hInstance, NULL);
SetWindowTextW(hBtn, L"&Button");
// Adding a Label.
hLabel = CreateWindowExW(WS_EX_CLIENTEDGE,
L"STATIC", NULL,
WS_CHILD | WS_VISIBLE,
7, 7, 50, 21,
hWnd, NULL, hInstance, NULL);
SetWindowTextW(hLabel, L"Label:");
// Adding a ListBox.
hListBox = CreateWindowExW(WS_EX_CLIENTEDGE,
L"LISTBOX", NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_AUTOVSCROLL,
7, 35, 300, 200,
hWnd, NULL, hInstance, NULL);
// Adding a TextBox.
hTextBox = CreateWindowExW(WS_EX_CLIENTEDGE,
L"EDIT", NULL,
WS_CHILD | WS_VISIBLE | ES_AUTOVSCROLL,
62, 7, 245, 21,
hWnd, NULL, hInstance, NULL);
SetWindowTextW(hTextBox, L"Input text here...");
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_CREATE:
InitializeComponent(hWnd);
break;
default:
return DefWindowProcW(hWnd, Msg, wParam, lParam);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
// Declaring, defining, registering and creating window here...
// Note that each Window/Control has to have its own Message handling function.
}
HWND hListBox; // Handle for list box control
hListBox = CreateWindowEx(
WS_EX_CLIENTEDGE, // extended window styles
"LISTBOX", // list box window class name
NULL,
WS_CHILD | WS_VISIBLE, // window styles
7, // horizontal position
35, // vertical position
300, // width
200, // height
hWnd,
NULL,
hInstance,
NULL
);
if (!hListBox){
// failed to create list box window - take actions ...
}

Resources