catching VK_UP in WM_KEYDOW message not working - winapi

this piece of code in the window procedure isn't working:
case WM_KEYDOWN:
switch(wParam)
{
case VK_UP:
MessageBox(NULL, L"key up", L"", MB_OK);
break;
default:
break;
}
I press the arrow up key but the MessageBox isn't being called. 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
#ifdef UNICODE
#define STRSPLIT wcsrchr
#else
#define STRSPLIT strrchr
#endif
#define __FILENAME__ (STRSPLIT(TEXT(__FILE__), '/') ? STRSPLIT(TEXT(__FILE__), '/') + 1 : TEXT(__FILE__))
#define NAMEOF(s) TEXT(#s)
#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))
#include <windows.h>
#include <strsafe.h>
#include <CommCtrl.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void setUpScrollBar(HWND hwnd);
void ErrorExit(LPWSTR lpszFunction, int line, LPWSTR filename);
DWORD ShowLastError(LPWSTR lpszFunction, int line, LPWSTR filename);
void InsertTabItem(HWND tabHwnd, UINT id, LPWSTR text);
void CreateTab(HWND hwnd);
HWND hTab;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.lpszClassName = L"main window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
HWND hWnd =
CreateWindowW(wc.lpszClassName, L"foo",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 330, 180, 0, 0, hInstance, 0);
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(hWnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
static int g_scrollY;
switch(msg)
{
case WM_CREATE:
CreateWindowW(L"Static", L"This is label 1...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_BORDER,
50, 10, 130, 25, hwnd, (HMENU) 18, NULL, NULL);
CreateWindowW(L"Static", L"This is label 2...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_BORDER,
50, 40, 130, 25, hwnd, (HMENU) 19, NULL, NULL);
CreateWindowW(L"Static", L"This is label 3...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_BORDER,
50, 70, 130, 25, hwnd, (HMENU) 19, NULL, NULL);
CreateWindowW(L"Static", L"This is label 4...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_BORDER,
50, 100, 130, 25, hwnd, (HMENU) 19, NULL, NULL);
CreateWindowW(L"Static", L"This is label 5...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_BORDER,
50, 130, 130, 25, hwnd, (HMENU) 19, NULL, NULL);
CreateWindowW(L"Static", L"This is label 6...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_BORDER,
50, 160, 130, 25, hwnd, (HMENU) 19, NULL, NULL);
CreateWindowW(L"Static", L"This is label 7...",
WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_BORDER,
50, 190, 130, 25, hwnd, (HMENU) 19, NULL, NULL);
setUpScrollBar(hwnd);
break;
case WM_VSCROLL:
int action = LOWORD(wParam);
//HWND hScroll = (HWND)lParam;
int pos = -1;
if (action == SB_THUMBPOSITION || action == SB_THUMBTRACK) {
pos = HIWORD(wParam);
} else if (action == SB_LINEDOWN) {
pos = g_scrollY + 30;
} else if (action == SB_LINEUP) {
pos = g_scrollY - 30;
}
if (pos == -1)
break;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS;
si.nPos = pos;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
GetScrollInfo(hwnd, SB_VERT, &si);
pos = si.nPos;
POINT pt;
pt.x = 0;
pt.y = pos - g_scrollY;
HDC hdc = GetDC(hwnd);
LPtoDP(hdc, &pt, 1);
ReleaseDC(hwnd, hdc);
ScrollWindow(hwnd, 0, -pt.y, NULL, NULL);
g_scrollY = pos;
return 0;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
switch(wParam)
{
case VK_UP:
MessageBox(NULL, L"key up", L"", MB_OK);
break;
default:
break;
}
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void setUpScrollBar(HWND hwnd)
{
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = 220;
si.nPage = (rc.bottom - rc.top);
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
}
void ErrorExit(LPWSTR lpszFunction, int line, LPWSTR filename)
{
DWORD dw = ShowLastError(lpszFunction, line, filename);
ExitProcess(dw);
}
DWORD ShowLastError(LPWSTR lpszFunction, int line, LPWSTR filename)
{
#define MAX_DIGITS 16
DWORD dw = GetLastError();
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
lpDisplayBuf = (LPVOID) LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) +
lstrlen((LPCTSTR)lpszFunction) + 40 +
(line > 0 ? MAX_DIGITS : 0) +
(filename != NULL ? lstrlen(filename) : 0)) *
sizeof(TCHAR)
);
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with %d: %s"),
lpszFunction, dw, lpMsgBuf
);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
return dw;
}

