How do I make a timer work in c++ WM_PAINT? I'm trying to "print" it via Wm_Paint, because at the moment I don't know another method of adding a timer, googling didn't help.
Here's what I have declared in CALLBACK:
TCHAR s[10], str[20] = _T("Seconds: ");
static int t;
case WM_CREATE:
SetTimer(hwnd, 1, 1000, NULL);
And here's how I try to draw the timer:
hdc = BeginPaint(hwnd, &ps);
hBrush = CreateSolidBrush(g_color);
hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
holdPen = HPEN(SelectObject(hdc, hPen));
holdBrush = (HBRUSH)SelectObject(hdc, hBrush);
_tcscat(str + 9, _itot(t, s, 10));
TextOut(hdc, 10, 300, str, _tcsclen(str));
SelectObject(hdc, holdBrush);
SelectObject(hdc, holdPen);
DeleteObject(hPen);
DeleteObject(hBrush);
EndPaint(hwnd, &ps);
So far, it just prints out "Seconds: 0" and stops updating.
Ok so, for me to make it work, I've had to create a WM_TIMER case in my CALLBACK function, in the end it looked like this:
//code above
case WM_TIMER:
t++;
InvalidateRect(hwnd, NULL, TRUE);
break;
//code below
Related
I have a couple of issues with this function:
The text turns into gibberish if I move the window a bit, or if I switch it to another monitor (Which has a different scaling, by the way)
A smaller version of the text appears before the initial text if I make a call to CreateFont and SelectObject before printing the text
Am I doing it right? Maybe there's too many calls inside the WM_PAINT case? I'm not sure how else it could be done
My code:
case(WM_PAINT):
{
HDC hDC = GetWindowDC(Window);
RECT lpRect;
GetClientRect(Window,
&lpRect
);
SetTextColor(hDC, RGB(0, 0, 0));
SetBkMode(hDC, TRANSPARENT);
DrawTextW(hDC,
L"Loading...",
-1,
&lpRect,
(DT_SINGLELINE | DT_TOP | DT_VCENTER | DT_NOCLIP)
);
DeleteDC(hDC);
break;
}
case(WM_ERASEBKGND):
{
HDC hDC = GetWindowDC(Window);
RECT lpRect;
GetClientRect(Window, &lpRect);
HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
FillRect(hDC, &lpRect, hBrush);
DeleteObject(hBrush);
break;
}
In WM_PAINT, you must call BeginPaint() and EndPaint(). It's this way you obtain the device context. If you don't call EndPaint() the rect is not validated.
Can somebody explain how to draw text on a bitmap in memory?
I have the following code but i can't figure out how to do it.
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HDC buffDC = CreateCompatibleDC(hdc);
SelectObject(buffDC, hFnt);
SetTextColor(buffDC, RGB(1, 1, 1));
SetBkColor(buffDC, RGB(0, 255, 0));
RECT rc;
GetClientRect(hWnd, &rc);
HBITMAP buffBitmap = CreateCompatibleBitmap(buffDC, rc.right, rc.bottom);
int savedDC = SaveDC(buffDC);
HBRUSH hBrush = CreateSolidBrush(RGB(0, 255, 0));
FillRect(buffDC, &rc, hBrush);
DeleteObject(hBrush);
//This is the part where i would like to draw to the bitmap
TextOutA(buffDC, 0, 0, "Hello", 6);
SelectObject(buffDC, buffBitmap);
BitBlt(hdc, 0, 0, rc.right, rc.bottom, buffDC, 0, 0, SRCCOPY);
RestoreDC(buffDC, savedDC);
DeleteObject(buffBitmap);
DeleteDC(buffDC);
EndPaint(hWnd, &ps);
break;
}
I have seen a lot of different solutions mostly using MFC, however I would like to avoid that approach if possible.
EDIT: I have checked the other already asked questions but I couldn't find one wich would cover this without MFC.
My original problem was that I'm using a timer to call RedrawWindow and update the position of the text and make a sort of scrolling text which moves from right to left.
When I was in the testing process I have noticed that on some machines the app runs with up to 25% CPU usage and on some others it uses < 1%. I have tested the app one two machines with exactly the same specs running Windows 7 and the app ran on one with ~10% and the other with 0%.
(By the way my timer is getting called every 33ms and RedrawWindow uses RDW_UPDATENOW and I didn't handle the WM_ERASEBKGND message either :P
Since WM_TIMER (as far as I know) is a low priority message I was not concerned about the timer causeing the issue with the CPU usage.)
I satrted to think that maybe I should be using a bitmap and BitBlt it to the screen rather than just simply drawing to the dc and updating the x coordinate every time I repaint the screen.
Thanks
Before you can draw onto a bitmap, you have to select it into a memory device context.
Move SelectObject(buffDC, buffBitmap); before the first call to a drawing function, but usually as soon as possible after you created the bitmap.
In your sample code it appears suitable to insert it after the SaveDC() call so the original bitmap will be restored later when you call RestoreDC():
int savedDC = SaveDC(buffDC);
SelectObject(buffDC, buffBitmap);
As commenters noted, CreateCompatibleBitmap(buffDC, rc.right, rc.bottom) should be changed to CreateCompatibleBitmap(hdc, rc.right, rc.bottom).
From the reference of CreateCompatibleBitmap():
When a memory device context is created, it initially has a 1-by-1
monochrome bitmap selected into it. If this memory device context is
used in CreateCompatibleBitmap, the bitmap that is created is a
monochrome bitmap. To create a color bitmap, use the HDC that was used
to create the memory device context
Finally a suggestion: If you just need a temporary bitmap (as in your sample code), there is a more efficient API available since Windows Vista. It is called the buffered paint API. MSDN does not appear to provide a good overview, here is a tutorial and the reference (all functions that have "BufferedPaint" in their name).
Here is the Window Procedure which worked for me and is based on the answer from zett42.
This piece of code is just for testing purposses as I cannot post the original source code of the application I'm working on due to work.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int xPos;
const bool bIsBufferedPaint = true;
switch (message)
{
case WM_CREATE:
{
BufferedPaintInit();
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
if(bIsBufferedPaint)
{
HDC newDC;
RECT rc;
RECT dstrc;
GetClientRect(hWnd, &rc);
dstrc = rc;
dstrc.left = rc.right + xPos;
HPAINTBUFFER hBufferedPaint = BeginBufferedPaint(hdc, &rc, BPBF_COMPATIBLEBITMAP, NULL, &newDC);
if(hBufferedPaint)
{
BufferedPaintClear(hBufferedPaint, NULL);
SetTextColor(newDC, RGB(0, 0, 0));
DrawText(newDC, L"Using buffered paint", -1, &dstrc, DT_SINGLELINE | DT_VCENTER | DT_LEFT);
Sleep(2);
EndBufferedPaint(hBufferedPaint, TRUE);
}
else
{
// buffer paint did not work.
}
}
else
{
HDC buffDC = CreateCompatibleDC(hdc);
SetTextColor(buffDC, RGB(0, 0, 0));
SetBkColor(buffDC, RGB(255, 255, 255));
RECT rc;
GetClientRect(hWnd, &rc);
HBITMAP buffBitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
int savedDC = SaveDC(buffDC);
SelectObject(buffDC, buffBitmap);
HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
FillRect(buffDC, &rc, hBrush);
DeleteObject(hBrush);
std::string testText = "Not using the buffered paint API";
TextOutA(buffDC, xPos, 0, testText.c_str(), testText.size());
BitBlt(hdc, 0, 0, rc.right, rc.bottom, buffDC, 0, 0, SRCCOPY);
RestoreDC(buffDC, savedDC);
DeleteObject(buffBitmap);
DeleteDC(buffDC);
}
EndPaint(hWnd, &ps);
}
break;
case WM_ERASEBKGND:
return 1;
case WM_TIMER:
{
switch(wParam)
{
case TIMER1:
{
xPos--;
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ERASE);
if(bIsBufferedPaint)
{
if(xPos <= -500)
xPos = 0;
}
else
{
if(xPos <= -50)
xPos = 1000;
}
}
break;
}
}
break;
case WM_NCDESTROY:
{
BufferedPaintUnInit();
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I am trying to teach myself C++ and have ran into a snag. This is my first attempt at making a simple connect to database Win32 program.
What I want to do:
I want all my strings defined in lang.h (this would allow for easier translating).
I then want to use DrawText() in the WM_PAINT case statement to draw the text in certain positions on the screen.
I have gotten the text to draw on the screen with this:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH));
Rectangle(ps.hdc, 100, 100, 500, 300);
RECT rect;
GetClientRect(hWnd, &rect);
SetTextColor(ps.hdc, RGB(0, 0, 0));
SetBkMode(ps.hdc, TRANSPARENT);
rect.left = 110;
rect.top = 110;
LPCWSTR message = L"Connect to Database: ";
DrawText(ps.hdc, message, -1, &rect, DT_SINGLELINE | DT_NOCLIP);
EndPaint(hWnd, &ps);
}
break;
But, what I really want to do is use the strings that I defined in lang.h
Example: (this does not work, just one of the many things I tried)
//lang.h
#define MSG_DBPATH = L"Path to Database:";
//SimpleDBConnect.cpp
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH));
Rectangle(ps.hdc, 100, 100, 500, 300);
RECT rect;
GetClientRect(hWnd, &rect);
SetTextColor(ps.hdc, RGB(0, 0, 0));
SetBkMode(ps.hdc, TRANSPARENT);
rect.left = 110;
rect.top = 110;
DrawText(ps.hdc, MSG_DBPATH, -1, &rect, DT_SINGLELINE | DT_NOCLIP);
EndPaint(hWnd, &ps);
}
break;
Is it possible to draw text on the screen within the WM_PAINT case statement using defined text in an external header file?
#define MSG_DBPATH L"Path to Database:"
Note no equal sign and no semicolon.
I'm trying to remake my Windows screensaver written with C++ and WinAPIs to work on multiple monitors. I found this article that gives the basics. But when I implement it in my own code, I get a weird result. Take a look at this code:
case WM_PAINT:
{
PAINTSTRUCT ps = {0};
HDC hdcE = BeginPaint(hWnd, &ps );
EnumDisplayMonitors(hdcE, NULL, MyPaintEnumProc, 0);
EndPaint(hWnd, &ps);
}
break;
BOOL CALLBACK MyPaintEnumProc(
HMONITOR hMonitor, // handle to display monitor
HDC hdc1, // handle to monitor DC
LPRECT lprcMonitor, // monitor intersection rectangle
LPARAM data // data
)
{
MONITORINFO mi = {0};
mi.cbSize = sizeof(mi);
if(GetMonitorInfo(hMonitor, &mi))
{
//Is it a primary monitor?
BOOL bPrimary = mi.dwFlags & MONITORINFOF_PRIMARY;
DoDrawing(bPrimary, hdc1, &mi.rcMonitor);
}
return 1;
}
void DoDrawing(BOOL bPrimaryMonitor, HDC hDC, RECT* pRcMonitor)
{
//#define DIRECT_PAINT //Comment out for double-buffering
int nMonitorW = abs(pRcMonitor->right - pRcMonitor->left);
int nMonitorH = abs(pRcMonitor->bottom - pRcMonitor->top);
HDC hMemDC = ::CreateCompatibleDC(hDC);
if(hMemDC)
{
HBITMAP hMemBmp = ::CreateCompatibleBitmap(hDC, nMonitorW, nMonitorH);
if(hMemBmp)
{
HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, hMemBmp);
COLORREF clr, clrBorder;
if(bPrimaryMonitor)
{
clr = RGB(0, 128, 0); //Green
clrBorder = RGB(255, 0, 0);
}
else
{
clr = RGB(128, 0, 0); //Red
clrBorder = RGB(0, 255, 0);
}
RECT rcRect;
#ifndef DIRECT_PAINT
//With double-buffering
rcRect.left = 0;
rcRect.top = 0;
rcRect.right = nMonitorW;
rcRect.bottom = nMonitorH;
#else
rcRect = *pRcMonitor;
#endif
HBRUSH hBrush = ::CreateSolidBrush(clr);
#ifndef DIRECT_PAINT
//With double-buffering
::FillRect(hMemDC, &rcRect, hBrush);
#else
::FillRect(hDC, &rcRect, hBrush);
#endif
#ifndef DIRECT_PAINT
//With double-buffering
::BitBlt(hDC, pRcMonitor->left, pRcMonitor->top, nMonitorW, nMonitorH, hMemDC, 0, 0, SRCCOPY);
#endif
//Debugging output
CString _s;
_s.Format(_T("%s\n")
_T("%s\n")
_T("hDC=0x%X\n")
_T("hMemDC=0x%X\n")
_T("RcMonitor: L=%d, T=%d, R=%d, B=%d")
,
bPrimaryMonitor ? _T("Primary") : _T("Secondary"),
#ifndef DIRECT_PAINT
_T("Double-buffering"),
#else
_T("Direct paint"),
#endif
hDC,
hMemDC,
pRcMonitor->left,
pRcMonitor->top,
pRcMonitor->right,
pRcMonitor->bottom);
::DrawText(hDC, _s, _s.GetLength(), pRcMonitor, DT_NOCLIP | DT_NOPREFIX);
SelectObject(hMemDC, hOldBmp);
::DeleteObject(hMemBmp);
}
::DeleteDC(hMemDC);
}
}
Painting always works on a primary monitor. But when I paint to the secondary monitor, I can only paint directly to its DC. When I use double-buffering technique (with DIRECT_PAINT pre-processor directive commented out) I only get a black screen on a secondary monitor when it should've been red.
I'm attaching two screenshots here.
First one with direct painting that works:
And then the one with double-buffering that fails:
Any ideas what am I doing wrong here?
Replace the code for WM_PAINT from
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EnumDisplayMonitors(hdc, NULL, MyPaintEnumProc, 0);
EndPaint(hWnd, &ps);
with
case WM_PAINT:
hdc = GetDC(NULL);
EnumDisplayMonitors(hdc, NULL, MyPaintEnumProc, 0);
ReleaseDC(NULL, hdc);
and it will work.
See this http://msdn.microsoft.com/en-us/library/windows/desktop/dd162610(v=vs.85).aspx.
I have this STATIC control displayed over a window that has an image as its background. When I initiate the control it displays a text. If I want to change the text inside a WM_TIMER message it is displayed over the initial text (it is not removed) I have tried UpdateWindow and InvalidateRect but id doesn't work.
This is my code
WM_CREATE:
HWND control = CreateWindowEx(
WS_EX_TRANSPARENT,
L"STATIC",
L"FirstText",
WS_CHILD|WS_VISIBLE|ES_LEFT,
0,
0,
200,
20,
hWnd,
HMENU(LABEL1),
Instance,
NULL
);
break;
case WM_TIMER:
SetWindowText(GetDlgItem(hWnd, LABEL1), L"SecondText");
KillTimer(hWnd, MYTIMER);
// Here I tried UpdateWindow and InvalidateRect but no result
break;
So, the second text is drawn over the first one. It looks like the STATIC content is not updated after changing it. What could be going wrong? Thanks!
I hard coded some numbers in there for testing (like the window ID) - but it's just an example to show you.
case WM_CREATE:
control = CreateWindowEx(
WS_EX_TRANSPARENT,
L"STATIC",
L"FirstText",
WS_CHILD|WS_VISIBLE|ES_LEFT,
0,
0,
200,
20,
hWnd,
HMENU(99),
hInst,
NULL
);
//Create a timer
SetTimer(hWnd,23, 5000,NULL);
break;
case WM_CTLCOLORSTATIC:
if ( GetDlgItem(hWnd, 99) == (HWND)lParam)
{
SetBkMode( (HDC)wParam, TRANSPARENT);
return (LRESULT) GetStockObject(HOLLOW_BRUSH);
}
break;
case WM_TIMER:
{
SetWindowText(GetDlgItem(hWnd, 99), L"Second Text");
KillTimer(hWnd, 23);
RECT rect = {0,0, 200,20};
InvalidateRect(hWnd, &rect, TRUE);
UpdateWindow(hWnd);
}
break;
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
//Load my test bitmap from resources
HBITMAP hb = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
BITMAP bm;
GetObject(hb, sizeof(BITMAP), &bm);
HDC memDC= CreateCompatibleDC(hdc);
SelectObject(memDC,hb);
// ========================
//Put the bitmap on the main window to act as a backdrop
BitBlt(hdc, 0,0, bm.bmWidth,bm.bmHeight,memDC,0,0, SRCCOPY);
DeleteDC(memDC);
EndPaint(hWnd, &ps);
}
break;