how to group many radio buttons into 3 groups in C++? - c++

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;
}

Related

C++ Win32 GetWindowText buffer capacity

I'm learning to write basic Win32 apps in C++ and am trying to pass typed text from one editable textbox to a new window after pressing a button.
I noticed that the default text buffer capacity for such a transfer is 20 characters in Visual Studio 2019 (I am on 64-bit Windows 10). After I try to pass a string longer than 20 characters, I get an exception thrown.
I would like to know how to increase that buffer capacity, since eventually I want to be able to pass a filepath into the text input window and open that file.
My code:
#include <windows.h>
#include <string>
#include <iostream>
//lresult callback prototype
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
//window handles
HWND hMainWindow;
HINSTANCE hMainInstance;
HWND hLblOutput;
HWND hTxtInput;
HWND hButton;
#define IDC_TEXTBOX 1000
#define IDC_BUTTON 1001
//call to winmain - equivalent of main for win32 environments
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg = { 0 };
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = TEXT("NiceWindowsApp");
if (!RegisterClass(&wc))
return 1;
hMainWindow = CreateWindow(wc.lpszClassName, TEXT("My Windows Application"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 640, 480, 0, 0, hInstance, NULL);
hMainInstance = wc.hInstance;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
//callback definition
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int offset = 0;
switch (message) {
case WM_CREATE:
hMainWindow = hWnd;
hTxtInput = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), TEXT("Type something here"),
WS_VISIBLE | WS_CHILD | ES_LEFT, 50, 50, 400, 25, hWnd,
(HMENU)IDC_TEXTBOX, hMainInstance, NULL);
hButton = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("BUTTON"), TEXT("Press Me!"), WS_VISIBLE | WS_CHILD | WM_COPY | ES_LEFT, 500, 30, 100, 60, hWnd,
(HMENU)IDC_BUTTON, hMainInstance, NULL);
break;
case WM_COMMAND:
if (LOWORD(wParam) == IDC_BUTTON)
{
//CANNOT HANDLE MORE THAN 20 CHARACTERS!!!
std::string input;
GetWindowTextA(hTxtInput, reinterpret_cast<char*> ((char*)input.c_str()), 400);
++offset;
hLblOutput = CreateWindowEx(WS_EX_STATICEDGE, TEXT("EDIT"), input.c_str(), WS_VISIBLE | WS_CHILD | ES_READONLY | ES_LEFT, 50, 200 + offset * 26, 800, 25, hWnd,
(HMENU)IDC_TEXTBOX, hMainInstance, NULL);
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I have tried to increase the third parameter in GetWindowTextA() to various numbers up to 4000, but it didn't seem to help.
One correct way to do this is:
std::wstring text;
text.resize(GetWindowTextLengthW(hTxtInput));
text.resize(GetWindowTextW(hTxtInput, text.data(), text.size() + 1));
Solved as per the below:
#include <windows.h>
#include <string>
#include <iostream>
//lresult callback prototype
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
//window handles
HWND hMainWindow;
HINSTANCE hMainInstance;
HWND hLblOutput;
HWND hTxtInput;
HWND hButton;
CHAR s_text_1[]{ "Some text.." };
CHAR s_text_2[]{ 0 };
#define IDC_TEXTBOX 1000
#define IDC_BUTTON 1001
//call to winmain - equivalent of main for win32 environments
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg = { 0 };
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = TEXT("NiceWindowsApp");
if (!RegisterClass(&wc))
return 1;
hMainWindow = CreateWindow(wc.lpszClassName, TEXT("My Windows Application"), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0, 0, 640, 480, 0, 0, hInstance, NULL);
hMainInstance = wc.hInstance;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
//callback definition
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int offset = 0;
switch (message) {
case WM_CREATE:
hMainWindow = hWnd;
hTxtInput = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), s_text_1,
WS_VISIBLE | WS_CHILD | ES_LEFT, 50, 50, 400, 25, hWnd,
(HMENU)IDC_TEXTBOX, hMainInstance, NULL);
hButton = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("BUTTON"), TEXT("Press Me!"), WS_VISIBLE | WS_CHILD | WM_COPY | ES_LEFT, 500, 30, 100, 60, hWnd,
(HMENU)IDC_BUTTON, hMainInstance, NULL);
break;
case WM_COMMAND:
if (LOWORD(wParam) == IDC_BUTTON)
{
//CANNOT HANDLE MORE THAN 20 CHARACTERS!!!
std::wstring input;
//GetWindowTextW(hTxtInput, reinterpret_cast<char*> ((char*)input.c_str()), 400);
int lgth = GetWindowTextLength(hTxtInput);
//GetWindowTextW(hTxtInput, reinterpret_cast<wchar_t*> ((wchar_t*)input.c_str()), lgth);
//GetWindowTextA(hTxtInput, char[], 400);
GetWindowText(hTxtInput, s_text_1, 255);
++offset;
hLblOutput = CreateWindowEx(WS_EX_STATICEDGE, TEXT("EDIT"), s_text_1, WS_VISIBLE | WS_CHILD | ES_READONLY | ES_LEFT, 50, 200 + offset * 26, 800, 25, hWnd,
(HMENU)IDC_TEXTBOX, hMainInstance, NULL);
SetWindowText(hLblOutput, s_text_1);
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Thanks for your hints everybody.
EDIT: I now realise that this solution is not technically perfect and can lead to undefined behaviour and/or memory leaks. I'll take the advice presented in the other answers and comments into account and adjust the code accordingly.
EDIT 2.0 (2023.02.04): My most recent code is below. Hopefully it's more robust.
#include <windows.h>
#include <string>
#include <iostream>
//last update - 04.02.2023 as per StackOverflow thread recommendations.
//lresult callback prototype
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
//window handles
HWND hMainWindow;
HINSTANCE hMainInstance;
HWND hLblOutput;
HWND hTxtInput;
HWND hButton;
CHAR s_text_1[]{ "Some text.." };
#define IDC_TEXTBOX 1000
#define IDC_BUTTON 1001
//call to winmain - equivalent of main for win32 environments
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg = { 0 };
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = TEXT("NiceWindowsApp");
if (!RegisterClass(&wc))
return 1;
hMainWindow = CreateWindow(wc.lpszClassName, TEXT("My Windows Application"), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0, 0, 640, 480, 0, 0, hInstance, NULL);
hMainInstance = wc.hInstance;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
//callback definition
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int offset = 0;
switch (message) {
case WM_CREATE:
hMainWindow = hWnd;
hTxtInput = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), s_text_1,
WS_VISIBLE | WS_CHILD | ES_LEFT, 50, 50, 400, 25, hWnd,
(HMENU)IDC_TEXTBOX, hMainInstance, NULL);
hButton = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("BUTTON"), TEXT("Press Me!"), WS_VISIBLE | WS_CHILD | WM_COPY | ES_LEFT, 500, 30, 100, 60, hWnd,
(HMENU)IDC_BUTTON, hMainInstance, NULL);
break;
case WM_COMMAND:
if (LOWORD(wParam) == IDC_BUTTON)
{
std::wstring input;
//resize added as suggested
input.resize(GetWindowTextLengthW(hTxtInput));
GetWindowTextW(hTxtInput, input.data(), input.size() + 1);
++offset;
hLblOutput = CreateWindowEx(WS_EX_STATICEDGE, TEXT("EDIT"), s_text_1, WS_VISIBLE | WS_CHILD | ES_READONLY | ES_LEFT, 50, 200 + offset * 26, 800, 25, hWnd,
(HMENU)IDC_TEXTBOX, hMainInstance, NULL);
SetWindowTextW(hLblOutput, input.data());
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

Adding Different Components in Tab Control Win32

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.

Changing background image inside WinApi

So I had this issue for a while now....and I can't seem to get around it...I managed to setup the start background which is relatively easy but I just can't figure out how to change it inside my application.
I have 2x Bitmaps imported with IDs:
IDI_BITMAP1
IDI_BITMAP2
I use bitmap1 for start background and I wanted to change the background when the user clicks on a button case ID_MENUBUTTON1: //change background
could any1 help me out with this? I really tried to understand it but I just can't. Here's how I created the start background:
case WM_CREATE:
/* Create start background image */
hBMP[0] = LoadBitmap(hInst, MAKEINTRESOURCE(IDI_BITMAP1));
hBitmap[0] = CreateWindowEx(0,
L"Static",
L"",
WS_CHILD | WS_VISIBLE | SS_BITMAP,
-10,
0,
0,
0,
hwnd,
(HMENU)IDI_BITMAP2,
hInst,
NULL);
SendMessage(hBitmap[0], STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBMP[0]);
I understand it's possible to do it using WM_PAINT but I'm not really sure how it would work with what I want, that's that the background only changes if the user clicks the button.
Code is in C++, using VS13
If I'm understanding your question correctly, you've already got the background set up correctly at startup, but you want to be able to change it at runtime.
If that's the case, you're more than halfway there. The way you set the background initially
SendMessage(hBitmap[0], STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBMP[0]);
is the same way that you change the background in the future. Just pass a different bitmap handle for the last parameter.
in this application you can change the background by a click.
compile the code below.
write your file name in textbox and press change background.
i use mingw 4.7.
you can only use .bmp images, if you want to use other images type you have to use GdiPluse.
#include <windows.h>
//variables
HWND hwnd01, label01;
HBITMAP hBitmap01 = NULL;
//functions
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "GUI01" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&wc);
hwnd01 = CreateWindow( wc.lpszClassName, TEXT("GUI01 Headline"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
150, 150, 330, 150, 0, 0, hInstance, 0);
while( GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch(msg)
{
case WM_CREATE:
{
CreateWindow(TEXT("button"), TEXT("Change Background"),
WS_VISIBLE | WS_CHILD ,
20, 50, 140, 25,
hwnd, (HMENU) 1, NULL, NULL);
CreateWindow(TEXT("button"), TEXT("Quit"),
WS_VISIBLE | WS_CHILD ,
190, 50, 80, 25,
hwnd, (HMENU) 2, NULL, NULL);
label01 = CreateWindow(TEXT("Edit"), TEXT("Label"),
WS_VISIBLE | WS_CHILD,
20, 10, 280, 25,
hwnd, (HMENU) 3, NULL, NULL);
break;
}
case WM_COMMAND:
{
if (LOWORD(wParam) == 1) {
TCHAR* string01 = new TCHAR[300];
GetWindowText(label01, string01, 300);
hBitmap01 = (HBITMAP)LoadImage(NULL, string01, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (hBitmap01==NULL)
MessageBox(NULL, "Error Loading Image.", "ERROR", MB_ICONWARNING | MB_DEFBUTTON2);
else
InvalidateRect(hwnd01, NULL, TRUE);
}
if (LOWORD(wParam) == 2) {
PostQuitMessage(0);
}
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps01;
HDC hdc01;
BITMAP bitmap01;
HDC hdcMem01;
HGDIOBJ oldBitmap01;
hdc01 = BeginPaint(hwnd01, &ps01);
hdcMem01 = CreateCompatibleDC(hdc01);
oldBitmap01 = SelectObject(hdcMem01, hBitmap01);
GetObject(hBitmap01, sizeof(bitmap01), &bitmap01);
BitBlt(hdc01, 0, 0, bitmap01.bmWidth, bitmap01.bmHeight, hdcMem01, 0, 0, SRCCOPY);
SelectObject(hdcMem01, oldBitmap01);
DeleteDC(hdcMem01);
EndPaint(hwnd01, &ps01);
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}

c++ winapi impossible to create 2 controls

I'm trying to create window that contain richedit control and listbox control,
the problem is that the second control which I create, doesn't show up.
I mean:
case WM_CREATE: // In main window procedure
{
/* Center the main window */
This->CenterWindow(hwnd);
/* Initialize the clients list */
This->InitListClients(hwnd);
/* Initialize the server log */
This->InitEditLog(hwnd);
return 0;
}
If InitListClients function will be first, only the listbox will show up,
if InitEditLog will be first, only the richedit will show up.
Here are the functions:
void ApostleServer::InitEditLog(HWND &_hwnd)
{
LoadLibrary(TEXT("Riched32.dll"));
hEditLog = CreateWindowEx(WS_EX_STATICEDGE, "richedit", "bla", WS_CHILD | WS_VISIBLE | ES_MULTILINE, 10, 10, 390, 310, _hwnd, NULL, (HINSTANCE)GetWindowLong(_hwnd, GWL_HINSTANCE), NULL);
}
void ApostleServer::InitListClients(HWND &_hwnd)
{
hListClients = CreateWindowEx(WS_EX_STATICEDGE, "listbox", "bla", WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 550, 20, 150, 150, _hwnd, NULL, (HINSTANCE)GetWindowLong(_hwnd, GWL_HINSTANCE), NULL);
}
I'm kinda newbie with winapi and I couldn't find solution for this problem.
Thanks.
EDIT:
As I commented, the cause of the problem is the use of class members.
Here is a whole code that I've wrote and has the same problem:
#include <Windows.h>
#include <stdlib.h>
class Server
{
public:
/* Fields */
MSG* msg;
WNDCLASSW* wc;
HWND hListClients;
HWND hEditLog;
/* Methods */
void InitEditLog(HWND &_hwnd)
{
LoadLibrary(TEXT("Riched32.dll"));
hEditLog = CreateWindowExW(WS_EX_STATICEDGE, L"richedit", L"Text", WS_CHILD | WS_VISIBLE | ES_MULTILINE, 10, 10, 390, 306, _hwnd, (HMENU)2, (HINSTANCE)GetWindowLong(_hwnd, GWL_HINSTANCE), NULL);
}
void InitListClients(HWND &_hwnd)
{
// Here I'm using hListClients class member, and that what cause the problem (I will see only the list on the window)
hListClients = CreateWindowExW(WS_EX_STATICEDGE, L"listbox", L"asd", WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 410, 10, 160, 306, _hwnd, (HMENU)1, (HINSTANCE)GetWindowLong(_hwnd, GWL_HINSTANCE), NULL);
// If I was only creating the listbox (without returning handler), I will see the listbox and the richedit.
}
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
Server* This = (Server*)GetWindowLongW(hwnd, GWL_USERDATA);
switch(msg)
{
case WM_CREATE:
{
/* Initialize the clients list */
This->InitListClients(hwnd); // Attention that I called this function first.
/* Initialize the server log */
This->InitEditLog(hwnd);
// If I would call this function first, I will see only the richedit.
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
Server(HINSTANCE &_hInstance)
{
msg = new MSG;
wc = new WNDCLASSW;
wc->style = CS_HREDRAW | CS_VREDRAW;
wc->cbClsExtra = 0;
wc->cbWndExtra = 0;
wc->lpszClassName = L"ApostleServer";
wc->hInstance = _hInstance;
wc->hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc->lpszMenuName = NULL;
wc->lpfnWndProc = WndProc;
wc->hCursor = LoadCursor(NULL, IDC_ARROW);
wc->hIcon = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassW(&(*wc));
CreateWindowW(wc->lpszClassName, L"Apostle Server", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 600, 400, 0, 0, _hInstance, 0);
while(GetMessage(&(*msg), NULL, 0, 0))
{
TranslateMessage(&(*msg));
DispatchMessage(&(*msg));
}
}
};
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
Server* srvr = new Server(hInstance);
return 0;
}
Problem solved by creating the controls on WM_CREATE message (but not to set control handlers!), and set the control handlers after the creation of the main window.
WM_CREATE message:
case WM_CREATE:
{
/* Center the main window */
This->CenterWindow(hwnd);
/* Initialize the clients list */
This->InitListClients(hwnd);
/* Initialize the server log */
This->InitEditLog(hwnd);
return 0;
}
After main window creation:
RegisterClassW(&(*wc));
hMainWindow = CreateWindowW(wc->lpszClassName, L"Apostle Server", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 600, 400, 0, 0, _hInstance, 0);
/* Set controls handlers */
hListClients = GetDlgItem(hMainWindow, IDC_LISTCLIENTS);
hEditLog = GetDlgItem(hMainWindow, IDC_EDITLOG);
InitEditLog and InitListClients functions:
void ApostleServer::InitEditLog(HWND &_hwnd)
{
LoadLibrary(TEXT("Riched32.dll"));
CreateWindowExW(WS_EX_STATICEDGE, L"richedit", L"Text", WS_CHILD | WS_VISIBLE | ES_MULTILINE, 10, 10, 390, 306, _hwnd, (HMENU)IDC_EDITLOG, (HINSTANCE)GetWindowLong(_hwnd, GWL_HINSTANCE), NULL);
}
void ApostleServer::InitListClients(HWND &_hwnd)
{
CreateWindowExW(WS_EX_STATICEDGE, L"listbox", L"asd", WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 410, 10, 160, 306, _hwnd, (HMENU)IDC_LISTCLIENTS, (HINSTANCE)GetWindowLong(_hwnd, GWL_HINSTANCE), NULL);
}

How to change the background Image of a window in win32?

How I can change the background image of a window at runtime in WIN32?
I have created the window using CreateWindow(). Then I have a LoadImage Button which opens a model dialog box (open file dialog)... Now I want to use the file name returned by this dialog box as the image of the window.
There is no native concept for the window background, you have to manually paint your image, usually in the WM_ERASEBKGND message. If you are using MFC you can check this article: http://www.codeproject.com/KB/graphics/picturewindow.aspx
A piece of sample code of what CoreyStup explains, in the window callback:
switch (message)
{
case WM_CREATE:
{
HBITMAP hImage = (HBITMAP)LoadImage(NULL, L"guibg.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HWND hImageView = CreateWindowEx(NULL, L"STATIC", NULL, SS_BITMAP | WS_VISIBLE | WS_CHILD, 0, 00, 500, 600, hWnd, (HMENU)IMAGE_VIEW, GetModuleHandle(NULL), NULL);
SendMessage(hImageView, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImage);
...
Credits also to fritzone who answered this on this similar thread: How to add picture box in win32 API using visual c++
NOTE: Use bitmaps generated by paint, otherwise LoadImage may return NULL without an error and the image will be invisible.
compile the code below.
write your file name in textbox and press change background.
i use mingw 4.7.
you can only use .bmp images, if you want to use other image you have to use GdiPluse.
#include <windows.h>
//variables
HWND hwnd01, label01;
HBITMAP hBitmap01 = NULL;
//functions
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "GUI01" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&wc);
hwnd01 = CreateWindow( wc.lpszClassName, TEXT("GUI01 Headline"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
150, 150, 330, 150, 0, 0, hInstance, 0);
while( GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch(msg)
{
case WM_CREATE:
{
CreateWindow(TEXT("button"), TEXT("Change Background"),
WS_VISIBLE | WS_CHILD ,
20, 50, 140, 25,
hwnd, (HMENU) 1, NULL, NULL);
CreateWindow(TEXT("button"), TEXT("Quit"),
WS_VISIBLE | WS_CHILD ,
190, 50, 80, 25,
hwnd, (HMENU) 2, NULL, NULL);
label01 = CreateWindow(TEXT("Edit"), TEXT("Label"),
WS_VISIBLE | WS_CHILD,
20, 10, 280, 25,
hwnd, (HMENU) 3, NULL, NULL);
break;
}
case WM_COMMAND:
{
if (LOWORD(wParam) == 1) {
TCHAR* string01 = new TCHAR[300];
GetWindowText(label01, string01, 300);
hBitmap01 = (HBITMAP)LoadImage(NULL, string01, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (hBitmap01==NULL)
MessageBox(NULL, "Error Loading Image.", "ERROR", MB_ICONWARNING | MB_DEFBUTTON2);
else
InvalidateRect(hwnd01, NULL, TRUE);
}
if (LOWORD(wParam) == 2) {
PostQuitMessage(0);
}
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps01;
HDC hdc01;
BITMAP bitmap01;
HDC hdcMem01;
HGDIOBJ oldBitmap01;
hdc01 = BeginPaint(hwnd01, &ps01);
hdcMem01 = CreateCompatibleDC(hdc01);
oldBitmap01 = SelectObject(hdcMem01, hBitmap01);
GetObject(hBitmap01, sizeof(bitmap01), &bitmap01);
BitBlt(hdc01, 0, 0, bitmap01.bmWidth, bitmap01.bmHeight, hdcMem01, 0, 0, SRCCOPY);
SelectObject(hdcMem01, oldBitmap01);
DeleteDC(hdcMem01);
EndPaint(hwnd01, &ps01);
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
A static control can have a background image (although its limited to bitmaps). You can put a child static control inside of your window (sized edge to edge) and set it as the background.
If you want fancy image handling like transparency, you'll need to do that manually.