Your message pump is not correctly formed. You're doing something for modeless dialogs (a window created via CreateDialog), but you have no such window type in your app. Hence, some of your messages are getting swallowed by the IsDialogMessage function.
So instead of this:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(hWnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
This:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
When I changed your code to that, the message box would appear.

Related

Message after clicking on the edit control

I’m new to WinAPI development.
The main window contains three Edit controls.When clicking with the left mouse button on Edit1, the message "Edit1 choosed" is to be displayed. When you click on Edit2, it is supposed to display the message "Edit2 choosed". Similarly, when you click on Edit3, the message "Edit3 choosed" should be displayed. I tried using various combinations of WM_LBUTTONDOWN and WM_SETFOCUS, unfortunately the program does not work. Could someone please help me?
#include <windows.h>
#define Edit1 501
#define Edit2 502
#define Edit3 503
HWND TextBox1, TextBox2, TextBox3;
HFONT HF = CreateFont (30, 0, 00, 00, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial");
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
switch(Message)
{
case WM_SETFOCUS:
if(wParam == Edit1)
{
MessageBox(NULL, "Edit1 choosed","Edit1",MB_ICONEXCLAMATION|MB_OK);
}
break;
if(wParam == Edit2)
{
MessageBox(NULL, "Edit2 choosed","Edit2",MB_ICONEXCLAMATION|MB_OK);
}
break;
if(wParam == Edit3)
{
MessageBox(NULL, "Edit3 choosed","Edit3",MB_ICONEXCLAMATION|MB_OK);
}
break;
case WM_CREATE:
{
TextBox1 = CreateWindowEx( WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_RIGHT,
30, 70, 190, 40, hwnd, (HMENU)Edit1, GetModuleHandle(NULL), NULL);
SendMessage(TextBox1, WM_SETFONT,( WPARAM ) HF, 0 );
TextBox2 = CreateWindowEx( WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_RIGHT,
30, 130, 190, 40, hwnd, (HMENU)Edit2, GetModuleHandle(NULL), NULL);
SendMessage(TextBox2, WM_SETFONT,( WPARAM ) HF, 0 );
TextBox3 = CreateWindowEx( WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_RIGHT,
30, 190, 190, 40, hwnd, (HMENU)Edit3, GetModuleHandle(NULL), NULL);
SendMessage(TextBox3, WM_SETFONT,( WPARAM ) HF, 0 );
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG msg;
memset(&wc,0,sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = "WindowClass";
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc)) {
MessageBox(NULL, "Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,"WindowClass","Caption",WS_VISIBLE|WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL,NULL,hInstance,NULL);
if(hwnd == NULL) {
MessageBox(NULL, "Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
the "break" in the message loop under WM_SETFOCUS is always executed. So the program never reaches the point of the second edit control. Move the break to before the closing "}" and retry.
{
MessageBox(NULL, "Edit1 choosed","Edit1",MB_ICONEXCLAMATION|MB_OK);
break;
}
Furthermore, consider using 'else if'.

How do i detect the control that sent the WM_KEYDOWN?

WM_KEYDOWN message sends the message to the main window, I'd like to get control that originated the message. I've tried with GetFocus() handling WM_KEYDOWN like below but it didn't do anything, even when a press a key with focus on the buttona. How is that done? what am I missing?
Here's how I'm handling WM_KEYDOWN:
case WM_KEYDOWN:
HWND hFocus = GetFocus();
if(hFocus == buttonA)
{
buttona_onKeyDown();
}
else if(hFocus == buttonB)
{
buttonb_onKeyDown();
}
else
{
//MessageBox(NULL, L"Not found!", L"", MB_OK);
}
break;
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);
void buttona_onKeyDown();
void buttonb_onKeyDown();
HWND buttonA, buttonB;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.lpszClassName = L"Buttons";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
HWND hWnd =
CreateWindowW(wc.lpszClassName, L"Buttons",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
150, 150, 300, 200, 0, 0, hInstance, 0);
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(hWnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
switch(msg)
{
case WM_CREATE:
buttonA =
CreateWindowW(L"Button", L"A",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
20, 50, 80, 25, hwnd, (HMENU) 10, NULL, NULL);
buttonB =
CreateWindowW(L"Button", L"B",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
120, 50, 80, 25, hwnd, (HMENU) 11, NULL, NULL);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
HWND hFocus = GetFocus();
if(hFocus == buttonA)
{
buttona_onKeyDown();
}
else if(hFocus == buttonB)
{
buttonb_onKeyDown();
}
else
{
//MessageBox(NULL, L"Not found!", L"", MB_OK);
}
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void buttona_onKeyDown()
{
MessageBox(NULL, L"A OnKeyDown", L"", MB_OK);
}
void buttonb_onKeyDown()
{
MessageBox(NULL, L"B OnKeyDown", L"", MB_OK);
}

How do I select item based on typed letters on owner-draw combobox?

On a regular combobox, you can jump to items that starts with the typed letter. e.g., if you have items like "baa", "arch", "foo", "art" and type "a" the item "arch" gets selected, you type "a" again then it jumps to "art". How can I implement this on owner-draw combobox? I thought I could handle WM_CHAR like below in the comobobox's subprocedure, however I couldn't even test it, since setting a procedure to the comobobox like that fails:
HWND hwndEdit = GetWindow(hwndCombo, GW_CHILD);
assert(hwndEdit != NULL); // fails here
lpfnEditWndProc = (WNDPROC) SetWindowLongPtr(hwndEdit, GWLP_WNDPROC, (LONG_PTR) SubClassProc);
the procecdure was going to be like this:
LRESULT CALLBACK SubClassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CHAR:
{
static int prevIndex = 0;
static wchar_t buffer[2] = {0};
memset(&buffer, 0, sizeof(buffer));
buffer[0] = wParam;
prevIndex = SendMessage(hwndCombo, CB_FINDSTRING, (WPARAM) prevIndex, (LPARAM) buffer);
SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) prevIndex, 0);
return 0;
}
break;
}
return CallWindowProc(lpfnEditWndProc, hwnd, msg, wParam, lParam);
}
Here's the full code of owner-draw combobox:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#pragma comment(lib, "UxTheme.lib")
#pragma comment(lib, "Comdlg32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <Commctrl.h>
#include <assert.h>
#include <uxtheme.h>
#include <Commdlg.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void SetDefaultFont(HWND hwnd);
HFONT LoadSystemDefaultFont();
void DrawBorder(HDC hdc, RECT *rect);
void DrawLine(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2);
void freeBrushes();
HBRUSH getBrushAt(int index);
void drawColorRect(HDC dc, RECT *editRect, int colorIndex);
void EnableVisualStyles2(void);
LRESULT CALLBACK SubClassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HINSTANCE g_hinst;
HFONT hDefaultSystemFont;
HWND hwndCombo;
#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))
#define LIST \
X(L"Black", RGB(0, 0, 0)) \
X(L"Red", RGB(255, 0, 0)) \
X(L"Blue", RGB(0, 0, 255)) \
X(L"Green", RGB(0, 128, 0)) \
X(L"Yellow", RGB(255, 255, 0))
#define X(a, b) a,
const wchar_t *items[] = { LIST };
#undef X
#define X(a, b) b,
const int colorCodes[] = { LIST };
#undef X
HBRUSH brushes[COUNTOF(items)];
WNDPROC lpfnEditWndProc;
enum
{
BTN_A = 20,
BTN_COMBO,
};
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
HWND hwnd;
MSG msg;
WNDCLASSW wc = {0};
wc.lpszClassName = L"Application";
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0,IDC_ARROW);
g_hinst = hInstance;
RegisterClassW(&wc);
hwnd = CreateWindowW(wc.lpszClassName, L"Combo box",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 270, 170, 0, 0, hInstance, 0);
while (GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
DeleteObject(hDefaultSystemFont);
freeBrushes();
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
hwndCombo = CreateWindow(L"Combobox", NULL,
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
CBS_OWNERDRAWFIXED | CBS_AUTOHSCROLL | WS_VSCROLL | WS_HSCROLL,
10, 10, 100, 110, hwnd, (HMENU) BTN_COMBO, g_hinst, NULL);
SendMessage(hwndCombo, CB_SETMINVISIBLE, 5, 0);
SetDefaultFont(hwndCombo);
COMBOBOXINFO ci = {0};
ci.cbSize = sizeof(COMBOBOXINFO);
GetComboBoxInfo(hwndCombo, &ci);
lpfnEditWndProc = (WNDPROC)SetWindowLongPtr(ci.hwndList, GWLP_WNDPROC, (LONG_PTR)SubClassProc);
for (int i = 0; i < COUNTOF(items); ++i)
{
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]);
}
}
break;
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT b = (LPDRAWITEMSTRUCT) lParam;
SetBkMode(b->hDC, TRANSPARENT);
if(b->itemState & ODS_SELECTED)
{
FillRect(b->hDC, &b->rcItem, (HBRUSH) (COLOR_HIGHLIGHT+1));
SetTextColor(b->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT+1));
}
else
{
FillRect(b->hDC, &b->rcItem, (HBRUSH) (COLOR_WINDOW+1));
SetTextColor(b->hDC, GetSysColor(COLOR_WINDOWTEXT+1));
}
if(b->itemID != -1)
{
drawColorRect(b->hDC, &b->rcItem, b->itemID);
DrawText(b->hDC, items[b->itemID], -1, &b->rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
if(b->itemAction & ODA_FOCUS)
{
DrawFocusRect(b->hDC, &b->rcItem);
}
return (INT_PTR) TRUE;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
HFONT LoadSystemDefaultFont()
{
if(hDefaultSystemFont == NULL) {
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(NONCLIENTMETRICS);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0);
hDefaultSystemFont = CreateFontIndirect(&ncm.lfMessageFont);
}
return hDefaultSystemFont;
}
void SetDefaultFont(HWND hwnd)
{
SendMessage(hwnd, WM_SETFONT, (LPARAM) LoadSystemDefaultFont(), TRUE);
}
void drawColorRect(HDC dc, RECT *editRect, int colorIndex)
{
assert(colorIndex >= 0 && colorIndex < COUNTOF(brushes));
assert(editRect != NULL);
RECT rt = {0};
rt.left = editRect->left + 2;
rt.top = editRect->top - 2;
rt.right = editRect->right / 5;
rt.bottom = editRect->bottom - 2;
InflateRect(&rt, -1, -1);
DrawBorder(dc, &rt);
//FrameRect(dc, &rt, getBrushAt(0));
FillRect(dc, &rt, getBrushAt(colorIndex));
}
void DrawLine(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2)
{
MoveToEx(hdc, x1, y1, NULL);
LineTo(hdc, x2, y2);
}
void DrawBorder(HDC hdc, RECT *rect)
{
DrawLine(hdc, rect->left, rect->top, rect->left, rect->bottom);
DrawLine(hdc, rect->left, rect->top, rect->right, rect->top);
DrawLine(hdc, rect->right, rect->top, rect->right, rect->bottom);
DrawLine(hdc, rect->left, rect->bottom, rect->right, rect->bottom);
}
HBRUSH getBrushAt(int index)
{
assert(index >= 0 && index < COUNTOF(brushes));
HBRUSH b = brushes[index];
if(b == NULL) {
int code = colorCodes[index];
brushes[index] = CreateSolidBrush(code);
b = brushes[index];
}
return b;
}
void freeBrushes()
{
for(int i = 0; i < COUNTOF(brushes); ++i){
DeleteObject(brushes[i]);
brushes[i] = NULL;
}
}
void EnableVisualStyles2(void)
{
TCHAR dir[MAX_PATH] = {0};
GetSystemDirectory(dir, sizeof(dir) / sizeof(*dir));
ACTCTX actCtx = {0};
actCtx.cbSize = sizeof(ACTCTX);
actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID |
ACTCTX_FLAG_SET_PROCESS_DEFAULT |
ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;
actCtx.lpSource = L"shell32.dll";
actCtx.lpAssemblyDirectory = dir;
actCtx.lpResourceName = (LPCTSTR) 124;
ULONG_PTR cookie = FALSE;
HANDLE h = CreateActCtx(&actCtx);
assert(h != INVALID_HANDLE_VALUE);
ActivateActCtx(h, &cookie);
}
LRESULT CALLBACK SubClassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CHAR:
{
static int prevIndex = 0;
static wchar_t buffer[2] = {0};
buffer[0] = wParam;
MessageBox(NULL, buffer, L"", MB_OK);
prevIndex = SendMessage(hwndCombo, CB_FINDSTRING, (WPARAM) prevIndex, (LPARAM) buffer);
SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) prevIndex, 0);
return 0;
}
break;
}
return CallWindowProc(lpfnEditWndProc, hwnd, msg, wParam, lParam);
}
UPDDATE sorry, I past the code sample from other file than the owner-draw combobox. As pointed out by #Song Zhu - MSFT, set the procedure to properly but that still doesn't process WM_CHAR.
The drop-down COMBOBOX does not have an EDIT control. Instead, it has a drop-down list.
I recommend you to use GetComboBoxInfo to get the child window of the COMBOBOX control.
Try to modify the code as:
COMBOBOXINFO ci{};
ci.cbSize = sizeof(COMBOBOXINFO);
GetComboBoxInfo(hwndCombo, &ci);
lpfnEditWndProc = (WNDPROC)SetWindowLongPtr(ci.hwndList, GWLP_WNDPROC, (LONG_PTR)SubClassProc);
Of course, you can use this code to see that hwndItem of COMBOBOXINFO returns NULL. You can adjust which handle you need to control according to your combobox type.
Edit:
Your message loop code and setting style are wrong(need CBS_HASSTRINGS), please refer to:
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
......
hwndCombo = CreateWindow(L"Combobox", NULL,
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
CBS_OWNERDRAWFIXED | CBS_AUTOHSCROLL | WS_VSCROLL | WS_HSCROLL | CBS_HASSTRINGS,
10, 10, 100, 110, hwnd, (HMENU) BTN_COMBO, g_hinst, NULL);

