c++ scrollbar to window - c++

hey, here im creating a window:
_hWnd = CreateWindowEx(
WS_EX_CLIENTEDGE, //dwExStyle
(LPCWSTR) _wndClass, //lpClassName
L"", //lpWindowName
WS_CHILD | WS_HSCROLL | WS_VSCROLL , //dwStyle
CW_USEDEFAULT, //X
CW_USEDEFAULT, //Y
200, //nWidth
150, //nHeight
hWndParent, //hWndParent
NULL, //hMenu
hInstance, //hInstance
NULL //lpParam
);
i added the scrollbars (WS_HSCROLL | WS_VSCROLL), but how can i control them?

I think you should refer to MSDN for more information, but here is some short part of code I wrote one day (just for you to start from something).
Generally, the idea is about handling specific messages in your WindowProcedure routine (WM_HSCROLL, WM_VSCROLL). The easiest way to evaluate new scroller position (I mean, the WinAPI way) is to use a specific SCROLLINFO structure. In the following chunk of code, SCROLLINFO si is used.
case WM_HSCROLL:
{
TEXTHANDLER * handler = ((TEXTHANDLER *)GetProp(hWnd, "TEXTHANDLER"));
// If user is trying to scroll outside
// of scroll range, we don't have to
// invalidate window
BOOL needInvalidation = TRUE;
if (handler->renderer->wordWrap)
{
return 0;
}
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hWnd, SB_HORZ, &si);
switch (LOWORD(wParam))
{
case SB_LINELEFT:
si.nPos -= 1;
if (si.nPos < 0)
{
si.nPos = 0;
needInvalidation = FALSE;
}
break;
case SB_LINERIGHT:
si.nPos += 1;
if (si.nPos > si.nMax)
{
si.nPos = si.nMax;
needInvalidation = FALSE;
}
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
}
si.fMask = SIF_POS;
SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
// Set new text renderer parameters
handler->renderer->xPos = si.nPos;
if (needInvalidation) InvalidateRect(hWnd, NULL, FALSE);
return 0;
}
case WM_VSCROLL:
{
TEXTHANDLER * handler = ((TEXTHANDLER *)GetProp(hWnd, "TEXTHANDLER"));
BOOL needInvalidation = TRUE;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hWnd, SB_VERT, &si);
switch (LOWORD(wParam))
{
case SB_LINEUP:
si.nPos -= 1;
if (si.nPos < 0)
{
si.nPos = 0;
needInvalidation = FALSE;
}
break;
case SB_LINEDOWN:
si.nPos += 1;
if (si.nPos > si.nMax)
{
si.nPos = si.nMax;
needInvalidation = FALSE;
}
break;
case SB_PAGEUP:
si.nPos -= handler->renderer->cyCount;
if (si.nPos < 0)
{
si.nPos = 0;
needInvalidation = FALSE;
}
break;
case SB_PAGEDOWN:
si.nPos += handler->renderer->cyCount;
if (si.nPos > si.nMax)
{
si.nPos = si.nMax;
needInvalidation = FALSE;
}
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
}
si.fMask = SIF_POS;
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
// Set new text renderer parameters
handler->renderer->yPos = si.nPos;
if (needInvalidation) InvalidateRect(hWnd, NULL, FALSE);
return 0;
}

Related

Scrollbar duplicate winapi c++

