Related
My goal is to create 5 groups of radio buttons (i know it contradict with the title but you still get the point) for user choice using only Win32 API (so no window form here).
I tried using a combination of groupbox and SetWindowLongPtr but it still not working as expected (note that im using GWLP_WNDPROC as the index). If i use SetWindowLongPtr to a group box then that groupbox is gone and everything else work as expected.
I could use a "virtual" group box but it reduce the efficency of my code. Some one might recommend using WS_GROUP but it only apply if there are 2 group of radio buttons ( I think ). And i also dont like using resource so is there any solution to this problem or i just have to stuck with the "virtual" group box?
Minimal reproducible sample:
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wp, LPARAM lp)
{
switch (uMsg)
{
default:
return DefWindowProc(hwnd, uMsg, wp, lp);
}
}
int WINAPI wWinMain(HINSTANCE hinst, HINSTANCE hiprevinst, PWSTR nCmdLine, int ncmdshow)
{
const wchar_t CLASS_NAME[] = L"Sample";
WNDCLASS wc = { };
wc.lpfnWndProc = WndProc;
wc.hInstance = hinst;
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0,
CLASS_NAME,
L"Sample window",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, // Window style
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL,
NULL,
hinst,
NULL);
HWND groupbox = CreateWindowEx(0, L"Button", L"Groupbox", WS_VISIBLE | WS_CHILD | BS_GROUPBOX, 10, 10, 100, 100, NULL, NULL, hinst, NULL);
HWND radiobutton1 = CreateWindowEx(0, L"Button", L"Groupbox", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 10, 10, 60, 60, groupbox, NULL, hinst, NULL);
SetWindowLongPtr(groupbox, GWLP_WNDPROC, (LONG)WndProc);
SendMessage(groupbox, NULL, NULL, TRUE);
ShowWindow(hwnd, ncmdshow);
MSG msg;
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Due to i stripped so much of the necessary function away, you need to go to task manager and kill the process named "Autoclicker" for some reason to be able to recompile it again
Make sure you handle WM_DESTROY otherwise window won't close properly.
The radio buttons, all child dialog items, and all child windows should be created in WM_CREATE section of parent window. They need the HWND handle from parent window.
SetWindowLongPtr(.. GWLP_WNDPROC ...) is an old method used for subclassing. Your usage is incorrect. You don't need it anyway.
It's unclear what SendMessage(groupbox, NULL, NULL, TRUE); is supposed to do.
Just add the radio buttons, make sure the first radio button has an added WS_TABSTOP|WS_GROUP as shown below
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wp, LPARAM lp)
{
switch (uMsg)
{
case WM_CREATE:
{
HINSTANCE hinst = GetModuleHandle(0);
auto add = [&](const wchar_t* name,
int id, int x, int y, int w, int h, bool first = false)
{
DWORD style = WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON;
if (first) style |= WS_GROUP | WS_TABSTOP;
return CreateWindowEx(0, L"Button", name, style,
x, y, w, h, hwnd, (HMENU)id, hinst, NULL);
};
HWND groupbox1 = CreateWindowEx(0, L"Button", L"Groupbox1",
WS_VISIBLE | WS_CHILD | BS_GROUPBOX, 2, 2, 250, 120, hwnd, NULL, hinst, NULL);
HWND radio1 = add(L"radio1", 1, 10, 30, 200, 20, true);
HWND radio2 = add(L"radio2", 2, 10, 51, 200, 20);
HWND radio3 = add(L"radio3", 3, 10, 72, 200, 20);
HWND radio4 = add(L"radio4", 4, 10, 93, 200, 20);
HWND groupbox2 = CreateWindowEx(0, L"Button", L"Groupbox2",
WS_VISIBLE | WS_CHILD | BS_GROUPBOX, 280, 2, 250, 120, hwnd, NULL, hinst, NULL);
HWND radio11 = add(L"radio1", 11, 300, 30, 200, 20, true);
HWND radio12 = add(L"radio2", 12, 300, 51, 200, 20);
HWND radio13 = add(L"radio3", 13, 300, 72, 200, 20);
HWND radio14 = add(L"radio4", 14, 300, 93, 200, 20);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, uMsg, wp, lp);
}
}
int WINAPI wWinMain(HINSTANCE hinst, HINSTANCE hiprevinst, PWSTR nCmdLine, int ncmdshow)
{
const wchar_t CLASS_NAME[] = L"Sample";
WNDCLASS wc = { };
wc.lpfnWndProc = WndProc;
wc.hInstance = hinst;
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(0, CLASS_NAME, L"Sample window",
WS_OVERLAPPEDWINDOW, 0, 0, 800, 600,
NULL, NULL, hinst, NULL);
ShowWindow(hwnd, ncmdshow);
MSG msg;
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
I am writing my first Win32 app in C++ and I am trying to create 3 windows between which to redirect based on what buttons the user clicks on. I initialised the windows (window1,window2,window3) as children of the main window hwnd and only set window1 as visible. window1 also has two buttons, each of which is supposed to direct either to window2 or window3.
I tried to hide window1 and show which window I want to switch to using the ShowWindow() function. However, it is not working (clicking the buttons does nothing). Could you help me understand why?
On another piece of code I had before, where I had not created window1, and the buttons and the other two windows were just children of hwnd, pressing the button did show the right window, but the button remained there, even if it did not belong on that window.
Also, is there a more efficient way of switching between windows (without deleting and creating them again and again?
Thanks!!!
EDIT: I managed to solve my problem. It arose from the fact that I had declared windows 1,2 and 3 as static. By declaring them under the same class as hwnd, I was able to process the messages from the buttons under the main WindowProcedure(). Since my program is gonna be quite simple, I do not need to create a different procedure for the new windows, but thanks to the comments, now I also know how to do that!
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
window1 = CreateWindow("STATIC","Window 1",WS_VISIBLE|WS_BORDER|WS_CHILD,0,0,600,600,hwnd,NULL,NULL,NULL);
window2 = CreateWindow("STATIC","Window 2",WS_BORDER|WS_CHILD,0,0,600,600,hwnd,NULL,NULL,NULL);
button2 = CreateWindow(
"BUTTON",
"SECOND WINDOW",
WS_CHILD | WS_VISIBLE | WS_BORDER,
350, 480,
200, 20,
window1, (HMENU) 2, NULL, NULL);
window3 = CreateWindow("STATIC","Window 3",WS_BORDER|WS_CHILD,0,0,600,600,hwnd,NULL,NULL,NULL);
button3 = CreateWindow(
"BUTTON",
"THIRD WINDOW",
WS_CHILD | WS_VISIBLE | WS_BORDER,
50, 480,
200, 20,
window1, (HMENU) 3, NULL, NULL);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case 2:
ShowWindow(window1,SW_HIDE);
ShowWindow(window2,SW_SHOW);
break;
case 3:
ShowWindow(window1,SW_HIDE);
ShowWindow(window3,SW_SHOW);
break;
}
break;
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
First, create a new window1 as in the steps of creating the main form.
Then create a windowprocessforwindow1 for window1, process the WM_COMMAND message in this function.
Here is the sample:
#include <Windows.h>
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK windowprocessforwindow1(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static HWND window1, window2, button2, window3, button3;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("hello windows");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WindowProcedure;
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 WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
static HINSTANCE hInstance;
static WNDCLASSEX win1;
case WM_CREATE:
hInstance = (HINSTANCE)::GetWindowLong(hwnd, GWL_HINSTANCE);
win1.hInstance = hInstance;
win1.lpszClassName = L"Window 1";
win1.lpfnWndProc = (WNDPROC)windowprocessforwindow1; /* This function is called by windows */
win1.style = CS_DBLCLKS; /* Catch double-clicks */
win1.cbSize = sizeof(WNDCLASSEX);
win1.hIcon = LoadIcon(NULL, IDI_APPLICATION);
win1.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
win1.hCursor = LoadCursor(NULL, IDC_ARROW);
win1.lpszMenuName = NULL; /* No menu */
win1.cbClsExtra = 0; /* No extra bytes after the window class */
win1.cbWndExtra = 0; /* structure or the window instance */
win1.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
if (!RegisterClassEx(&win1))
return 0;
window1 = CreateWindowEx(
0, /* Extended possibilites for variation */
L"Window 1", /* Classname */
L"Window 1", /* Title Text */
WS_VISIBLE | WS_BORDER | WS_CHILD, /* default window */
0, /* Windows decides the position */
0, /* where the window ends up on the screen */
600, /* The programs width */
600, /* and height in pixels */
hwnd, /* The window is a child-window to desktop */
NULL, /* No menu */
hInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
window2 = CreateWindow(L"STATIC", L"Window 2", WS_BORDER | WS_CHILD, 0, 0, 600, 600, hwnd, NULL, NULL, NULL);
button2 = CreateWindow(
L"BUTTON",
L"SECOND WINDOW",
WS_CHILD | WS_VISIBLE | WS_BORDER,
350, 480,
200, 20,
window1, (HMENU)2, NULL, NULL);
window3 = CreateWindow(L"STATIC", L"Window 3", WS_BORDER | WS_CHILD, 0, 0, 600, 600, hwnd, NULL, NULL, NULL);
button3 = CreateWindow(
L"BUTTON",
L"THIRD WINDOW",
WS_CHILD | WS_VISIBLE | WS_BORDER,
50, 480,
200, 20,
window1, (HMENU)3, NULL, NULL);
ShowWindow(window1, SW_SHOW);
break;
case WM_DESTROY:
PostQuitMessage(0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK windowprocessforwindow1(HWND handleforwindow1, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
{
switch (wParam) {
case 2:
ShowWindow(window1, SW_HIDE);
ShowWindow(window2, SW_SHOW);
break;
case 3:
ShowWindow(window1, SW_HIDE);
ShowWindow(window3, SW_SHOW);
break;
}
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(handleforwindow1, msg, wParam, lParam);
}
return 0;
}
I am creating a Windows desktop application using win32 api in visual studio 2019. I know there are many other options avialable to build UI like MFC, XAMAL and C#, but i needed to build it in win32. I have learnt some basics in win32 api but recently i was working on tabControll and there i got an issue or i missed some thing. I am creating two tabs and want to add different content withing them. My current code is working and creating the tabs but it is adding same content in both tabs. How should i define each tab's content differently.
void createTabView(HWND hWnd) {
RECT rcClient;
INITCOMMONCONTROLSEX icex;
static HWND hwndTab_1_1_1;
HWND hwndTab;
TCITEM tie;
int i;
TCHAR achTemp[256];
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
GetClientRect(hWnd, &rcClient);
hwndTab = CreateWindow(WC_TABCONTROL, L"",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, rcClient.right, rcClient.bottom,
hWnd, NULL, hInst, NULL);
// Add tabs for each day of the week.
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.iImage = -1;
tie.pszText = tabLBL1;
TabCtrl_InsertItem(hwndTab, 1, &tie);
tie.pszText = tabLBL2;
TabCtrl_InsertItem(hwndTab, 2, &tie);
SendMessage(hwndTab, WM_SETFONT,
reinterpret_cast<WPARAM>(GetStockObject(DEFAULT_GUI_FONT)), 0);
HWND hwndStatic = CreateWindow(WC_STATIC, L"",
WS_CHILD | WS_VISIBLE | WS_BORDER,
200, 200, 100, 100, // Position and dimensions; example only.
hwndTab, NULL, hInst, // g_hInst is the global instance handle
NULL);}
The tab control triggers the WM_NOTIFY signal when the tab page is switched, so as long as we process the WM_NOTIFY message, we can control the tab control message.
In the WM_NOTIFY message:
wParam: a control ID that identifies the WM_NOTIFY message sent.
lParam: a pointer to the NMHDR structure.
Therefore, we can determine the notification code sent by the Tab control in the WM_NOTIFY message processing program by judging the code value in the NMHDR structure.
We can use TCN_SELCHANGE to handle the operation when the Tab tab changes, and use TabCtrl_GetCurSel to get the index of the current tab and define the content of the current tab.
Here is the sample:
#include <Windows.h>
#include <commctrl.h>
LRESULT CALLBACK WndProc(HWND, UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("windows");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
}
hwnd = CreateWindow(szAppName,
TEXT("the hello program"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
while (GetMessageW(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message,WPARAM wParam,LPARAM lParam)
{
static HINSTANCE hInstance;
static HWND hwndTab = 0 , hwndStatic = 0;
TCITEM tie;
RECT rcClient;
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
TCHAR tabLBL1[256];
GetClientRect(hwnd, &rcClient);
switch (message)
{
case WM_CREATE:
{
hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
hwndTab = CreateWindow(WC_TABCONTROL, "",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, rcClient.right, rcClient.bottom,
hwnd, NULL, hInstance, NULL);
// Add tabs for each day of the week.
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.iImage = -1;
wsprintf(tabLBL1, "tab1");
tie.pszText = tabLBL1;
TabCtrl_InsertItem(hwndTab, 0, &tie);
wsprintf(tabLBL1, "tab2");
TabCtrl_InsertItem(hwndTab, 1, &tie);
SendMessage(hwndTab, WM_SETFONT,
reinterpret_cast<WPARAM>(GetStockObject(DEFAULT_GUI_FONT)), 0);
hwndStatic = CreateWindow(WC_STATIC, "",
WS_CHILD | WS_VISIBLE | WS_BORDER,
200, 200, 100, 100, // Position and dimensions; example only.
hwndTab, NULL, hInstance, // g_hInst is the global instance handle
NULL);
ShowWindow(hwndStatic,TRUE);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_NOTIFY:
if (((LPNMHDR)lParam)->code == TCN_SELCHANGE)
{
int tabID = TabCtrl_GetCurSel(hwndTab);
switch (tabID)
{
case 0:
ShowWindow(hwndStatic, TRUE);
break;
case 1:
ShowWindow(hwndStatic, FALSE);
break;
default:
break;
}
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
Not sure if this is the solution, but the iItem parameter to TabCtrl_InsertItem is zero-based. Try:
tie.pszText = tabLBL1;
TabCtrl_InsertItem(hwndTab, 0, &tie);
tie.pszText = tabLBL2;
TabCtrl_InsertItem(hwndTab, 1, &tie);
Also (and I'm not sure if this is relevant), you look like you intended to call InitCommonControlsEx in your code but fail to do so.
Hello all I have been trying to paint to my child window outside of WM_PAINT and Instead of painting on the child window it paints off of the window onto the screen I think it may be because of the location of x and y but shouldn't point (0,0) be the top left of the child window not of my actual screen?
Here is what the code I wrote to try to paint onto the child window:
#pragma warning(disable:4996)
#pragma comment(lib, "Ws2_32.lib")
#include <Windows.h>
#define WIDTH 800
#define HEIGHT 600
#define CLASS_NAME "Class"
#define IDC_MAIN_EDIT 101
#define IDC_WINDOW 102
int x = 0, y = 0;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void print_line(HWND hwnd, char *Msg);
void Println();
int Run();
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcex = { 0 };
MSG msg;
HWND hwnd = NULL;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_VREDRAW | CS_OWNDC;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = CLASS_NAME;
wcex.hInstance = hInstance;
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wcex))
{
MessageBoxA(NULL, "Failed to register class", "Error", MB_OK | MB_ICONERROR);
return -1;
}
hwnd = CreateWindow(CLASS_NAME //Name of the window class
, CLASS_NAME//Title of the window
, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN// the window style
, 0 //x Postition of the window
, 0// y position of the windpo
, WIDTH, HEIGHT, // Width and Height
NULL,//Parent window(we have no parent window)
NULL,//Menu(we are not using menu's)
hInstance,//application handle
NULL);//Creates the window
if (!hwnd)
{
MessageBoxA(NULL, "Failed to register class", "Error", MB_OK | MB_ICONERROR);
return -2;
}
ShowWindow(hwnd, nCmdShow);
return Run();
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND hEdit = NULL;
HWND hWindow = NULL;
#define Print(msg2) print_line(hWindow, msg2)
switch (msg)
{
case WM_CREATE:
hWindow = CreateWindowEx(WS_EX_CLIENTEDGE, "Window", "",
WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOHSCROLL | WS_CLIPSIBLINGS,
0, 0, WIDTH, HEIGHT - 25, hwnd, (HMENU)IDC_WINDOW, GetModuleHandle(NULL), NULL);
hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOHSCROLL,
0, 535, WIDTH, 25, hwnd, (HMENU)IDC_MAIN_EDIT, GetModuleHandle(NULL), NULL);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
Print("Hello");
break;
default:
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
}
int Run()
{
MSG msg = { 0 };
WSAData wsa;
WORD DllVersion = MAKEWORD(2, 1);
if (WSAStartup(DllVersion, &wsa) != 0) return -1;
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
void print_line(HWND hwnd, char *Msg)
{
HDC hdc;
hdc = GetDC(hwnd);
TextOut(hdc,
x,
y,
Msg,
strlen(Msg));
ReleaseDC(hwnd, hdc);
}
void Println()
{
x = 0;
y += 20;
}
Yes I know there are other questions concerning this topic but none of them seemed to answer my question or address any of the problems I have been experiencing while trying to paint on the child window.
It Seems to me you create your windows in WndProc, but they do not get returned, so both windows created in WndProc end up being NULL when WM_CREATE returns (AS the are only declared in WndProc).
Perhaps you need to set these both to globals, the use for example GetDC(hEdit) to draw to them. Overall it still looks like an odd bit of code.
It seems to me that you want to write text to the edit control you created with id=IDC_MAIN_EDIT by manually drawing the text into the control?
Try
case WM_COMMAND:
//Print("Hello");
hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT); // get handle to edit window
if (hEdit != NULL) // did we get a handle to the edit window?
{
int len = GetWindowTextLength(hEdit);
SendMessage(hEdit , EM_SETSEL, len, len); // select end of contents
SendMessage(hEdit , EM_REPLACESEL, 0, (LPARAM)"Hello"); // replace end with new textt
}
break;
and the edit control will draw the text for you.
If you want to manually write the text to the window created by you modify the Print macro as follows:
#define Print(msg2) print_line(hwnd, msg2)
I wrote a simple program; it's made of a main window and TextBox window above it. TextBox is 50% transparent. When TextBox gets a message, it draws a blue line on the main window.
The problem is that "transparent" actually is not transparent at all. If the blue line crosses a text in TextBox, the text just erased, despite the fact that the text is above. And vice versa: if I start typing, a part of the line in a row of a text just disappears instead of shine through.
Is this a bug? Or am I missing something?
#include <windows.h>
#include <stdio.h>
#define IDC_MAIN_EDIT 101
void DrawInWindow(HWND hWndToPaint){
HDC hdc = GetDC(hWndToPaint);
if(!hdc)printf("Invalid handle\n");
HPEN hPen = CreatePen(PS_SOLID,5,RGB(0, 0, 255));
SelectObject(hdc, hPen);
static float x=620, y=1, tg=0.5, ctg=2;
static int Xone = 1, Yone = 1;//depending on later recalculation this may become negative
MoveToEx(hdc,(int)x,(int)y,NULL);
if(tg<1){
y+=tg;
x+=Xone;
}else{
y+=Yone;
x+=ctg;
}
if(!LineTo(hdc, (int)x, (int)y) )printf("There are paint problem\n");
ReleaseDC(hWndToPaint,hdc);
//Now recalculate direction
RECT WndRect;
GetClientRect(hWndToPaint,&WndRect);
if(x>=WndRect.right){
if(ctg>0)ctg*=-1;//make negative
Xone=-1;
}
if(x<=WndRect.left){
if(ctg<0)ctg*=-1;//make positive
Xone=1;
}
if(y>=WndRect.bottom){
if(tg>0)tg*=-1;//make negative
Yone=-1;
}
if(y<=WndRect.top){
if(tg<0)tg*=-1;//make positive
Yone=1;
}
}
int CALLBACK EnumWindowsFunc(HWND hWnd, LPARAM lParam){
DrawInWindow(hWnd);
return false;
}
void PaintInMainWnd(){
EnumWindows(EnumWindowsFunc,0L);//Getting the handle of main window to draw
}
LRESULT __stdcall MyMainCallBckProcedure( HWND window, unsigned msg, WPARAM wp, LPARAM lp ){
switch(msg){
case WM_KEYDOWN:
if(wp == VK_ESCAPE)PostQuitMessage(0);
break;
case WM_DESTROY:
printf("\ndestroying window\n");
PostQuitMessage(0);
return 0;
case WM_SIZE:{
HWND hEdit;
RECT rcClient;
GetClientRect(window, &rcClient);
hEdit = GetDlgItem(window, IDC_MAIN_EDIT);
SetWindowPos(hEdit, NULL, 0, 0, rcClient.right, rcClient.bottom, SWP_NOZORDER);
break;
}
default:
return DefWindowProc( window, msg, wp, lp ) ;
}
}
WNDPROC lpEditWndProc;
LRESULT CALLBACK MyEditCallBckProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
if( (uMsg == WM_CHAR) && (wParam == VK_ESCAPE) )
{
PostQuitMessage(0);
return 0;
}
PaintInMainWnd();
lpEditWndProc(hWnd, uMsg, wParam, lParam);
}
bool CreateWindows(){
const char* const myclass = "myclass";
WNDCLASSEX wndclass = { sizeof(WNDCLASSEX), CS_DBLCLKS, MyMainCallBckProcedure,
0, 0, GetModuleHandle(0), LoadIcon(0,IDI_APPLICATION),
LoadCursor(0,IDC_ARROW), HBRUSH(COLOR_WINDOW+1),
0, myclass, LoadIcon(0,IDI_APPLICATION) };
if(RegisterClassEx(&wndclass)<0){
printf("ERR: in registering window class\n");
return false;
}
//Creating window
HWND window = CreateWindowEx( 0, myclass, "title",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
640, 480, 0, 0, GetModuleHandle(0), 0 );
if(!window){
printf("ERR: in creating window\n");
return false;
}
ShowWindow( window, SW_SHOWDEFAULT );
//creating TextBox on the window
HFONT hfDefault;
HWND hEdit;
hEdit = CreateWindowEx(0, "edit", "",
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
window, (HMENU)IDC_MAIN_EDIT, GetModuleHandle(NULL), NULL);
if(hEdit == NULL){
MessageBox(window, "Could not create edit box.", "Error", MB_OK | MB_ICONERROR);
return false;
}
hfDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
SendMessage(hEdit, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
//Now resize TextBox to fill whole parent window
RECT RectSize;
GetClientRect(window,&RectSize);
hEdit = GetDlgItem(window,IDC_MAIN_EDIT);
SetWindowPos(hEdit, 0,0,0,RectSize.right,RectSize.bottom,SWP_NOZORDER);
//Let's try to catch some messages in TextBox...
lpEditWndProc = (WNDPROC)SetWindowLongPtr(hEdit, GWL_WNDPROC, (LONG_PTR)&MyEditCallBckProcedure);
//Making hEdit transparent
SetWindowLongPtr(hEdit,GWL_EXSTYLE, WS_EX_LAYERED | GetWindowLongPtr(hEdit, GWL_EXSTYLE) );
SetLayeredWindowAttributes(hEdit, 0, (255*50)/100, LWA_ALPHA);
return true;
//###
}
int main(){
if(!CreateWindows() ){printf("Something gone wrong\n");return 1;}
MSG msg;
while(GetMessage(&msg,0,0,0) ){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Don't know is it important, but I should also mention, that I have tested only under Ubuntu with Wine, as my Windows is screwed up by this bug. Anyway, I hope the problem is not in Wine itself.
And sorry for amount of code, I really don't know what to remove to make it smaller.
I found the workaround. I created background top level window, and made foreground window 50% trasparent. I draw the lines in background window. If the front window moved or resized, the back window reacts accordingly with help of WM_WINDOWPOSCHANGED message, it sent on every little move/resize.
Anyway, this workaround is little dirty because of:
Linux/wine specific problems: 1) Display Manager do not decorate transparent window of wine(but this could be evaded by making second window 0% transparent) 2) Dragged window wobbling, but the second moving straight. All OS specific problem: second window is visible in the taskbar. Theoretically the last could be avoided by adding WS_EX_TOOLWINDOW to unowned window. The quote
To prevent the window button from being placed on the taskbar, create
the unowned window with the WS_EX_TOOLWINDOW extended style.
But, at least, in wine it doesn't work. Well, I hope this is a bug :)
#include <windows.h>
#include <stdio.h>
#define IDC_MAIN_EDIT 101
HWND hBackWnd;
void DrawInWindow(HWND hWndToPaint){
HDC hdc = GetDC(hWndToPaint);
if(!hdc)printf("Invalid handle\n");
HPEN hPen = CreatePen(PS_SOLID,5,RGB(0, 0, 255));
SelectObject(hdc, hPen);
static float x=620, y=1, tg=0.5, ctg=2;
static int Xone = 1, Yone = 1;//depending on later recalculation this may become negative
MoveToEx(hdc,(int)x,(int)y,NULL);
if(tg<1){
y+=tg;
x+=Xone;
}else{
y+=Yone;
x+=ctg;
}
if(!LineTo(hdc, (int)x, (int)y) )printf("There are paint problem\n");
ReleaseDC(hWndToPaint,hdc);
//Now recalculate direction
RECT WndRect;
GetClientRect(hWndToPaint,&WndRect);
if(x>=WndRect.right){
if(ctg>0)ctg*=-1;//make negative
Xone=-1;
}
if(x<=WndRect.left){
if(ctg<0)ctg*=-1;//make positive
Xone=1;
}
if(y>=WndRect.bottom){
if(tg>0)tg*=-1;//make negative
Yone=-1;
}
if(y<=WndRect.top){
if(tg<0)tg*=-1;//make positive
Yone=1;
}
}
LRESULT __stdcall MyMainCallBckProcedure( HWND window, unsigned msg, WPARAM wp, LPARAM lp ){
switch(msg){
case WM_KEYDOWN:
if(wp == VK_ESCAPE)PostQuitMessage(0);
break;
case WM_DESTROY:
printf("\ndestroying window\n");
PostQuitMessage(0);
return 0;
case WM_SIZE:
HWND hEdit;
RECT rcClient;
GetClientRect(window, &rcClient);
hEdit = GetDlgItem(window, IDC_MAIN_EDIT);
SetWindowPos(hEdit, NULL, 0, 0, rcClient.right, rcClient.bottom, SWP_NOZORDER);
break;
case WM_WINDOWPOSCHANGED:{//LPARAM is a ptr to WINDOWPOS
RECT BckWndRect;
if(!GetWindowRect(hBackWnd, &BckWndRect) )printf("ERR: getting backwnd rectangle\n");
bool IsRepaint;
WINDOWPOS* pNewPos = (WINDOWPOS*)lp;
if(BckWndRect.left+BckWndRect.right != pNewPos->cx
|| BckWndRect.top+BckWndRect.bottom != pNewPos->cy)IsRepaint = true;
else IsRepaint = false;
MoveWindow(hBackWnd, pNewPos->x, pNewPos->y, pNewPos->cx, pNewPos->cy, IsRepaint);
break;
}
default:
return DefWindowProc( window, msg, wp, lp ) ;
}
}
WNDPROC lpEditWndProc;
LRESULT CALLBACK MyEditCallBckProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
if( (uMsg == WM_CHAR) && (wParam == VK_ESCAPE) )
{
PostQuitMessage(0);
return 0;
}
DrawInWindow(hBackWnd);
lpEditWndProc(hWnd, uMsg, wParam, lParam);
}
bool CreateWindows(){
//creating back window
const char* backwnd = "backwnd";
WNDCLASSEX backwndclass = { sizeof(WNDCLASSEX), CS_DBLCLKS, MyMainCallBckProcedure,
0, 0, GetModuleHandle(0), LoadIcon(0,IDI_APPLICATION),
LoadCursor(0,IDC_ARROW), HBRUSH(COLOR_WINDOW+1),
0, backwnd, LoadIcon(0,IDI_APPLICATION) };
if(RegisterClassEx(&backwndclass)<0){
printf("ERR: in registering second window class\n");
return false;
}
hBackWnd = CreateWindowEx( 0, backwnd, "title", WS_EX_TOOLWINDOW |
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
640, 480, 0, 0, GetModuleHandle(0), 0 );
if(!hBackWnd){
printf("ERR: in creating background window\n");
return false;
}
ShowWindow( hBackWnd, SW_SHOWDEFAULT );
//Creating front window
const char* const frontwnd = "frontwnd";
WNDCLASSEX wndclass = { sizeof(WNDCLASSEX), CS_DBLCLKS, MyMainCallBckProcedure,
0, 0, GetModuleHandle(0), LoadIcon(0,IDI_APPLICATION),
LoadCursor(0,IDC_ARROW), HBRUSH(COLOR_WINDOW+1),
0, frontwnd, LoadIcon(0,IDI_APPLICATION) };
if(RegisterClassEx(&wndclass)<0){
printf("ERR: in registering foreground window class\n");
return false;
}
HWND window = CreateWindowEx( 0, frontwnd, "title",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
640, 480, 0, 0, GetModuleHandle(0), 0 );
if(!window){
printf("ERR: in creating foreground window\n");
return false;
}
ShowWindow( window, SW_SHOWDEFAULT );
//creating textbox
HWND hEdit = CreateWindowEx( 0, "edit", "", WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL
| ES_MULTILINE | ES_AUTOVSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, 640,
480, window, (HMENU)IDC_MAIN_EDIT, GetModuleHandle(0), 0 );
HFONT hfDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
SendMessage(hEdit, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
//Let's try to catch some messages in TextBox...
lpEditWndProc = (WNDPROC)SetWindowLongPtr(hEdit, GWL_WNDPROC, (LONG_PTR)&MyEditCallBckProcedure);
//Making foreground window transparent
SetWindowLongPtr(window,GWL_EXSTYLE, WS_EX_LAYERED | GetWindowLongPtr(window, GWL_EXSTYLE) );
SetLayeredWindowAttributes(window, 0, (255*50)/100, LWA_ALPHA);
return true;
//###
}
int main(){
if(!CreateWindows() ){printf("Something gone wrong\n");return 1;}
MSG msg;
while(GetMessage(&msg,0,0,0) ){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}