I'm trying to move static TCHAR greeting[] = _T("123"); by using arrows, but it doesn't move at all. MessageBox is used as a confirmation of getting keyboard input.
void move(HWND hWnd, WPARAM wParam, LPARAM lParam, unsigned int *x, unsigned int * y) {
RECT rt;
if (wParam == VK_LEFT) {
GetClientRect(hWnd, &rt);
x = x - 5;
InvalidateRect(hWnd, &rt, FALSE);
MessageBox(hWnd, LPCSTR("123"), LPCSTR("123"), MB_OK);
}
if (wParam == VK_DOWN) {
GetClientRect(hWnd, &rt);
y = y - 5;
InvalidateRect(hWnd, &rt, FALSE);
}
if (wParam == VK_UP) {
GetClientRect(hWnd, &rt);
y = y + 5;
InvalidateRect(hWnd, &rt, FALSE);
}
if (wParam == VK_RIGHT) {
GetClientRect(hWnd, &rt);
x = x + 5;
InvalidateRect(hWnd, &rt, FALSE);
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT ps;
HDC hdc;
static TCHAR greeting[] = _T("123");
unsigned int x = 50;
unsigned int y = 50;
switch (message)
{
case WM_PAINT: {
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc,
x, y,
greeting, 3);
EndPaint(hWnd, &ps);
break;
}
case WM_KEYDOWN:
move(hWnd, wParam, lParam, &x, &y);
break;
case WM_DESTROY: {
PostQuitMessage(0);
break;
default: {
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
}
return 0;
}
}
Can somebody explain, what is wrong here? I'm still trying to learn about <Windows.h> so pls don't try to make the code much more complicated.
The x and y variables you are using to draw the text with are local to WndProc and are always reset to the initial values whenever a new message is received. You need to move their declarations outside of WndProc() (or at least make them static) so that their values can persist between messages. You can then update their values in your WM_KEYDOWN handler, and use their current values to draw the text in your WM_PAINT handler.
Also, your move() function is updating the pointers that are pointing at the x and y variables, it is not updating the values of the x and y variables themselves.
Try this instead:
void move(HWND hWnd, WPARAM wParam, LPARAM lParam, unsigned int *x, unsigned int * y) {
switch (wParam) {
case VK_LEFT:
*x -= 5;
InvalidateRect(hWnd, NULL, FALSE);
break;
case VK_DOWN:
*y -= 5;
InvalidateRect(hWnd, NULL, FALSE);
break;
case VK_UP:
*y += 5;
InvalidateRect(hWnd, NULL, FALSE);
break;
case VK_RIGHT:
*x += 5;
InvalidateRect(hWnd, NULL, FALSE);
break;
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
static TCHAR greeting[] = _T("123");
static unsigned int x = 50;
static unsigned int y = 50;
switch (message) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
TextOut(hdc,
x, y,
greeting, 3);
EndPaint(hWnd, &ps);
break;
}
case WM_KEYDOWN:
move(hWnd, wParam, lParam, &x, &y);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
While #Remy Lebeau's answer is right, I don't think I'd write the move function quite the way he has. I'd pass x and y by reference instead of using pointers, and I'd consolidate more of the code together. Oh, and since it doesn't use lParam, we might as well not pass it at all:
void move(HWND hWnd, WPARAM wParam, unsigned int &x, unsigned int & y) {
switch (wParam) {
case VK_LEFT:
x -= 5;
break;
case VK_DOWN:
y -= 5;
break;
case VK_UP:
y += 5;
break;
case VK_RIGHT:
x += 5;
break;
default: return;
}
InvalidateRect(hWnd, NULL, FALSE);
}
Also note, however that this has a little bit of a problem. Passing FALSE as the last parameter to InvalidateRect means it doesn't erase the background. If you move your string around much, you're probably going to end up with artifacts of the movement--fragments of the string showing up in both the previous and the current location.
One easy way to fix that is change the final parameter to TRUE. To make that a bit more efficient, you can calculate the rectangle covering the old and new locations of the string, and invalidate only that much. Alternatively, invalidate the old rectangle with TRUE and the new rectangle with FALSE, and Windows will figure things out from there.
Another possibility (slightly more work, but under the right circumstances it can pay off) is to create an off-screen bitmap containing both your message and a border in the background color that's large enough to cover up the message in its previous location. Then when you move it around, you just blit that bitmap to the screen.
That obviously only works if your background is a solid, uniform color though.
Related
Is there a way to automatically/by default set the mouse cursor to point inside a newly created window (using for e.g. the CreateWindow function)? I know that for keyboard input there is a SetFocus() function, but haven't found a similar function for a mouse.
As #IInspectable said, the following is the code implementation.
LRESULT CALLBACK WndProcNOP(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
RECT rcClient;
GetWindowRect(hwnd, &rcClient);
int x = rcClient.left + (rcClient.right - rcClient.left) / 2;
int y = rcClient.top + (rcClient.bottom - rcClient.top) / 2;
SetCursorPos(x, y);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
return 0;
}
LRESULT handleDoubleClicks(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, POINT ptLastClickPos, DWORD dwLastClickTime)
{
DWORD dwClickTime = GetMessageTime();
POINT ptClickPos = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
switch(message)
{
case WM_LBUTTONDOWN:
if (dwLastClickTime + GetDoubleClickTime() > dwClickTime
&& abs(ptLastClickPos.x - ptClickPos.x) < GetSystemMetrics(SM_CXDOUBLECLK)
&& abs(ptLastClickPos.y - ptClickPos.y) < GetSystemMetrics(SM_CYDOUBLECLK))
{
MessageBox(hWnd, TEXT("Double click"), TEXT("I appear when double clicked"), MB_OKCANCEL);
}
else
{
dwLastClickTime = dwClickTime;
ptLastClickPos = ptClickPos;
wchar_t waCoord[20];
wsprintf(waCoord, _T("(%i,%i)"), ptLastClickPos.x, ptLastClickPos.y);
MessageBox(hWnd, waCoord, _T("Left mouse button click"), MB_OK);
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
This is function i have made to handle double click:
This function is called when windows recieve WM_LBUTTONDOWN it will take the time of the message and coordinate of the click and will transfer it to the function here i want the fuction to recieve another message WM_LBUTTONDOWN and determine the time of the message and compare with the previous click time and the co ordinates to identify if it is a double click.
But this is not working .May be I am Wrong with the approach I am newbie Pls help me to solve this problem.
case WM_LBUTTONDOWN:
{
dwLastClickTime= GetMessageTime();
// SetTimer(hWnd,0,GetDoubleClickTime(),0);
ptLastClickPos.x = LOWORD(lParam);
ptLastClickPos.y = HIWORD(lParam);
handleDoubleClicks(hWnd, message, wParam, lParam, ptLastClickPos, dwLastClickTime);
}
You could use SetTimer, after one click, not immediately judge it as a click, but start the timer, check whether there is another click within the timer range, if there is, it is judged as double-click, if not, and the previous time is determined as a single click.
#define TIMER_ID 10
static int click_count = 0;
static POINT point = { 0 };
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_TIMER:
{
KillTimer(hWnd, TIMER_ID);
if (click_count == 1)
{
wchar_t waCoord[20];
wsprintf(waCoord, _T("(%i,%i)"), point.x, point.y);
MessageBox(hWnd, waCoord, _T("Left mouse button click"), MB_OK);
}
else if(click_count == 2)
{
MessageBox(hWnd, TEXT("Double click"), TEXT("I appear when double clicked"), MB_OKCANCEL);
}
else if (click_count == 3)
{
MessageBox(hWnd, TEXT("Triple click"), TEXT("I appear when triple clicked"), MB_OKCANCEL);
}
click_count = 0;
return 0;
}
break;
case WM_LBUTTONDOWN:
{
if (click_count == 0)
{
SetTimer(hWnd, TIMER_ID, GetDoubleClickTime(), NULL);
}
click_count++;
return 0;
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
And make sure your window doesn't have CS_DBLCLKS style. Otherwise, the second WM_LBUTTONDOWN message that would normally be generated becomes a WM_LBUTTONDBLCLK message, according to the document: Double Clicks
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.
Here is how I create my scrollbar:
CreateWindowEx(NULL, L"SCROLLBAR", NULL, WS_CHILD | WS_VISIBLE | scrollPos, x, y, width, height, parent, (HMENU)155, GetModuleHandle(NULL), NULL);
How could I restore my id (155) to know which control I will operate?
Here is how I tried:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
int id;
SCROLLBARINFO si;
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (message)
{
case WM_USER:
break;
case WM_USER + 1:
break;
case CONNECT_TO_SERVER:
break;
case WM_VSCROLL:
id = GetDlgCtrlID(hWnd);
//id isn't my 155 id, it is some kind of random number
//wmId isn't my 155 id
break;
case WM_COMMAND:
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I need to regain this id to choose which one of my controls should react inside my own grid.
id = GetDlgCtrlID(hWnd);
That's not correct, hWnd is the handle to your main window, not the scrollbar control. Also beware that WM_VSCROLL can be sent both by your main window and the scrollbar. The lParam argument tells you where it came from. Fix:
case WM_VSCROLL:
if (lParam != 0) {
int id = GetDlgCtrlID((HWND)lParam);
// etc..
}
break;
Currently, I am creating my own function onPress and onRelease. However, I am having problem with my onRelease function. Apparently, my onRelease kept on triggering even if I have not release my keyboard.
I suspected it has to do with the number of cycle inside the CPU but I wasn't sure of this theory. Somehow, maybe the cycle is slower than my frame, that why the PeerMessage return false? As no event was triggered.
Solution to it:
**Under the WinProc function create a new case called WM_KEYUP. This event will trigger once the user leave the button. It help to solve the number of cycle inside the CPU issued.
**
*Note:
I have update my detail of my code.
Description. Window Programming
I have two std::array that store my keyboard data
1) InputCurr
2) InputPrev
std::array<char, 256> inputPrev;
std::array<char, 256> inputCurr;
While(TRUE) {
std::copy(InputCurr.begin(), InputCurr.end(), InputPrev.begin());
inputCurr.fill(0);
while(PeekMessage (&uMsg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage (&uMsg);
DispatchMessage (&uMsg);
}
if(onReleased(x030)) //Button 0
//do something
}
char onReleased(char key)
{
return (inputCurr[key] && !inputPrev[key])? 1 : 0;
}
void VEInputMessage(WPARAM key)
{
inputCurr[key]= 1; //Set to true of the keyboard key
}
LRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC dc; /* device context */
PAINTSTRUCT ps; /* the paint struct */
RECT rect;
UNREFERENCED_PARAMETER(rect);
switch (msg)
{
/* when the window is created */
case WM_CREATE:
break;
/* when the rectangle is drawn */
case WM_LBUTTONDOWN:
break;
case WM_MOUSEMOVE:
break;
case WM_PAINT:
dc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
/* When it's time for the window to be closed and removed */
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
VEINPUT::VEInputMessage(wParam); //Updated the input key
if(wParam == AEVK_ESCAPE)
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}