I am making a scrollbar for the first time and don't understand why I have a duplicate of my scrollbar when content is scrollable.
Here is how it looks, I don't know if you can see it good enough but here's a screenshot; one scrollbar is the thick gray one (the one a want to keep, looks more like the classic scrollbar), and one is a little gray rectangle to the left of it. Both of these scrollbars work and scroll my program.
:
But when content is not scrollable then it looks like this:
Here is my ScrollBar.cpp if this will help:
#include "ScrollBar.h"
ScrollBar::ScrollBar(){}
ScrollBar::~ScrollBar(){
RemoveWindowSubclass(hWnd, scrollProc, IDC_STATIC);
delete con;
}
ScrollBar::ScrollBar(HWND hWnd, HINSTANCE hInst) : hWnd(hWnd),hInst(hInst) {
this->hWndParent = GetParent(hWnd);
con = new Controls(hWndParent);
}
void ScrollBar::createScroll(int x, int y, int w, int h, bool stickRight, bool stickBottom) {
this->x = x;
this->y = y;
this->width = w;
this->height = h;
hScroll = con->create("scrollbar", WS_VISIBLE | WS_CHILD | SBS_VERT, x, y, width, height,"scrollBar","",0, stickRight, stickBottom);
//Creates like this
/// CreateWindowEx(0, type, text, styles, cData.x, cData.y, cData.width, cData.height, hWnd, (HMENU)id, NULL, (LPVOID)lParam);
SetWindowSubclass(hScroll, scrollProc, IDC_STATIC, (DWORD_PTR)this);
SetWindowSubclass(hWndParent, scrollProc, IDC_STATIC, (DWORD_PTR)this);
con->setEventListener();
}
void ScrollBar::setScrollRange(int scrollMaxHeight) {
scrollMaxH = scrollMaxHeight == -1 ? scrollMaxH : scrollMaxHeight;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE;
si.nMin = 0;
si.nMax = scrollMaxH;
SetScrollInfo(hScroll, SB_VERT, &si, false);
}
void ScrollBar::setScrollSize() {
SCROLLINFO oSi = { 0 };
oSi.cbSize = sizeof(oSi);
oSi.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
GetScrollInfo(hScroll, SB_VERT, &oSi);
RECT rect = GetLocalCoordinates(hWnd);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(si);
si.fMask = SIF_PAGE;
si.nPage = height;
SetScrollInfo(hScroll, SB_VERT, &si, TRUE);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
GetScrollInfo(hScroll, SB_VERT, &si);
int res = oSi.nPos - si.nPos;
if (res > 0) {
setChildScrollOffset(hWnd, res);
}
}
void ScrollBar::handleVScroll(int action,int thum) {
int nPos;
int nOldPos;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
GetScrollInfo(hScroll, SB_VERT, &si);
nOldPos = si.nPos;
switch (action)
{
case SB_TOP:
nPos = si.nMin;
break;
case SB_BOTTOM:
nPos = si.nMax;
break;
case SB_LINEUP:
nPos = si.nPos - 30;
break;
case SB_LINEDOWN:
nPos = si.nPos + 30;
break;
case SB_PAGEUP:
nPos = si.nPos - 30;
break;
case SB_PAGEDOWN:
nPos = si.nPos + 30;
break;
case SB_THUMBTRACK:
nPos = thum;
break;
default:
nPos = si.nPos;
break;
}
SetScrollPos(hScroll, SB_VERT, nPos, TRUE);
nPos = GetScrollPos(hScroll, SB_VERT);
if (nOldPos != nPos) {
ScrollWindowEx(hWnd, 0, nOldPos - nPos, NULL, NULL, NULL, NULL,
SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN);
UpdateWindow(hWnd);
setChildScrollOffset(hWnd, nOldPos - nPos);
}
}
void ScrollBar::setChildScrollOffset(HWND h, int offset) {
Controls* childsCon = reinterpret_cast<Controls*>(GetWindowLongPtr(h, GWLP_USERDATA));
for (auto it : childsCon->sControls)
{
childsCon->getControlItem(it.first)->y += offset;
}
}
LRESULT CALLBACK ScrollBar::scrollProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
ScrollBar* lpData = (ScrollBar*)dwRefData;
HDC hdc = (HDC)wParam;
PAINTSTRUCT ps;
HBRUSH hbrBackground;
switch (uMsg) {
case WM_SIZE:
{
lpData->setScrollSize();
break;
}
case WM_VSCROLL:
{
lpData->handleVScroll(LOWORD(wParam), HIWORD(wParam));
return true;
}
case WM_MOUSEWHEEL:
{
short zDelta = (short)(0xFFFF & (wParam >> 16));
if (zDelta > 0)
PostMessage(lpData->hScroll, WM_VSCROLL, SB_LINEUP, NULL);
else
PostMessage(lpData->hScroll, WM_VSCROLL, SB_LINEDOWN, NULL);
return 0;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
I am blind and so can't see your screen shots but does your parent window have WS_VSCROLL? (you don't show the styles associated with that window). If so remove it, the WS_VSCROLL adds a scrollbar that is not control-based to the window's non-client area.
You need to distinguish between Standard Scroll Bars and Scroll Bar Controls. A scroll bar control is a control window that belongs to the SCROLLBAR window class.
For more details about how to Create Scroll Bar, I suggest you could refer to the Doc: How to Create Scroll Bars
According to the code, you want to create a Scroll Bar Controls. So you should creates a window without standard horizontal and vertical scroll bars and then use the CreateWindowEx function to create a scroll bar by specifying the SCROLLBAR window class.
Could you please show a minimal, reproducible sample?

how to scroll a painted region?

I want to scroll a painted region. I use the CS_OWNDC style with WS_HSCROLL | WS_VSCROLL.My WM_PAINT block draws a line outgoing from the visible region (I suspect that I need to increase the buffer somehow).How can I make this?Please give me an example
PS. Block of code WM_HSCROLL incorrect working i don't know how to fix this.
My code:
// C++ WINAPI LEARN
#include <Windows.h>
WNDCLASS windowClass;
HDC hdc;
PAINTSTRUCT ps;
int hScroll, vScroll;
bool first = true;
LRESULT CALLBACK WndProc(HWND hWindow, UINT typeMessage, WPARAM wParam, LPARAM lParam)
{
switch (typeMessage)
{
case WM_PAINT:
hdc = BeginPaint(hWindow, &ps);
//HERE ANYTHING DRAW CODE (words, line any figure)
//(draw line out visible region)
if (first == true)
{
first = false;
MoveToEx(hdc, 5, 5, NULL);
LineTo(hdc, 900, 500);
}
EndPaint(hWindow, &ps);
break;
case WM_HSCROLL:
//SCOLLBAR NOT JOB!
if (LOWORD(wParam) == SB_LINERIGHT) // this block not job!!!
{
hScroll++;
}
else if (LOWORD(wParam) == SB_LINELEFT) // this block not job!!!
{
hScroll--;
}
else if (LOWORD(wParam) == SB_THUMBTRACK)
{
hScroll=HIWORD(wParam);
}
SetScrollPos(hWindow, SB_HORZ, hScroll, FALSE);
ScrollWindow(hWindow, hScroll, vScroll, NULL, NULL);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
}
return DefWindowProc(hWindow, typeMessage, wParam, lParam);
}
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstanse, LPSTR LpszCmdParam, int nCmdShow)
{
//CREATE WINDOW
windowClass = { 0 };
windowClass.lpszClassName = "ScrollTest";
windowClass.hInstance = hInstance;
windowClass.lpfnWndProc = WndProc;
windowClass.style = CS_OWNDC;
//REGISTER CLASS
RegisterClass(&windowClass);
HWND myWindow = ::CreateWindowEx(
0,
"ScrollTest",
"Scroll Paint",
WS_VISIBLE | WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
100, 100,
500, 500,
NULL,
0,
hInstance,
0
);
//TRANSLATE MESSAGE
for (MSG windowMessage; GetMessage(&windowMessage, NULL, 0, 0);)
{
DispatchMessage(&windowMessage); //TRANSLATE MESSAGES
TranslateMessage(&windowMessage); //TRANSLATE KEYS
}
}
Based on the demo, I modified part of the code and it works normally.
Some code:
LRESULT CALLBACK MyTextWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm;
SCROLLINFO si;
// These variables are required to display text.
static int xChar; // horizontal scrolling unit
static int yChar; // vertical scrolling unit
static int xPos; // current horizontal scrolling position
static int yPos; // current vertical scrolling position
int x, y; // horizontal and vertical coordinates
// Create an array of lines to display.
switch (uMsg)
{
case WM_CREATE:
// Get the handle to the client area's device context.
hdc = GetDC(hwnd);
// Extract font dimensions from the text metrics.
GetTextMetrics(hdc, &tm);
xChar = tm.tmAveCharWidth;
yChar = tm.tmHeight + tm.tmExternalLeading;
// Free the device context.
ReleaseDC(hwnd, hdc);
return 0;
case WM_HSCROLL:
// Get all the vertial scroll bar information.
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
// Save the position for comparison later on.
GetScrollInfo(hwnd, SB_HORZ, &si);
xPos = si.nPos;
switch (LOWORD(wParam))
{
// User clicked the left arrow.
case SB_LINELEFT:
si.nPos -= 1;
break;
// User clicked the right arrow.
case SB_LINERIGHT:
si.nPos += 1;
break;
// User clicked the scroll bar shaft left of the scroll box.
case SB_PAGELEFT:
si.nPos -= si.nPage;
break;
// User clicked the scroll bar shaft right of the scroll box.
case SB_PAGERIGHT:
si.nPos += si.nPage;
break;
// User dragged the scroll box.
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
default:
break;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.
si.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
GetScrollInfo(hwnd, SB_HORZ, &si);
// If the position has changed, scroll the window.
if (si.nPos != xPos)
{
ScrollWindow(hwnd, xChar * (xPos - si.nPos), 0, NULL, NULL);
}
return 0;
case WM_VSCROLL:
// Get all the vertial scroll bar information.
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, SB_VERT, &si);
// Save the position for comparison later on.
yPos = si.nPos;
switch (LOWORD(wParam))
{
// User clicked the HOME keyboard key.
case SB_TOP:
si.nPos = si.nMin;
break;
// User clicked the END keyboard key.
case SB_BOTTOM:
si.nPos = si.nMax;
break;
// User clicked the top arrow.
case SB_LINEUP:
si.nPos -= 1;
break;
// User clicked the bottom arrow.
case SB_LINEDOWN:
si.nPos += 1;
break;
// User clicked the scroll bar shaft above the scroll box.
case SB_PAGEUP:
si.nPos -= si.nPage;
break;
// User clicked the scroll bar shaft below the scroll box.
case SB_PAGEDOWN:
si.nPos += si.nPage;
break;
// User dragged the scroll box.
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
default:
break;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.
si.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
GetScrollInfo(hwnd, SB_VERT, &si);
// If the position has changed, scroll window and update it.
if (si.nPos != yPos)
{
ScrollWindow(hwnd, 0, yChar * (yPos - si.nPos), NULL, NULL);
UpdateWindow(hwnd);
}
return 0;
case WM_PAINT:
// Prepare the window for painting.
hdc = BeginPaint(hwnd, &ps);
// Get vertical scroll bar position.
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
GetScrollInfo(hwnd, SB_VERT, &si);
yPos = si.nPos;
// Get horizontal scroll bar position.
GetScrollInfo(hwnd, SB_HORZ, &si);
xPos = si.nPos;
x = xChar * (1 - xPos);
y = yChar * (1 - yPos);
MoveToEx(hdc, 5 + x, 5 + y, NULL);
LineTo(hdc, 900 + x, 500 + y);
// Indicate that painting is finished.
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Debug:

Link SCROLLINFO to my scrollbar — WINAPI C++

I have a window with a working default scrollbar.
Now I'd like to link all the code I have to the custom scrollbar created via CreateWindow
Here is the procedure that
LRESULT CALLBACK InfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR) {
switch (msg) {
case WM_COMMAND:
if(wParam == ID_CLOSECROSS){
if(IsWindowVisible(aboutPanel)) SwitchToWindow(aboutPanel); else SwitchToWindow(howToPanel);
}
break;
case WM_MOUSEWHEEL:{
if((short)HIWORD(wParam) > 0){
SendMessage(hwnd, WM_VSCROLL, VK_UP, 0);
}else{
SendMessage(hwnd, WM_VSCROLL, VK_DOWN, 0);
}
break;
}
case WM_VSCROLL:{
RECT hrc; GetClientRect(hwnd, &hrc);
SCROLLINFO si;
si.cbSize = sizeof (si);
si.fMask = SIF_ALL;
GetScrollInfo (hwnd, SB_VERT, &si);
int pos;
yPos = si.nPos;
switch (LOWORD (wParam)){
case SB_TOP: si.nPos = si.nMin; break;
case SB_BOTTOM: si.nPos = si.nMax; break;
case SB_LINEUP: si.nPos -= 1; break;
case SB_LINEDOWN: si.nPos += 1; break;
case VK_UP: si.nPos -= 20; break;
case VK_DOWN: si.nPos += 20; break;
case SB_PAGEUP: si.nPos -= si.nPage; break;
case SB_PAGEDOWN: si.nPos += si.nPage; break;
case SB_THUMBTRACK: si.nPos = si.nTrackPos; pos = HIWORD(wParam); break;
}
si.fMask = SIF_POS;
SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
GetScrollInfo (hwnd, SB_VERT, &si);
if (si.nPos != yPos){
ScrollWindow(centerHowToPanel, 0, scrollSpeed*(yPos-si.nPos), NULL, &hrc);
// here
UpdateWindow (centerHowToPanel);
}
return 0;
}
case WM_SIZE:{
RECT hrc = { 0 };
GetClientRect(hwnd, &hrc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = 30 * 99 + 21;
si.nPage = (hrc.bottom - hrc.top);
si.nPos = yPos;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, true);
// here
}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Here is my scrollbar I create in WinMain:
howToScroll = CreateWindow(L"SCROLLBAR", NULL, WS_VISIBLE|WS_CHILD|SBS_VERT, 0, 0, 0, 0, howToPanel, NULL, NULL, NULL);
If I replace each (Get|Set)ScrollInfo(hwnd, ...) with (Get|Set)ScrollInfo(howToScroll, ...) in the procedure it almost works. I can use scrollbar's arrows, but can't drag it. Also mouse wheel doesn't seem to change custom scrollbar's position.
I tried to use SetScrollPos(howToScroll, SB_CTL, yPos, 1); in the procedure at places where is commented 'here' word but it doesn't help either.
Also when resizing my custom scrollbar doesn't change it's size as default one does.
Just a quick demostration how it works with the default scrollbar.
                                                             
And here how it works with the custom one.
                                                             
Edit 1
I managed to make it scroll!
LRESULT CALLBACK InfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR) {
switch (msg) {
case WM_COMMAND:
if(wParam == ID_CLOSECROSS){
if(IsWindowVisible(aboutPanel)) SwitchToWindow(aboutPanel); else SwitchToWindow(howToPanel);
}
break;
case WM_MOUSEWHEEL:{
if((short)HIWORD(wParam) > 0){
SendMessage(hwnd, WM_VSCROLL, VK_UP, 0);
}else{
SendMessage(hwnd, WM_VSCROLL, VK_DOWN, 0);
}
break;
}
case WM_VSCROLL:{
RECT hrc; GetClientRect(hwnd, &hrc);
SCROLLINFO si;
si.cbSize = sizeof (si);
si.fMask = SIF_ALL;
GetScrollInfo (howToScroll, SB_VERT, &si);
int pos;
yPos = si.nPos;
switch (LOWORD (wParam)){
case SB_TOP: si.nPos = si.nMin; break;
case SB_BOTTOM: si.nPos = si.nMax; break;
case SB_LINEUP: si.nPos -= 1; break;
case SB_LINEDOWN: si.nPos += 1; break;
case VK_UP: si.nPos -= 20; break;
case VK_DOWN: si.nPos += 20; break;
case SB_PAGEUP: si.nPos -= si.nPage; break;
case SB_PAGEDOWN: si.nPos += si.nPage; break;
case SB_THUMBTRACK: si.nPos = HIWORD(wParam); break;
}
SetScrollPos(howToScroll, SB_CTL, yPos, true);
si.fMask = SIF_POS;
SetScrollInfo (howToScroll, SB_VERT, &si, TRUE);
GetScrollInfo (howToScroll, SB_VERT, &si);
if (si.nPos != yPos){
ScrollWindow(centerHowToPanel, 0, scrollSpeed*(yPos-si.nPos), NULL, &hrc);
UpdateWindow (centerHowToPanel);
}
return 0;
}
case WM_SIZE:{
RECT hrc = { 0 };
GetClientRect(hwnd, &hrc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = hrc.bottom+40;
SetScrollRange(howToScroll, SB_CTL, 0, hrc.bottom+40, TRUE);
si.nPage = (hrc.bottom - hrc.top);
si.nPos = yPos;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, true);
}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Now it seems like the head of the scroll is not big enough so it doesn't goes to the end of the bar.
                                                             
The only thing I need to manage -- the size of the scroll.
Edit 2
If I replace every single SB_VERT with SB_CTL it works as should! Thanks #JonathanPotter for that.

Winapi creating a tab menu

I'm trying to make a tab menu, it should have an active state with different styling when a tab is selected, the problem is when starting the program the tabs get selected after two clicks. When displaying the contents of the buttonPressed array in WM_DRAWITEM even, is a delay in changing the value of the active state
case WM_CREATE:
buttonPressed[0] = 1;
buttonPressed[1] = 0;
buttonPressed[2] = 0;
buttonPressed[3] = 0;
button1 = CreateWindow("BUTTON","Overview",WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 0,0,200,50, hwnd , (HMENU) 1 , NULL, NULL);
button2 = CreateWindow("BUTTON","Send",WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 200,0,200,50, hwnd , (HMENU) 2, NULL, NULL);
button3 = CreateWindow("BUTTON","Receive",WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 400,0,200,50, hwnd , (HMENU) 3, NULL, NULL);
button4 = CreateWindow("BUTTON","Transactions",WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 600,0,200,50, hwnd , (HMENU) 4, NULL, NULL);
break;
case WM_DRAWITEM:
{
if((wParam == 1 && buttonPressed[0] == 1) || (wParam == 2 && buttonPressed[1] == 1) || (wParam == 3 && buttonPressed[2] == 1) || (wParam == 4 && buttonPressed[3] == 1) ){
FillRect(Item->hDC, &Item->rcItem, CreateSolidBrush(0x6C6C6C) );
SetBkMode(Item->hDC, 0x6C6C6C);
SetTextColor(Item->hDC, RGB(0,0,255));
}else{
FillRect(Item->hDC, &Item->rcItem, CreateSolidBrush(0x6C6C6C) );
SetBkMode(Item->hDC, 0x6C6C6C);
SetTextColor(Item->hDC, RGB(255,255,255));
}
int len;
len = GetWindowTextLength(Item->hwndItem);
LPSTR lpBuff;
lpBuff = new char[len+1];
GetWindowTextA(Item->hwndItem, lpBuff, len+1);
DrawTextA(Item->hDC, lpBuff, len, &Item->rcItem, DT_CENTER | DT_VCENTER | DT_WORDBREAK);
}
break;
case WM_COMMAND:
switch ( LOWORD(wParam) ){
case 1:
buttonPressed[0] = 1;
buttonPressed[1] = 0;
buttonPressed[2] = 0;
buttonPressed[3] = 0;
break;
case 2:
buttonPressed[0] = 0;
buttonPressed[1] = 1;
buttonPressed[2] = 0;
buttonPressed[3] = 0;
break;
case 3:
buttonPressed[0] = 0;
buttonPressed[1] = 0;
buttonPressed[2] = 1;
buttonPressed[3] = 0;
break;
case 4:
buttonPressed[0] = 0;
buttonPressed[1] = 0;
buttonPressed[2] = 0;
buttonPressed[3] = 1;
break;
}
break;
Your WM_COMMAND handler is not triggering any repaints of the buttons after changing the states in the buttonPressed array. Call InvalidateRect() on each button to trigger a new WM_PAINT message for each button.
Also, you are making the same mistakes in this code (leaking memory and resources, invalid parameter to SetBkMode(), etc) that you made in your earlier code. You are using very few portions of code from the accepted answer, and you are ignoring all of the mistakes that answer pointed out to you.
Try something more like this:
struct ButtonInfo {
HWND Wnd;
bool Pressed;
};
ButtonInfo button[4];
...
case WM_CREATE:
{
LPCTSTR captions[4] = {TEXT("Overview"), TEXT("Send"), TEXT("Receive"), TEXT("Transactions")};
for (int i = 0; i < 4; ++i) {
button[i].Pressed = false;
button[i].Wnd = CreateWindow(TEXT("BUTTON"), captions[i], WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, i * 200, 0, 200, 50, hwnd, reinterpret_cast<HMENU>(i+1), NULL, NULL);
}
button[0].Pressed = true;
}
break;
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT Item = reinterpret_cast<LPDRAWITEMSTRUCT>(lParam);
HFONT hFont = CreateFont(17, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, TEXT("Arial Black"));
HFONT hOldFont = (HFONT) SelectObject(Item->hDC, hFont);
HBRUSH hBrush = CreateSolidBrush(RGB(0x6C, 0x6C, 0x6C));
FillRect(Item->hDC, &Item->rcItem, hBrush);
DeleteObject(hBrush);
SetBkMode(Item->hDC, TRANSPARENT);
int buttonID = wParam;
if (button[buttonID - 1].Pressed) {
SetTextColor(Item->hDC, RGB(0,0,255));
} else {
SetTextColor(Item->hDC, RGB(255,255,255));
}
int len = GetWindowTextLength(Item->hwndItem) + 1;
LPTSTR lpBuff = new TCHAR[len];
len = GetWindowText(Item->hwndItem, lpBuff, len);
DrawText(Item->hDC, lpBuff, len, &Item->rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
delete[] lpBuff;
SelectObject(Item->hDC, hOldFont);
DeleteObject(hFont);
}
break;
case WM_COMMAND:
{
int buttonID = LOWORD(wParam);
int buttonIdx = buttonID - 1;
for (int i = 0; i < 4; ++i) {
button[i].Pressed = (i == buttonIdx);
InvalidateRect(button[i].Wnd, NULL, TRUE);
}
switch (buttonID) {
// perform whatever actions you need based on which button was clicked....
}
}
break;

C++ & WinApi - Text output from the keyboard to the screen in the created window

I do not see where the error is. The window display only the first character you type, and the other is not. Although the cursor moves when typing. And when minimize and maximize the window, the text becomes visible.
#include <windows.h>
#define IDI_MYICON 201
#define IDR_MYMENU 201
#define ID_FIO_FirstName 4002
#define ID_FIO_SecondName 4003
#define ID_FIO_Name 4001
#define ID_INFO_DeveloperStatus 9002
#define ID_INFO_LearningForm 9003
#define ID_INFO_Group 9004
#define BUFSIZE 6665535
#define SHIFTED 0x8000
const char g_szClassName[] = "MFW";
LONG APIENTRY WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
TEXTMETRIC tm;
static DWORD dwCharX;
static DWORD dwCharY;
static DWORD dwClientX;
static DWORD dwClientY;
static DWORD dwLineLen;
static int nCaretPosX = 0;
static int nCaretPosY = 0;
static int nCharWidth = 0;
static int cch = 0;
static int nCurChar = 0;
static PTCHAR pchInputBuf;
int i, j;
int cCR = 0;
int nCRIndex = 0;
int nVirtKey;
TCHAR szBuf[128];
TCHAR ch;
PAINTSTRUCT ps;
RECT rc;
SIZE sz;
COLORREF crPrevText;
COLORREF crPrevBk;
switch(Message)
{
case WM_CREATE:
{
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);
dwCharX = tm.tmAveCharWidth;
dwCharY = tm.tmHeight;
pchInputBuf=(LPTSTR)GlobalAlloc(GPTR,BUFSIZE*sizeof(TCHAR));
HMENU hMenu, hSubMenu;
HICON hIcon, hIconSm;
hMenu = CreateMenu();
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_FIO_FirstName, "FirstName");
AppendMenu(hSubMenu, MF_STRING, ID_FIO_SecondName, "SecondName");
AppendMenu(hSubMenu, MF_STRING, ID_FIO_Name, "Name");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&FIO");
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_INFO_DeveloperStatus, "DeveloperStatus");
AppendMenu(hSubMenu, MF_STRING, ID_INFO_LearningForm, "LearningForm");
AppendMenu(hSubMenu, MF_STRING, ID_INFO_Group, "Group");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&INFO");
SetMenu(hwnd, hMenu);
hIcon =(HICON)LoadImage(NULL, "my_icon.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
if(hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
else
MessageBox(hwnd, "Could not load large icon!","Error", MB_OK | MB_ICONERROR);
hIconSm =(HICON)LoadImage(NULL, "my_icon.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
if(hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
else
MessageBox(hwnd, "Could not load small icon!","Error", MB_OK | MB_ICONERROR);
break;
}
break;
case WM_PAINT:
if (cch==0)break;
hdc=BeginPaint(hwnd,&ps);
HideCaret(hwnd);
SetRect(&rc, 0, 0, dwLineLen, dwClientY);
DrawText(hdc, pchInputBuf,-1,&rc,DT_NOCLIP);
ShowCaret(hwnd);
EndPaint(hwnd, &ps);
break;
case WM_SIZE:
dwClientX=LOWORD(lParam);
dwClientY = HIWORD(lParam);
dwLineLen=dwClientX-dwCharX;
break;
case WM_SETFOCUS:
CreateCaret(hwnd, (HBITMAP) 0,3, dwCharY);
SetCaretPos(nCaretPosX,nCaretPosY*dwCharY);
ShowCaret(hwnd);
break;
case WM_KILLFOCUS:
HideCaret(hwnd);
DestroyCaret();
break;
case WM_CHAR:
if (cch > BUFSIZE-5)
{
pchInputBuf[cch] = 0x00;
SendMessage(hwnd,WM_PAINT, 0, 0);
}
switch (wParam)
{
case 0x08:
case 0x0A:
case 0x1B:
MessageBeep(0xFFFFFFFF);
return 0;
case 0x09:
for (i = 0; i < 4; i++)
SendMessage(hwnd, WM_CHAR, 0x20, 0);
return 0;
case 0x0D:
pchInputBuf[cch++] = 0x0D;
nCaretPosX = 0;
nCaretPosY += 1;
break;
default:
ch =(TCHAR)wParam;
HideCaret(hwnd);
hdc = GetDC(hwnd);
GetCharWidth32(hdc,wParam,wParam,&nCharWidth);
DrawText(hdc, pchInputBuf,-1,&rc, DT_NOCLIP);
ReleaseDC(hwnd, hdc);
pchInputBuf[cch++] = ch;
nCaretPosX += nCharWidth;
if ((DWORD) nCaretPosX > dwLineLen)
{
nCaretPosX = 0;
pchInputBuf[cch++] = 0x0D;
++nCaretPosY;
}
nCurChar = cch;
ShowCaret(hwnd);
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;
case WM_KEYDOWN:
switch (wParam)
{
case VK_LEFT:
if (nCaretPosX > 0)
{
HideCaret(hwnd);
ch = pchInputBuf[--nCurChar];
hdc = GetDC(hwnd);
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwnd, hdc);
nCaretPosX = max(nCaretPosX - nCharWidth, 0);
ShowCaret(hwnd);
} break;
case VK_RIGHT:
if (nCurChar > cch) {
HideCaret(hwnd);
ch = pchInputBuf[nCurChar];
if (ch == 0x0D) {
nCaretPosX = 0;
nCaretPosY++;
}
else {
hdc = GetDC(hwnd);
nVirtKey = GetKeyState(VK_SHIFT);
if (nVirtKey & SHIFTED) {
crPrevText = SetTextColor(hdc,RGB(255, 255, 255));
crPrevBk = SetBkColor(hdc,RGB(0,0,0));
TextOut(hdc, nCaretPosX,nCaretPosY * dwCharY,&ch,1);
SetTextColor(hdc, crPrevText);
SetBkColor(hdc, crPrevBk);
}
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwnd, hdc);
nCaretPosX = nCaretPosX + nCharWidth;
}
nCurChar++;
ShowCaret(hwnd);
break;
}
break;
case VK_HOME:
nCaretPosX = nCaretPosY = 0;
nCurChar = 0;
break;
case VK_END:
for (i=0; i < cch; i++)
{
if (pchInputBuf[i] == 0x0D) {
cCR++;
nCRIndex = i + 1;
}
}
nCaretPosY = cCR;
for (i = nCRIndex, j = 0; i < cch; i++, j++)
szBuf[j] = pchInputBuf[i];
szBuf[j] = TEXT('\0');
hdc = GetDC(hwnd);
GetTextExtentPoint32(hdc,szBuf,lstrlen(szBuf),&sz);
nCaretPosX = sz.cx;
ReleaseDC(hwnd, hdc);
nCurChar = cch;
break;
default:
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FIO_FirstName:
MessageBox(hwnd, "F-3", "FirstName", 0);
break;
case ID_FIO_SecondName:
MessageBox(hwnd, "B-2", "SecondName", 0);
break;
case ID_FIO_Name:
MessageBox(hwnd, "A-1", "Name", 0);
break;
case ID_INFO_DeveloperStatus:
MessageBox(hwnd, "S", "DeveloperStatus", 0);
break;
case ID_INFO_LearningForm:
MessageBox(hwnd, "FORM", "LearningForm", 0);
break;
case ID_INFO_Group:
MessageBox(hwnd, "GR", "Group", 0);
break;
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
GlobalFree((HGLOBAL) pchInputBuf);
UnregisterHotKey(hwnd, 0xAAAA);
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)
{
HWND hwnd;
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(GetModuleHandle(NULL),MAKEINTRESOURCE(IDI_MYICON));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MYMENU);
wc.lpszClassName = g_szClassName;
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(IDI_MYICON),IMAGE_ICON,16,16, 0);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd = CreateWindowEx(
WS_EX_OVERLAPPEDWINDOW,
g_szClassName,
"QWERTY",
WS_OVERLAPPEDWINDOW,
200, 100, 800, 600,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG Msg;
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
You use WM_PAINT incorrectly. You shouldn't send it manually. You need to call InvalidateRect(hwnd, NULL, true); instead of SendMessage(hwnd,WM_PAINT, 0, 0); and it will post a WM_PAINT message to your WndProc correctly.
When you minimize/maximize the window, Windows call internally something like InvalidateRect(hwnd, NULL, true);
Addition by manuell:
Also always call BeginPaint/EndPaint in WM_PAINT handler (you shouldn't break before calling BeginPaint/EndPaint).