[c++]WinApi - Simple Program drawing lines
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!
c++ winapi gdi
add a comment |
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!
c++ winapi gdi
"I have problem with drawing this line, cause it leaves traces." - have you considered clearing the screen between each draw?
– Jesper Juhl
Nov 12 '18 at 22:31
You have a logic issue. Please check this example. docs.microsoft.com/en-us/windows/desktop/gdi/…
– Naidu
Nov 12 '18 at 22:56
I'm pretty sureReleaseDC
is meant to take a parameter.
– Jonathan Potter
Nov 12 '18 at 23:02
@Naidu i was trying this example from microsoft, but also didn't work.
– Kamilen
Nov 13 '18 at 10:57
add a comment |
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!
c++ winapi gdi
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!
c++ winapi gdi
c++ winapi gdi
edited Nov 14 '18 at 12:40
Kamilen
asked Nov 12 '18 at 22:22
KamilenKamilen
82
82
"I have problem with drawing this line, cause it leaves traces." - have you considered clearing the screen between each draw?
– Jesper Juhl
Nov 12 '18 at 22:31
You have a logic issue. Please check this example. docs.microsoft.com/en-us/windows/desktop/gdi/…
– Naidu
Nov 12 '18 at 22:56
I'm pretty sureReleaseDC
is meant to take a parameter.
– Jonathan Potter
Nov 12 '18 at 23:02
@Naidu i was trying this example from microsoft, but also didn't work.
– Kamilen
Nov 13 '18 at 10:57
add a comment |
"I have problem with drawing this line, cause it leaves traces." - have you considered clearing the screen between each draw?
– Jesper Juhl
Nov 12 '18 at 22:31
You have a logic issue. Please check this example. docs.microsoft.com/en-us/windows/desktop/gdi/…
– Naidu
Nov 12 '18 at 22:56
I'm pretty sureReleaseDC
is meant to take a parameter.
– Jonathan Potter
Nov 12 '18 at 23:02
@Naidu i was trying this example from microsoft, but also didn't work.
– Kamilen
Nov 13 '18 at 10:57
"I have problem with drawing this line, cause it leaves traces." - have you considered clearing the screen between each draw?
– Jesper Juhl
Nov 12 '18 at 22:31
"I have problem with drawing this line, cause it leaves traces." - have you considered clearing the screen between each draw?
– Jesper Juhl
Nov 12 '18 at 22:31
You have a logic issue. Please check this example. docs.microsoft.com/en-us/windows/desktop/gdi/…
– Naidu
Nov 12 '18 at 22:56
You have a logic issue. Please check this example. docs.microsoft.com/en-us/windows/desktop/gdi/…
– Naidu
Nov 12 '18 at 22:56
I'm pretty sure
ReleaseDC
is meant to take a parameter.– Jonathan Potter
Nov 12 '18 at 23:02
I'm pretty sure
ReleaseDC
is meant to take a parameter.– Jonathan Potter
Nov 12 '18 at 23:02
@Naidu i was trying this example from microsoft, but also didn't work.
– Kamilen
Nov 13 '18 at 10:57
@Naidu i was trying this example from microsoft, but also didn't work.
– Kamilen
Nov 13 '18 at 10:57
add a comment |
1 Answer
1
active
oldest
votes
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;
}
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53270959%2fcwinapi-simple-program-drawing-lines%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
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;
}
add a comment |
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;
}
add a comment |
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;
}
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;
}
edited Nov 13 '18 at 0:56
answered Nov 13 '18 at 0:40
Remy LebeauRemy Lebeau
332k18251443
332k18251443
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53270959%2fcwinapi-simple-program-drawing-lines%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
"I have problem with drawing this line, cause it leaves traces." - have you considered clearing the screen between each draw?
– Jesper Juhl
Nov 12 '18 at 22:31
You have a logic issue. Please check this example. docs.microsoft.com/en-us/windows/desktop/gdi/…
– Naidu
Nov 12 '18 at 22:56
I'm pretty sure
ReleaseDC
is meant to take a parameter.– Jonathan Potter
Nov 12 '18 at 23:02
@Naidu i was trying this example from microsoft, but also didn't work.
– Kamilen
Nov 13 '18 at 10:57