I am trying to draw a rectangle as you left click on a point and then drag across , while your dragging across the rectangle gets form to show you a preview of how the rectangle will look . This works fine except when I drag back into a smaller rectangle the old rectangles still remain.
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
if(painting)
{
HPEN greenPen=CreatePen(PS_SOLID, 1, RGB(0,255,0));
SelectObject(hdc,greenPen);
Rectangle(hdc, x1, y1, x2, y2);
}
EndPaint(hWnd, &ps);
break;
case WM_LBUTTONDOWN:
painting=true;
x1=LOWORD(lParam);
y1=HIWORD(lParam);
InvalidateRect(hWnd, NULL ,false);
break;
case WM_MOUSEMOVE:
x2=LOWORD(lParam);
y2=HIWORD(lParam);
InvalidateRect(hWnd, NULL ,false);
break;
case WM_LBUTTONUP:
painting=false;
break;
#user1788175 - just use a RECT struct. When the drawing starts, you set the left & top members to the x,y pos of the mouse. When the mouse is released, set the right,bottom members. Swap left,right if necessary, to ensure that left
Here's some code ripped from a class I wrote to deal with with selection rectangles. You can ignore the normalize code - it just converts the pixel coordinates to ones in the range [0..1], so I can draw the selection on a scaled-down version of the image, yet still select the same region if the image is shown at a different scale. My images were 4944x6992, so I had to display a scaled-down version of them.
LRESULT CALLBACK cSelBoxImg::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT mappedRect, tmpRect, myRect;
HBRUSH myBrush;
BITMAP bm;
PAINTSTRUCT ps;
HDC mDC;
HBITMAP tmpBmp;
switch (uMsg)
{
case WM_LBUTTONDOWN:
if (bMouseSelectionEnabled)
{
bRubberBanding = true;
mouseClickDownPos.x = LOWORD(lParam);
mouseClickDownPos.y = HIWORD(lParam);
curMousePos = mouseClickDownPos;
selectionRect.left = min(curMousePos.x, mouseClickDownPos.x);
selectionRect.right = max(curMousePos.x, mouseClickDownPos.x);
selectionRect.top = min(curMousePos.y, mouseClickDownPos.y);
selectionRect.bottom = max(curMousePos.y, mouseClickDownPos.y);
normalizeSelRect();
InvalidateRect(mHwnd, NULL, isBkgTransparent);
PostMessage(GetParent(hWnd), WM_COMMAND, GetDlgCtrlID(hWnd), (LPARAM)hWnd);
}
return 1;
case WM_LBUTTONUP:
if (bMouseSelectionEnabled)
if (bRubberBanding)
{
bRubberBanding = false;
mouseClickUpPos.x = LOWORD(lParam);
mouseClickUpPos.y = HIWORD(lParam);
selectionRect.left = min(mouseClickUpPos.x, mouseClickDownPos.x);
selectionRect.right = max(mouseClickUpPos.x, mouseClickDownPos.x);
selectionRect.top = min(mouseClickUpPos.y, mouseClickDownPos.y);
selectionRect.bottom = max(mouseClickUpPos.y, mouseClickDownPos.y);
normalizeSelRect();
InvalidateRect(mHwnd, NULL, isBkgTransparent);
PostMessage(GetParent(hWnd), WM_COMMAND, GetDlgCtrlID(hWnd), (LPARAM)hWnd);
}
return 1;
case WM_MOUSEMOVE:
if (bMouseSelectionEnabled)
if (bRubberBanding)
{
curMousePos.x = LOWORD(lParam);
curMousePos.y = HIWORD(lParam);
selectionRect.left = min(curMousePos.x, mouseClickDownPos.x);
selectionRect.right = max(curMousePos.x, mouseClickDownPos.x);
selectionRect.top = min(curMousePos.y, mouseClickDownPos.y);
selectionRect.bottom = max(curMousePos.y, mouseClickDownPos.y);
// UpdateWindow(hWnd);
//RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW);
normalizeSelRect();
InvalidateRect(hWnd, NULL, false);
PostMessage(GetParent(hWnd), WM_COMMAND, GetDlgCtrlID(hWnd), (LPARAM)hWnd);
// printf("Message posted\n");
}
return 0;
case WM_PAINT:
mDC = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &tmpRect);
// GetObject(mBmp, sizeof(bm), &bm);
mappedRect.left = mLeft * tmpRect.right;
mappedRect.right = mRight * tmpRect.right;
mappedRect.top = mTop * tmpRect.bottom;
mappedRect.bottom = mBottom * tmpRect.bottom;
displayImage();
if (mBmp) drawRect(mDC, mappedRect, RGB(0,0,255));
DeleteObject(tmpBmp);
EndPaint(hWnd, &ps);
return 0;
case WM_ERASEBKGND:
if (isBkgTransparent)
{
GetClientRect(hWnd, &myRect);
myBrush = (HBRUSH) GetWindowLong(hWnd, GCL_HBRBACKGROUND);
FillRect((HDC)wParam, &myRect, myBrush);
printf("background cleared\n");
}
return true;
case WM_SETCURSOR:
SetCursor(mCursor);
return true;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Related
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:
I want to draw the line by clicking the mouse on the first coordinate where the line should start and second coordinate where the line should end.
When I run my project nothing happens.
I cannot find out what my code is missing.
LONG WINAPI WndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
BOOL fDraw = FALSE;
POINT ptPrevious = { 0 };
HPEN Pen = CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
switch (Message) {
case WM_LBUTTONDOWN: {
fDraw = TRUE;
ptPrevious.x = LOWORD(lParam);
ptPrevious.y = HIWORD(lParam);
break;
}
case WM_LBUTTONUP: {
if (fDraw)
{
hdc = GetDC(hWnd);
MoveToEx(hdc, ptPrevious.x, ptPrevious.y, NULL);
LineTo(hdc, LOWORD(lParam), HIWORD(lParam));
ReleaseDC(hWnd, hdc);
}
fDraw = FALSE;
break;
}
case WM_MOUSEMOVE: {
if (fDraw)
{
hdc = GetDC(hWnd);
MoveToEx(hdc, ptPrevious.x, ptPrevious.y, NULL);
LineTo(hdc, ptPrevious.x = LOWORD(lParam),
ptPrevious.y = HIWORD(lParam));
ReleaseDC(hWnd, hdc);
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, Message, wParam, lParam);
}
return 0;
}
Fundamentally, the problem is because none of your data is persistent. Since they are all local variables, their content disappears when WndProc returns. fDraw is set to FALSE on every message, and will never be true for the WM_LBUTTONDOWN or WM_MOUSEMOVE messages. Therefore nothing happens.
You'll want to create some sort of a class to hold fDraw, ptPrevious, and a structure to hold the coordinates of the line(s) to draw. Use InvalidateRect in the WM_MOUSEMOVE and WM_LBUTTONUP messages. Then only draw them in response to a WM_PAINT message (using the DC provided in the paint message).
Using the CWnd MFC class can greatly simplify those tasks.
Because declared variables are local variables, variables can be declared with Static.
static BOOL fDraw = FALSE;
static POINT ptPrevious = { 0 };
In addition, you forget to use the SelectObject function to select pen into DC.
You can also use WM_PAINT redrawing to improve your code:
Example:
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
static BOOL fDraw = FALSE;
static BOOL fDraw_begin = FALSE;
static POINT ptPrevious;
static RECT rcClient;
static POINT pt;
static HPEN Pen;
switch (message)
{
case WM_CREATE:
{
Pen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
break;
}
case WM_LBUTTONDOWN:
{
ptPrevious.x = (LONG)LOWORD(lParam);
ptPrevious.y = (LONG)HIWORD(lParam);
return 0;
}
case WM_LBUTTONUP:
{
if (fDraw = TRUE)
{
fDraw = FALSE;
fDraw_begin = TRUE;
InvalidateRect(hwnd, &rcClient, TRUE);
UpdateWindow(hwnd);
}
return 0;
}
case WM_MOUSEMOVE:
{
if (wParam && MK_LBUTTON)
{
GetClientRect(hwnd, &rcClient);
hdc = GetDC(hwnd);
SetROP2(hdc, R2_NOTXORPEN);
if (!IsRectEmpty(&rcClient)) // Detecting whether the rectangular area is empty
{
MoveToEx(hdc, ptPrevious.x, ptPrevious.y, NULL);
LineTo(hdc, (LONG)LOWORD(lParam),
(LONG)HIWORD(lParam));
}
MoveToEx(hdc, ptPrevious.x, ptPrevious.y, NULL);
LineTo(hdc, (LONG)LOWORD(lParam),
(LONG)HIWORD(lParam));
pt.x = (LONG)LOWORD(lParam);
pt.y = (LONG)HIWORD(lParam);
fDraw = TRUE;
ReleaseDC(hwnd, hdc);
}
return 0;
}
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
SelectObject(hdc, Pen);
if (fDraw_begin)
{
fDraw_begin = FALSE;
MoveToEx(hdc, ptPrevious.x, ptPrevious.y, NULL);
LineTo(hdc, pt.x,
pt.y);
}
EndPaint(hwnd, &ps);
ReleaseDC(hwnd, hdc);
return 0;
}
case WM_DESTROY:
{
DeleteObject(Pen);
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
Do not get the DC three times, get it once and release it on mouse up.
Also, your drawing will be eventually validated, so you need to log the mouse move and repeat this stuff in WM_PAINT handler.
I'm new and I'm trying to write a simple program drawing lines with the mouse.
I have a problem with drawing these lines, because it leaves traces behind.
Here is an image of my problem:
And here is a sample of my code:
LRESULT APIENTRY WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:PostQuitMessage(0);break;
case WM_LBUTTONDOWN:
hdc = GetDC(hwnd);
last_x = LOWORD(lParam);
last_y = HIWORD(lParam);
isDown = true;
break;
case WM_MOUSEMOVE:
if (isDown)
{
Pen = CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
Box = (HPEN)SelectObject(hdc, Pen);
int x = LOWORD(lParam);
int y = HIWORD(lParam);
MoveToEx(hdc, last_x, last_y, NULL);
LineTo(hdc, x, y);
}
break;
case WM_LBUTTONUP:
isDown = false;
ReleaseDC;
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
EDIT:
Now it's working well, but if You could explain me one another thing, how can i make that my old lines stayed on the Client area when i drawing new lines? Cause now i can draw only one line. Should i use Bitmap to save screen or something?
EDIT:EDIT:
Ok i used Vector to save coordinates of every line. Thank You guys for help!
You are getting residual traces because you are not erasing your old drawings before drawing a new line, or at least updating last_x and last_y on each move so that a new line connects to the end of the previous line, like in Microsoft's example.
But, you really should not draw on the window directly in the mouse message handlers at all. As soon as the window needs a repaint for any reason, all of your drawing will be lost. The correct way to handle this is to perform all of your drawing in a WM_PAINT message handler instead. Use the mouse messages to keep track of line information as needed, and then do all of the actual drawing in WM_PAINT only.
For example:
LRESULT APIENTRY WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
Pen = CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
break;
case WM_DESTROY:
DeleteObject(Pen);
PostQuitMessage(0);
break;
case WM_LBUTTONDOWN:
x = last_x = LOWORD(lParam);
y = last_y = HIWORD(lParam);
isDown = true;
InvalidateRect(hwnd, NULL, TRUE);
break;
case WM_MOUSEMOVE:
if (isDown)
{
x = LOWORD(lParam);
y = HIWORD(lParam);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case WM_LBUTTONUP:
isDown = false;
InvalidateRect(hwnd, NULL, TRUE);
break;
/* if your WNDCLASS sets hbrBackground=NULL, uncomment this handler...
case WM_ERASEBKGND:
{
HDC hdc = (HDC) wParam;
draw a background on the hdc as needed...
return 1;
}
*/
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
if (last_x != x) || (last_y != y)
{
HPEN OldPen = (HPEN) SelectObject(hdc, Pen);
MoveToEx(hdc, last_x, last_y, NULL);
LineTo(hdc, x, y);
SelectObject(hdc, OldPen);
}
EndPaint(hwnd, &ps);
break;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
That will draw a single line that starts at the point where the mouse was first held down, and then follow the mouse as it moves around.
Or, if you want to draw multiple lines end-to-end that follow the mouse while it is held down, try this:
std::vector<POINT> points;
LRESULT APIENTRY WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
Pen = CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
points.clear();
break;
case WM_DESTROY:
DeleteObject(Pen);
PostQuitMessage(0);
break;
case WM_LBUTTONDOWN:
{
points.clear();
POINT pt;
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
points.push_back(pt);
isDown = true;
InvalidateRect(hwnd, NULL, TRUE);
break;
}
case WM_MOUSEMOVE:
if (isDown)
{
POINT pt;
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
points.push_back(pt);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case WM_LBUTTONUP:
isDown = false;
InvalidateRect(hwnd, NULL, TRUE);
break;
/* if your WNDCLASS sets hbrBackground=NULL, uncomment this handler...
case WM_ERASEBKGND:
{
HDC hdc = (HDC) wParam;
draw a background on the hdc as needed...
return 1;
}
*/
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
if (points.size() > 1)
{
HPEN OldPen = (HPEN) SelectObject(hdc, Pen);
MoveToEx(hdc, points[0].x, points[0].y, NULL);
for (size_t i = 1; i < points.size(); ++i) {
LineTo(hdc, points[i].x, points[i].y);
}
SelectObject(hdc, OldPen);
}
EndPaint(hwnd, &ps);
break;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
I am late to party here and it may be obvious to experienced Win32 programmers, but I wanted to add an important note to Remy Lebeau example:
both the boolean isDown and the 4 ints x,y,last_x and last_y have to be static, or else the entire solution won't work.
I have a main window which is created with the following styles
WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_TABSTOP | WS_GROUP | WS_VISIBLE
and with ex-stles
WS_EX_ACCEPTFILES | WS_EX_CONTROLPARENT | WS_EX_LEFT | WS_EX_LTRREADING.
This main window has a child window on it, which is an edit control created with styles
WS_VISIBLE | WS_CHILD | ES_READONLY
and ex-style
WS_EX_CLIENTEDGE.
I am going to use this edit control as a progress-bar control. I don't want to use standard Wind32 progress-bar control (PROGRESS_CLASS), because I want to do a some custom painting on it (e.g.; dynamically changing fill color, displaying text on it, etc).
I can paint any region of the main window by the following code:
// hWnd: Handle of the main window
case WM_PAINT:
hDc = BeginPaint(hWnd, &Ps);
Rect = AFunctionToGetCornerThePointsOfTheEditControl();
Rect.right = Rect.left + 3 * (Rect.right - Rect.left) / 4; // Fill 3/4 (75%) of it
Rect.left -= 10; // Enlarge the paint region a little
Rect.top -= 10; // so that we can see it if it stays
Rect.bottom += 10; // under the edit control.
hBrush = CreateSolidBrush(RGB(50,100,255));
ret = FillRect(hDc, &Rect, hBrush); // ret = 1 always
ler = GetLastError(); // ler = 0
EndPaint(hWnd, &Ps);
break;
It looks like this:
I changed this code a little to paint the child control instead:
// hWndEdit: Handle of the edit control
case WM_PAINT:
hDc = BeginPaint(hWndEdit, &Ps);
Rect = AFunctionToGetCornerThePointsOfTheEditControl();
Rect.right = Rect.left + 3 * (Rect.right - Rect.left) / 4; // Fill 3/4 (75%) of it
Rect.left -= 10;
Rect.top -= 10;
Rect.bottom += 10;
hBrush = CreateSolidBrush(RGB(50,100,255));
ret = FillRect(hDc, &Rect, hBrush); // ret = 0 always
ler = GetLastError(); // ler = 6 (ERROR_INVALID_HANDLE)
EndPaint(hWndEdit, &Ps);
break;
This time it doesn't work. The main windows completely disappears as soon as I drag some part it out of the screen area, and it becomes totally unresponsive. Desktop icons under it are visible, but are not clickable.
So, what do I have to do in order to paint the child window (the edit control)?
This article helped me a lot: Subclassing Controls
First, I create a separate message processing function for processing child messages.
LRESULT CALLBACK MyClass::ChildWindowProc( HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData)
{
static PAINTSTRUCT Ps;
static RECT Rect;
static HBRUSH hBrush1 = CreateSolidBrush(RGB(50,100,255));
static HBRUSH hBrush2 = CreateSolidBrush(RGB(255,100,50));
HDC hDc;
LRESULT lResult;
switch (uMsg)
{
case WM_PAINT:
switch (uIdSubclass)
{
case 1:
hDc = BeginPaint(hWnd, &Ps);
Rect.left = 0;
Rect.right = (LONG) (((double) ITEM2_WIDTH) * Status::GI()->Get_JobCurPercentage());
Rect.top = 0;
Rect.bottom = ITEM_HEIGHT - 3;
FillRect(hDc, &Rect, hBrush1);
EndPaint(hWnd, &Ps);
break;
case 2:
hDc = BeginPaint(hWnd, &Ps);
Rect.left = 0;
Rect.right = (LONG) (((double) ITEM2_WIDTH) * Status::GI()->Get_JobTotPercentage());
Rect.top = 0;
Rect.bottom = ITEM_HEIGHT - 3;
FillRect(hDc, &Rect, hBrush2);
EndPaint(hWnd, &Ps);
break;
default:
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
break;
case WM_NCDESTROY:
//ReleaseDC(hWnd, hDc);
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
break;
default:
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
Next, I bind this function to the controls:
SetWindowSubclass( /*_In_ HWND hWnd*/ ed_cur_Progress.hWnd,
/*_In_ SUBCLASSPROC pfnSubclass*/ ChildWindowProc,
/*_In_ UINT_PTR uIdSubclass*/ 1,
/*_In_ DWORD_PTR dwRefData*/ (DWORD_PTR) NULL);
SetWindowSubclass( /*_In_ HWND hWnd*/ ed_tot_Progress.hWnd,
/*_In_ SUBCLASSPROC pfnSubclass*/ ChildWindowProc,
/*_In_ UINT_PTR uIdSubclass*/ 2,
/*_In_ DWORD_PTR dwRefData*/ (DWORD_PTR) NULL);
And, that's all! The result is amazing.
WM_PAINT you are handling is main window's. you need to draw the editbox in its owner WM_PAINT message. I guess you get the error from "hDc = BeginPaint(hWndEdit, &Ps);", you may check it.
The problem I am having is that when I draw my line, the screen flickers every time it redraws. I just can't quite figure out how to not make it flicker. I understand the flicker is coming from me redrawing the client area hundreds of times a second as I move my mouse with my left button down but how would I be able to get around this?
LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
static HDC hdc;
PAINTSTRUCT ps;
RECT rect;
RECT size;
static POINT point1;
static POINT point2;
static HBRUSH origBrush;
static bool drawingLine = false;
switch (message)
{
case WM_CREATE:
origBrush = CreateSolidBrush(RGB(0, 0 , 0) );
break;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps);
GetClientRect (hwnd, &rect);
GetWindowRect(hwnd, &size);
Rectangle(hdc, 10, 10, 80, 80 );
if(drawingLine == true)
{
MoveToEx(hdc, point1.x, point1.y, NULL);
LineTo(hdc, point2.x, point2.y);
}
EndPaint (hwnd, &ps);
return 0;
//Has all the commands that exist in your program.
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_COLOR_RED:
break;
case ID_COLOR_BLUE:
break;
case ID_COLOR_BLACK:
break;
case ID_THICKNESS_1:
break;
case ID_THICKNESS_2:
break;
case ID_THICKNESS_3:
break;
}
break;
case WM_LBUTTONDOWN:
drawingLine = true;
SelectObject(hdc, GetStockObject(BLACK_BRUSH));
point1.x = LOWORD(lParam);
point1.y = HIWORD(lParam);
MoveToEx(hdc, point1.x, point1.y, NULL);
break;
case WM_MOUSEMOVE:
point2.x = LOWORD(lParam);
point2.y = HIWORD(lParam);
InvalidateRect(hwnd, NULL, 1);
break;
case WM_LBUTTONUP:
point2.x = LOWORD(lParam);
point2.y = HIWORD(lParam);
drawingLine = false;
break;
//Causes the program to exit.
case WM_DESTROY:
PostQuitMessage(0);
break;
}
The main thing to do is respond to WM_ERASEBKGND and return true to prevent the default re-drawing of the background.
Then you'll need to erase the old line you before drawing the new one. Just for example, you might save bits from under the line, draw it, then restore those (and only those) pixels before drawing the line, so when you need to erase it, you can just restore those pixels to erase it.
The obvious alternative (that's usually simpler though theoretically at least a little slower) is to use double buffering. Do roughly the drawing you are now, but to an off-screen bitmap. Then, when it's all complete just BitBlt from there to the screen.