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;
Related
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
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;
}
How do I change the background color of a Combo Box in C++ using the Windows API?
In general you can create a brush in heap, for example using
static HBRUSH hbrush;
...
hbrush = CreateSolidBrush(RGB(255,0,0));
Then in Window or Dialog procedure, handle WM_CTL*** messages and return the brush handle, for example
case WM_CTLCOLORBTN:
case WM_CTLCOLORSTATIC:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX:
{
HDC hdc = (HDC)wParam;
SetTextColor(hdc, RGB(0, 0, 255)); //change text color
SetBkMode(hdc, TRANSPARENT); //change text-background color, or set it to transparent
return (INT_PTR)hbrush;
}
This works for ComboBox too. If you want to handle different controls individually, you can check lParam against the control's handle.
For a ComboBox it's a little more complicated. You have to get the handle to ComboBox's EditBox and ListBox. You can do that using GetComboBoxInfo
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX:
{
HWND hTest = (HWND)lParam;
COMBOBOXINFO ci = { sizeof(COMBOBOXINFO) };
GetComboBoxInfo(hComboBox, &ci);
if (
hTest == ci.hwndItem ||
hTest == ci.hwndList
)
{
HDC hdc = (HDC)wParam;
SetTextColor(hdc, RGB(0, 0, 255));
SetBkMode(hdc, TRANSPARENT);
return (INT_PTR)hbrush;
}
}
I need some help here.
Im importing an bitmap onto my Win32 Window. I am building it and after some seconds it's starting to lag a lot. I am not sure why, but I suppose I am not correctly deleting it from memory after using it.
Thank you for Help in advance.
I saw a behavior while I was testing it. If Im not moving the window than it is okey, but after moving it it start to lag and block my IDE. Maybe something with WM_PAINT?
Here is my code.
#include <windows.h>
//For more makros
#include <windowsx.h>
#include "Simulatron.h"
HINSTANCE hProgramInstance;
Simulatron Exo;
char Simulatron::m_szClassName[] = "Simulatron";
Simulatron::Simulatron(HINSTANCE hInstance)
{
m_hInstance = hInstance; // Save Instance handle
m_wndClass.cbSize = sizeof(WNDCLASSEX); // Must always be sizeof(WNDCLASSEX)
m_wndClass.style = CS_DBLCLKS; // Class styles
m_wndClass.lpfnWndProc = MainWndProc; // Pointer to callback procedure
m_wndClass.cbClsExtra = 0; // Extra bytes to allocate following the wndclassex structure
m_wndClass.cbWndExtra = 0; // Extra bytes to allocate following an instance of the structure
m_wndClass.hInstance = hInstance; // Instance of the application
m_wndClass.hIcon = NULL;//LoadIcon(hInstance, MAKEINTRESOURCE(IDC_MAINCURSOR)); // Class Icon
m_wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // Class cursor
m_wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // Background brush
m_wndClass.lpszMenuName = NULL; // Menu Resource
m_wndClass.lpszClassName = (LPCWSTR)m_szClassName; // Name of this class
m_wndClass.hIconSm = NULL;//LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); // Small icon for this class
}
Simulatron::~Simulatron()
{
}
Simulatron::Simulatron()
{
// If we declare a window class with a default constructor,
// we need to reset the window to a nothing
}
LRESULT CALLBACK Simulatron::MainWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HDC hdc;
static PAINTSTRUCT ps;
static HDC hdc_mem;
static HBRUSH newbrush;
//Child Window Handles
Simulatron create;
RECT rect;
hProgramInstance = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);
static HBITMAP logo = NULL;
static BITMAP bitmap;
logo = (HBITMAP)LoadImage(hProgramInstance, L"Space.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
GetObject(logo, sizeof(bitmap), &bitmap);
switch (msg)
{
case WM_CREATE:
{
create.Create(hProgramInstance,hwnd,lParam,logo);
}
break;
case WM_GETMINMAXINFO:
{
LPMINMAXINFO pInfo = (LPMINMAXINFO) lParam;
//pInfo -> ptMaxTrackSize.x = 450;
//pInfo -> ptMaxTrackSize.y = 650;
}
break;
case WM_SIZE:
break;
case WM_CTLCOLORSTATIC:
SetTextColor((HDC)wParam, RGB(150, 100, 255));
SetBkMode((HDC)wParam, TRANSPARENT);
newbrush = (HBRUSH)GetStockObject(NULL_BRUSH);
DeleteObject(newbrush);
return (LRESULT)newbrush;
break;
case WM_COMMAND:
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd , &rect);
hdc_mem = CreateCompatibleDC(hdc);
SelectObject(hdc_mem, logo);
BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdc_mem, 0, 0, SRCCOPY);
DeleteObject(hdc_mem);
EndPaint(hwnd, &ps);
break;
//Handle the combinations from the keyboard input
case WM_DESTROY:
PostQuitMessage (0);
DeleteObject(logo);
DeleteBitmap(logo);
break;
default:
return DefWindowProc (hwnd, msg, wParam, lParam);
}
return 0;
}
//Create function of all Childs
void Simulatron::Create(HINSTANCE Hinst, HWND hWindow, LPARAM lParam, HBITMAP logo)
{
Hinst = ((LPCREATESTRUCT) lParam) -> hInstance; // handle to instance for custom cursor
logo = (HBITMAP)LoadImage(Hinst, L"Space.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
}
bool Simulatron::Run(int nCmdShow)
{
if(!RegisterClassEx(&m_wndClass))
return false;
m_hwnd = CreateWindowEx(0,(LPCWSTR)m_szClassName,
L"Simulatron",
//WS_OVERLAPPEDWINDOW,
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, // Dissable Resizing and Maximizing
0, 0, 1280, 1000,
NULL, NULL,
m_hInstance,
NULL);
if(!m_hwnd)
return false;
ShowWindow(m_hwnd, nCmdShow);
return true;
}
Simulatron::operator HWND()
{
// This overloaded operator allows us to use HWND anyway we want
return m_hwnd;
}
You load the BMP File over and over again in your MainWndProc. You should load it once at Init and use it from there! Have a look at a win32 API tutorial and you will see that MainWndProc is getting called throughout the whole program lifetime. You could load that image in your WM_CREATE state for example.