Sending message TVM_EDITLABEL fails

TreeView_EditLabel(tv, item) fails(returns zero).
item is valid, and is the only item in the treeview.
What's the problem? Is there some prerequisite? Or it just doesn't work?
I'm on Windows 10.
Here's a minimal reproducible example
#include <windowsx.h>
#include <CommCtrl.h>
#include <assert.h>
#pragma comment(lib, "Comctl32.lib")
HWND hTv;
HTREEITEM hItem;
auto className = L"someclass";
ATOM MyRegisterClass(HINSTANCE hInstance);
void InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
InitCommonControls();
MyRegisterClass(hInstance);
InitInstance(hInstance, nCmdShow);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex{};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.lpszClassName = className;
return RegisterClassExW(&wcex);
}
void InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd = CreateWindowW(className, L"", WS_OVERLAPPEDWINDOW, 400, 400, 400, 400, nullptr, nullptr, hInstance, nullptr);
assert(hWnd);
hTv = CreateWindowW(WC_TREEVIEW, L"Tree View", WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES, 0, 0, 300, 300, hWnd, 0, 0, NULL);
assert(hTv);
{
TVITEM tvi{};
TVINSERTSTRUCT tvins{};
tvi.mask = TVIF_TEXT;
wchar_t name[] = L"item";
tvi.pszText = name;
tvins.item = tvi;
hItem = TreeView_InsertItem(hTv, &tvins);
}
ShowWindow(hWnd, nCmdShow);
SetFocus(hTv);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_NOTIFY:
{
switch (reinterpret_cast<LPNMHDR>(lParam)->code)
{
case NM_CLICK:
assert(TreeView_EditLabel(hTv, hItem));
break;
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
A tree-view control needs the TVS_EDITLABELS control style set to support editing labels of tree-view items.
You need to replace the tree-view control creation code
hTv = CreateWindowW(
WC_TREEVIEW,
L"Tree View",
WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES,
0, 0, 300, 300,
hWnd,
0,
0,
NULL);
with this:
hTv = CreateWindowW(
WC_TREEVIEW,
L"Tree View",
WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES | TVS_EDITLABELS,
0, 0, 300, 300,
hWnd,
0,
0,
NULL);
The tree-view controls overview overview provides additional details under the section titled tree-view label edition.

What is the correct designpattern for UI?

I am making a small app for loading data from an external device. It uses a DLL.
I want to make the app running in one window. With the menu it should be possible to bring up the correct state of UI. I dont want to use dialogs or seperate windows.
My idea is to use a state variable and have the WM_PAINT message handle the different content of the windows (createwindowEx for different text and button items i.e.).
Is this the correct way of doing so? I have searched the internet, read Programming Windows and searched stackoverflow and could not find a simple example. It is always using different windows with dedicated winproc functions. It feels like i dont need that...
Can someone help me with a direction or small example/link ?
thx
EDIT:
I enclose the code that i have so far now. Can someone have a look on that, Thx.
#if defined _MSC_VER || defined __BORLANDC__
#define OEMRESOURCE
#endif
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#include <windows.h> //include all the basics
#include <tchar.h> //string and other mapping macros
#include <iostream>
#include <string>
#include "hrmcom.h"
#include "resource.h"
typedef std::basic_string<TCHAR> ustring;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildProc(HWND, UINT, WPARAM, LPARAM);
inline int ErrMsg(const ustring&);
bool CreateChild(HWND);
bool CreateChildScreen(HWND);
ustring classname = TEXT("MAINWND");
ustring childname = TEXT("CHILDWND");
HWND hwndChild[3], hwndCtrl[CTRNUM];
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR pStr, int nCmd)
{
WNDCLASSEX wcx = { 0 };
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.lpfnWndProc = WndProc;
wcx.hInstance = hInst;
wcx.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1));
wcx.hCursor = reinterpret_cast<HCURSOR>(LoadImage(0, IDC_ARROW,
IMAGE_CURSOR, 0, 0, LR_SHARED));
wcx.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1);
wcx.lpszClassName = classname.c_str();
if (!RegisterClassEx(&wcx))
{
ErrMsg(_T("Failed to register wnd class"));
return -1;
}
wcx.lpfnWndProc = ChildProc;
wcx.cbWndExtra = sizeof (long);
wcx.hIcon = NULL;
wcx.lpszClassName = childname.c_str();
if (!RegisterClassEx(&wcx))
{
ErrMsg(_T("Failed to register wnd class"));
return -1;
}
HMENU hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU));
HWND hwnd = CreateWindowEx(0,
classname.c_str(),
TEXT("Polar Loader"),
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,
400, 200, 600, 300,
0,
hMenu,
hInst,
0);
if (!hwnd)
{
ErrMsg(TEXT("Failed to create wnd"));
return -1;
}
ShowWindow(hwnd, nCmd);
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg, 0, 0, 0)>0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return static_cast<int>(msg.wParam);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
//Create the Childwindows
if (!CreateChild(hwnd))
{
ErrMsg(TEXT("Failed to create Childwindows"));
return -1;
}
//Create controls of the Childwindows
if (!CreateChildScreen(hwnd))
{
ErrMsg(TEXT("Failed to create Conrols of Childwindows"));
return -1;
}
return 0;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_Acc:
ShowWindow(hwndChild[0], SW_SHOW);
return 0;
case IDM_Con:
case IDM_Help:
case IDM_Log:
case IDM_Pol:
case IDM_Trans:
ShowWindow(hwndChild[0], SW_HIDE);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0); //signal end of application
return 0;
default:
//let system deal with msg
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
LRESULT CALLBACK ChildProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
SetWindowLong(hwnd, 0, 0);
return 0;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_CTRL+5:
MessageBeep(0);
return 0;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
inline int ErrMsg(const ustring& s)
{
return MessageBox(0, s.c_str(), _T("ERROR"), MB_OK | MB_ICONEXCLAMATION);
}
bool CreateChild(HWND hwnd)
{
static int x;
for (x = 0; x < 3; x++)
{
hwndChild[x] = CreateWindowEx(0, childname.c_str(), NULL,
WS_CHILDWINDOW,
0, 0, 600, 300,
hwnd, (HMENU)x,
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
NULL);
if (!hwndChild[x]) return FALSE;
}
return TRUE;
}
bool CreateChildScreen(HWND hwnd)
{
LPCWSTR lpType[CTRNUM] = { TEXT("static"), TEXT("edit"), TEXT("edit"), TEXT("static"), TEXT("static"), TEXT("button") };
LPCWSTR lpName[CTRNUM] = { TEXT("Log on to Gedysan Training App:"), TEXT(""), TEXT(""), TEXT("Username:"), TEXT("Password:"), TEXT("Log on") };
DWORD dStyle[CTRNUM] = { WS_CHILD | WS_VISIBLE | ES_LEFT,
WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER | ES_AUTOHSCROLL,
WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER | ES_PASSWORD | ES_AUTOHSCROLL,
WS_CHILD | WS_VISIBLE | ES_LEFT,
WS_CHILD | WS_VISIBLE | ES_LEFT,
WS_CHILD | WS_VISIBLE | ES_LEFT
};
RECT rc[CTRNUM] = { { 15, 10, 400, 25 }, { 100, 40, 150, 25 }, { 100, 70, 150, 25 }, { 15, 45, 70, 25 }, { 15, 75, 70, 25 }, { 15, 110, 70, 25 } };
int iChildNum[CTRNUM] = { 0, 0, 0, 0, 0, 0 };
static int x;
for (x = 0; x < CTRNUM; x++)
{
RECT x1 = rc[x];
hwndCtrl[x] = CreateWindowEx(0, lpType[x], lpName[x], dStyle[x],
x1.left, x1.top, x1.right, x1.bottom,
hwndChild[iChildNum[0]], (HMENU)(ID_CTRL + x),
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), 0);
if (!hwndChild[x]) return FALSE;
}
return TRUE;
}
With your proposed approach you will get a bunch of unrelated stuff all in the WM_PAINT handler, making a messy design. When a menu selection is made you get a message. Run code related to that menu command in that message handler.
If your UI is composed of controls then WM_PAINT is not involved at all: Those controls do their own painting. Your WM_PAINT handler should do any custom painting you need, and nothing else.

Resources