Mouse Co-ordinates in Static Control
Progressing with my project, I need to determine/display the mouse co-ordinates when inside the control only.
I have posted what I have come up with; It works OK except that mouse co-ordinates are constantly being calculated/displayed.
How could I go about this (without subtracting control co-ords from form co-ords)?
BTW, how could I fix the display of the co-ordinates so
1) the background is the same color as the form, and
2) so the display is "cleared" so that the previous displayed # does not
show thru if the current displayed # is of smaller size?
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
const char g_szClassName[] = "myWindowClass";
#define IDS_PIC 100
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd = CreateWindowEx(
NULL,
g_szClassName,
"Window Title",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return static_cast<int>(Msg.wParam);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
HWND hPic01;
hPic01 = CreateWindow("static","",WS_CHILD|WS_VISIBLE|WS_BORDER,
125,10,250,250,hwnd,(HMENU)IDS_PIC,GetModuleHandle(NULL),NULL);
break;
case WM_CTLCOLORSTATIC:
{
SetBkMode((HDC)wParam, TRANSPARENT);
HBRUSH hbrBackground= CreateSolidBrush(RGB(255, 255, 204));
return (LONG)hbrBackground;
}
case WM_MOUSEMOVE:
HDC hDC;
PAINTSTRUCT ps;
int xPos;
int yPos;
char MousePos[32];
RECT rRect;
xPos = (LOWORD(lParam));
yPos = (HIWORD(lParam));
hDC = BeginPaint(hwnd, &ps);
wsprintf(MousePos, "x (%d), " " y (%d)", xPos, yPos);
SetRect(&rRect, 0, 0, 225, 200);
hDC = GetDC(hwnd);
DrawText(hDC, MousePos, -1, &rRect, NULL);
EndPaint(hwnd, &ps);
ReleaseDC(hwnd, hDC);
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Also
warning C4311: 'type cast' : pointer truncation from 'HBRUSH' to 'LONG'
at this line
return (LONG)hbrBackground;
[3906 byte] By [
mmscg] at [2007-11-19 1:36:51]

# 1 Re: Mouse Co-ordinates in Static Control
Don't use a static control inside your main window. There are alot of reasons (one of which is an OO reason), but one important reason for purposes of your project is that a static control returns HT_TRANSPARENT to WM_NCHITTEST, which means that a static control will never get any mouse messages.
Instead, register another window class, and create the window for it inside the WM_CREATE handler for the main window (just like you're doing now for the static). Let's say the class name is "MouseText". Then, handle mousemove messages in the WndProc for MouseText, not in the WndProc for your main window.
The way you handle mousemove messages is important. You should not do any painting in the WM_MOUSEMOVE handler. Instead, simply capture the mouse position, call ::ScreenToClient(g_hWnd /*of your main form, store this in a global variable*/, point) if you want to convert to client coordinates of your form, and store the result in another global variable. Then call ::Invalidate( hWndMouseText ), and wait for Windows to call your WM_PAINT handler. Do your DrawText functions in the WM_PAINT handler for the MouseText WndProc.
If you want the text background to be the same as the MouseText background, then you must call SetBkColor (or something like that) in the WM_PAINT handler.
Before Windows sends the WM_PAINT message, it will send the WM_ERASEBACKGROUND (not eh exact spelling) message to the MouseText window. This is your chance to erase old text. Simply DrawRect a rectangle of the background color onto the rectangle returned by GetClientRect. (It actaully might not be necessary to do this, depending on how you call ::Invalidate; check the documentation.)
Incidentally, one more benefit of registering the MouseText class is that you will not need to handle the WM_CTLCOLOR message. Simply register the class with the background brush that you want.
Regards,
Mike
# 2 Re: Mouse Co-ordinates in Static Control
Don't use a static control inside your main window. There are alot of reasons (one of which is an OO reason), but one important reason for purposes of your project is that a static control returns HT_TRANSPARENT to WM_NCHITTEST, which means that a static control will never get any mouse messages...
I just realized that although this is correct, it can be "fixed" by setting the SS_NOTIFY style when creating the static control.
But I still think you should use your own window class.
Mike
# 3 Re: Mouse Co-ordinates in Static Control
Thanks Mike for replying,
I will still very new to all of this; this will be my first winAPI C++ app if I can see it through.
I will add the SS_NOTIFY flag as you instructed and look to capture mouse movements inside the static control (only because I have this much already working).
I will search the net and look for examples to create a new window class inside the main window class.
mmscg at 2007-11-9 13:14:36 >

# 4 Re: Mouse Co-ordinates in Static Control
I added the SS_NOTIFY flag when creating the Static Control, and now the
mouse co-ordinates cease to display when it is positioned anywhere within the Static Control.
So I guess I am on the right track, as now the program can differentiate between
the form and the Static Control
BUT
I can't figure out how to determine/display the Static Control's mouse co-ordinates!!
Might it be possible that the code kawasaki056 provided in this link
http://www.dev-archive.com/forum/showthread.php?t=314681
could be modified to do what I need?
(I don't want to waste days tinkering with it if I am way off track)
mmscg at 2007-11-9 13:15:42 >

# 5 Re: Mouse Co-ordinates in Static Control
BUT
I can't figure out how to determine/display the Static Control's mouse co-ordinates!!
What do you mean by that ? Can you give an example ?
BTW:SS_NOTIFY is the way to go
# 6 Re: Mouse Co-ordinates in Static Control
...Might it be possible that the code kawasaki056 provided in this link
http://www.dev-archive.com/forum/showthread.php?t=314681
could be modified to do what I need?...
Probably not. The code posted by kawasaki is meant to determine when there has been a mouse-click on a control that belongs to another application. As far as we can determine, you are not interested in other applications; rather you want the coordinates of the mouse when it's over a window contained within the main window of your own application.
It really would be helpful if you described more about your objectives. Also, is this homework?
Mike
# 7 Re: Mouse Co-ordinates in Static Control
Mike:
It really would be helpful if you described more about your objectives. Also, is this homework?I plan to use this Static Control as a "container" for a user generated graphs etc.
(as user clicks and moves points in the "container" the graph will change).
I realize the "container" isn't necessarily required, but this is how I would like to do it.
Basically I guess I am trying to make this Static Control act something like VB's PictureBox Control with regards to trapping of events.
No this is not homework ( I am 45 yrs old and just trying to teach myself WinAPI C++)
kirants:
What do you mean by that ? Can you give an example ?
BTW:SS_NOTIFY is the way to goOK, here is what I have to this point:
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
const char g_szClassName[] = "myWindowClass";
#define IDS_PIC 100
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd = CreateWindowEx(
NULL,
g_szClassName,
"Window Title",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return static_cast<int>(Msg.wParam);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
HWND hPic01;
hPic01 = CreateWindow("static","",SS_NOTIFY|WS_CHILD|WS_VISIBLE|WS_BORDER,
125,10,250,250,hwnd,(HMENU)IDS_PIC,GetModuleHandle(NULL),NULL);
break;
case WM_CTLCOLORSTATIC:
{
SetBkMode((HDC)wParam, TRANSPARENT);
HBRUSH hbrBackground= CreateSolidBrush(RGB(255, 255, 204));
return (LONG)hbrBackground;
}
case WM_MOUSEMOVE:
HDC hDC;
PAINTSTRUCT ps;
int xPos;
int yPos;
char MousePos[32];
RECT rRect;
xPos = (LOWORD(lParam));
yPos = (HIWORD(lParam));
hDC = BeginPaint(hwnd, &ps);
wsprintf(MousePos, "x (%d), " " y (%d)", xPos, yPos);
SetRect(&rRect, 0, 0, 225, 200);
hDC = GetDC(hwnd);
DrawText(hDC, MousePos, -1, &rRect, NULL);
EndPaint(hwnd, &ps);
ReleaseDC(hwnd, hDC);
break;
case WM_COMMAND:
WORD wNotification;
wNotification = HIWORD(wParam);
WORD wId;
wId = LOWORD(wParam);
if(wNotification == STN_CLICKED && wId == IDS_PIC)
{
MessageBox(NULL, "STN_CLICKED", "Static Control Message", MB_OK);
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
mmscg at 2007-11-9 13:18:41 >

# 8 Re: Mouse Co-ordinates in Static Control
...No this is not homework ( I am 45 yrs old and just trying to teach myself WinAPI C++)
OK, sorry. It changes the way I answer the question.
...Basically I guess I am trying to make this Static Control act something like VB's PictureBox Control with regards to trapping of events...
Unfortunately, I don't know what a PictureBox looks like or acts.
Here's some quick code that illustrates what I was trying to describe in an earlier post:
#include "stdafx.h"
#include "resource.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szMainWindowClass[MAX_LOADSTRING]; // The main window class name
TCHAR szMouseWindowClass[MAX_LOADSTRING]; // The mouse window class name
POINT g_pt = {0,0};
// Foward declarations of functions included in this code module:
ATOM MyRegisterClassMain(HINSTANCE hInstance);
ATOM MyRegisterClassMouse(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK MouseWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDS_MAINWNDNAME, szMainWindowClass, MAX_LOADSTRING);
LoadString(hInstance, IDS_MOUSEWNDNAME, szMouseWindowClass, MAX_LOADSTRING);
MyRegisterClassMain(hInstance);
MyRegisterClassMouse(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_MOUSETEXT);
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
// This function and its usage is only necessary if you want this code
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
// so that the application will get 'well formed' small icons associated
// with it.
//
ATOM MyRegisterClassMain(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_MOUSETEXT);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = (LPCSTR)IDC_MOUSETEXT;
wcex.lpszClassName = szMainWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
return RegisterClassEx(&wcex);
}
ATOM MyRegisterClassMouse(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)MouseWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(255, 255, 204));
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szMouseWindowClass;
wcex.hIconSm = NULL;
return RegisterClassEx(&wcex);
}
//
// FUNCTION: InitInstance(HANDLE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szMainWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
switch (message)
{
case WM_CREATE:
CreateWindow(szMouseWindowClass, NULL, WS_CHILD|WS_VISIBLE,
40, 40, 100, 100, hWnd, NULL, hInst, NULL);
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
char MousePos[32];
RECT rRect;
wsprintf(MousePos, "x (%d), " " y (%d)", g_pt.x, g_pt.y);
SetRect(&rRect, 0, 0, 225, 200);
DrawText(hdc, MousePos, -1, &rRect, NULL);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
LRESULT CALLBACK MouseWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_MOUSEMOVE:
g_pt.x = (LOWORD(lParam));
g_pt.y = (HIWORD(lParam));
ClientToScreen( hWnd, &g_pt );
ScreenToClient( GetParent( hWnd ), &g_pt );
InvalidateRect( GetParent( hWnd ), NULL, TRUE );
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
// Mesage handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
}
return FALSE;
}
The entire project (VC++ 6.0) is attached in the following post. With this code, you get something that loks like this. Is this what you want?
Mike
# 9 Re: Mouse Co-ordinates in Static Control
Here's the project (VC++ 6.0). The download includes a release-version of the exe, that you can run (without the necessity of compiling the project) to see if it's close to what you want.
Mike
# 10 Re: Mouse Co-ordinates in Static Control
Just wanted to repeat what MikeAThon posted.. I guess you are looking for how to show the co-ords of the mouse relative to the window( static ). Now, there are 2 coords. Screen coords and client coords. Screen coords are the coords taking the whole screen into consideration disregarding the window.. So a screen coord is the 0,0 at the top left and the max x, max y at the bottom right corner..
Client coords is something which is something in the context of a window. A window has a client area and non client area. The client area is the area in window not including the title bar, the resizing border, etc.. I hope u get the idea. The test is all client area. Now, given a screen coords, there can be so many windows to which the screen coord belongs. Say you have a dialog, and a static in it. Now, when a given coord on the static control in screen coords can be said to be within the client area of the static , AND also in the client area of the dialog which hosts the static.. What will change is the client coords. If you take the point relative to top left of the static area, you get the client coords of the point within the static window.. Else, if you take that poinyt relative to the top left of the dialog, then you may end up in a different coord.
The API used for this is ScreenToClient passing the screen coord and the hWnd of the window relative to which you want to know the client coord.
BTW !! It's good to know you are learning Win32.. I wish I have the same zeal to learn something at ur age.. Welcome to dev-archive..
:wave:
# 11 Re: Mouse Co-ordinates in Static Control
Mike:
Yes, the project you put together is what I am after! :)
Thanks for taking the time to post a working sample, because for me this is the best way to learn.
(took you about an hour for what I have been working on 3 nights already :D )
I will have to spend tomorrow night digesting your code.
I will then do as kirants points out, and use ScreenToClient to get the mouse co-ordinates relative to the Static Control
(in VB this is automatic).
Is your method sub-classing?
Is the way in which I was going about it, impossible to get the mouse co-ordinates in the Static?
kirants:
BTW !! It's good to know you are learning Win32.. I wish I have the same zeal to learn something at ur age.. Welcome to dev-archive..Thank you! I think I will learn alot here !!
mmscg at 2007-11-9 13:22:47 >

# 12 Re: Mouse Co-ordinates in Static Control
I'm glad it's helpful.
With respect to coordinates, the WM_MOUSEMOVE handler converted to coordinates of the main window, since that's what I thought you wanted. Look at this code:
case WM_MOUSEMOVE:
g_pt.x = (LOWORD(lParam));
g_pt.y = (HIWORD(lParam));
ClientToScreen( hWnd, &g_pt );
ScreenToClient( GetParent( hWnd ), &g_pt );
InvalidateRect( GetParent( hWnd ), NULL, TRUE );
break;
In the WM_MOUSEMOVE message, Windows gives you coordinates in client coordinates of the window. The above code first converts that to screen coordinates (which is often used as a reference coordinate system) using ClientToScreen, relative to the MouseWndProc window. The next call to ScreenToClient converts the screen coordinates to client coordinates, but since we pass it the hWnd of the parent (i.e., the MainWnd), we get coordinates relative to the main window.
But if you want coordinates relative to the MouseWndProc window, then niether function is necessary, for the reason that those ore the coordinates that Windows hands you to begin with.
As for subvlassing, no, it's not subclassing. Subclassing would have been another way to write your program, using a static window like you started with. But subclassing is a bit more complicated to understand than the above program. With subclassing, you replace the WndProc of a window with your own user-defined WndProc. After subclassing, instead of passing messages to the original WndProc, Windows will pass the message to your own WndProc. You can do whatever you want with the message, but usually you process just one or two of the messages and pass the remaining messages along to the original WndProc. This lets you "intercept" messages, proces the few that you want, and then let the original WndProc do most of the "default" work.
That probably doesn't seem too useful to you right now, since you're writing your own WndProc's anyway. The advantage comes about because you can subclass the WndProc for pre-existing Windows classes like static classes, changing just a little bit of their behavior to suit your precise needs. So, in your original problem, you could have started with a static control, subclassed it so as to intercept the WM_MOUSEMOVE messages and processed them as shown above, and then let the original static class process the remaining messages.
Subclassing is useful where the pre-existing behavior of the Windows class is close to what you want, and needs just a few modifications. But in this situation, and at your level of experience, I think the solution above is cleaner, and simpler to understand.
Mike
# 13 Re: Mouse Co-ordinates in Static Control
- 0 error(s), 0 warning(s)
I never get that message during build... except for tonight :D
I've taken MikeAThon's "tutorial" code and fit it into a structure I've become comfortable with.
I've left things out (for now), that I've never seen or used before.
I figure it's no use using something until I understand it, or I won't learn.
Anyways, it runs great except for the flickering.
Is there any way to get rid of this flickering?
Also, MikeAThon corrected the co-ordinate display to shrink & expand as necessary,
but for the life of me I can't spot the change made that corrects this.
Here is my current code...
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
const char g_szMainWindowClassName[] = "myMainWindowClass";
const char g_szStaticWindowClassName[] = "myMouseWindowClass";
// Foward declarations
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK StaticWndProc(HWND, UINT, WPARAM, LPARAM);
// Global variables
HINSTANCE hInst;
POINT g_pt = {0,0};
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc; // for Main Window Class
WNDCLASSEX wcex; // for Static Window Class
HWND hwnd;
MSG Msg;
hInst = hInstance; // Store instance handle in our global variable
// Register the Main Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szMainWindowClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Main Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Register the Static Window Class
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)StaticWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(255, 255, 204));
wcex.lpszMenuName = NULL;
wcex.lpszClassName = g_szStaticWindowClassName;
wcex.hIconSm = NULL;
if(!RegisterClassEx(&wcex))
{
MessageBox(NULL, "Static Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Create the Main Window
hwnd = CreateWindowEx(
NULL,
g_szMainWindowClassName,
"Static Control Test",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Main Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Display the Main Window
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Create message loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return static_cast<int>(Msg.wParam);
}
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_CREATE:
CreateWindow(g_szStaticWindowClassName, NULL, WS_CHILD|WS_VISIBLE|WS_BORDER,
125, 10, 250, 250, hWnd, NULL, hInst, NULL);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
char MousePos[32];
RECT rRect;
wsprintf(MousePos, "x (%d), " " y (%d)", g_pt.x, g_pt.y);
SetRect(&rRect, 0, 0, 225, 200);
DrawText(hdc, MousePos, -1, &rRect, NULL);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_MOUSEMOVE:
g_pt.x = (LOWORD(lParam));
g_pt.y = (HIWORD(lParam));
InvalidateRect( GetParent( hWnd ), NULL, TRUE );
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
mmscg at 2007-11-9 13:24:48 >

# 14 Re: Mouse Co-ordinates in Static Control
To prevent flickering, when you create the main window, change this
// Create the Main Window
hwnd = CreateWindowEx(
NULL,
g_szMainWindowClassName,
"Static Control Test",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
to this
// Create the Main Window
hwnd = CreateWindowEx(
NULL,
g_szMainWindowClassName,
"Static Control Test",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, // <<<-- note extra style
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
The extra style tell Windows not to erase child windows when drawing the background for the main window. That's what's causing the flickering: the very-quick erasing of the main window (to its white background as set by the background brush) followed by the painting of the child window (with its pale yellow color) on top of the white background. With WS_CLIPCHILDREN, Windows doesn't erase the background for the child (it clips it -- get it?) resulting in flicker-free drawing. This works for this application because the child window's position doesn't change; you need more work if you use WS_CLIPCHILDREN and the position of the child window changes.
I don't understand what you mean by "Also, MikeAThon corrected the co-ordinate display to shrink & expand as necessary, but for the life of me I can't spot the change made that corrects this." What do you mean by "shrink and expand"?
Mike
# 15 Re: Mouse Co-ordinates in Static Control
In the first screenshot, you can see that if the most recent text being displayed was of shorter in length than previous text,
the longer text would show thru.
This is corrected in the second.
WS_CLIPCHILDREN eliminates the flicker as you said! :D
...Windows doesn't erase the background for the child (it clips it -- get it?)I want to say yes, but quite honestly... no.
Why do my screenshots not display?
mmscg at 2007-11-9 13:26:46 >

# 16 Re: Mouse Co-ordinates in Static Control
Why do my screenshots not display?
The screenshots don't display because you have more than one attachment. Make a single attachment per post, and the screenshot will show.
In the first screenshot, you can see that if the most recent text being displayed was of shorter in length than previous text,
the longer text would show thru.
This is corrected in the second.
The reason for this difference in behavior is because of the difference in where you're drawing. In the "before" screenshot, you were drawing from within the WM_MOUSEMOVE handler. It's usually wrong to draw outside of the WM_PAINT handler (usually, but not always; one great exception is when you draw "live", while the user is dragging or clicking), for the reason that the WM_PAINT message is the core of the mechanism that Windows uses to tell your application that it needs to re-draw itself.
Play around a bit more with the "before" application, and you'll see what I mean. Minimize and then restore it. What does the restored screen look like? Blank. right? What happened to the text giving mouse coordinates? Well, Windows sent your "before" applicatio a WM_PAINT message when the app was restored, but since your app paints only in the WM_MOUSEMEOVE handler (and in fact doesn't even have a WM_PAINT handler) your text disappeared.
Try another experiment and drag a window (like Notepad or this browser window or whatever) over your application, a bit at a time. What happens to the text this time? well, whatever was obscured by another window doesn't get re-painted when the obscuring window is moved away. Do you understand that the reason for this is the same as when you minimze and then restore the app?
Now look at the "after" code and you'll see that all painting is done in the WM_PAINT handler. In the WM_MOUSEMOVE ahndler we don't do any drawing. All we do is store the mouse coordinates, and then call InvalidateWindow. This is a commonly-called function. Check the documentation for it (at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/pantdraw_7ano.asp ) and you'll see that:
The InvalidateRect function adds a rectangle to the specified window's update region. The update region represents the portion of the window's client area that must be redrawn.
...
The system sends a WM_PAINT message to a window whenever its update region is not empty and there are no other messages in the application queue for that window.
So, in response to InvalidateRect, Windows invalidates the entire region of your main window's client area. Then, when there are no messages in the message queue (which is probably immediate for this app), Windows sends your app a WM_PAINT message and, eh voila!, your app responds by drawing the text for the mouse coordinates.
But before that happens (check the docs again), because we called InvalidateRect with TRUE as the bErase (third) parameter, when we call BeginPaint in the WM_PAINT handler, Windows sends our app a WM_ERASEBKGND message. We don't have a handler for WM_ERASEBKGND so the default handler gets called and erases the background of the window using the hbrBackground brush that we specified when the windows class was registered (in the RegisterClassEx function). So, old text is erased before new text is drawn.
As a result, the "after" application behaves very differently from the "before" app. Again try the above experiments with minimize/restore, and dragging-another-window-in-front. Think about the Wm-PAINT messages that are being sent. Do you now understand why this works correctly?
This should also clarify what WS_CLIPCHILDREN does. If this style is set, then during processing of the WM_ERASEBKGND message, Windows does not erase the background of any region which is occupied by a child window.
Regards,
Mike
# 17 Re: Mouse Co-ordinates in Static Control
As I read (and re-read) your last post, I am beginning to understand.
Also it helped to try the "experiments" - minimize/restore etc.
I am now going to begin work on the graphics part of my project,
and your explanation of how Windows paints/erases etc should help me decide where to put things.
I will be doing "live" drawing, so I see this is going to be tricky.
I'll see what I can come up with. :D
mmscg at 2007-11-9 13:28:57 >

# 18 Re: Mouse Co-ordinates in Static Control
OK then !
if you are going to draw using "DrawText" , no longer you do not have to use "Static Control" !!
you settele your own coodinates , left,top,right,bottom. and that is your original Static control ! static controls is just useful because you dont need handle "WM_PAINT" and "WM_ERASEBKGND" . to show the texts you use SetWindowText or, SetDlgItemText( may be it's macro...?) , well, WM_SETTEXT is also available.. but you are already doing what jobs this control does.
you looks choosing way what makes things worse ...
flicks => removes WM_CLIPCHILDREN , then yes, strange drawing like mentioned.
it does occur . very funny though .
ok if you want to stick to your way, handle WM_ERASEBKGND of parent window.and , you yourself "erase bkground" , with, well, FillRect . the coordinates is needed for this func, so, in parent proc,
case WM_ERASEBKGND:
{ HDC hdc = (HDC)wp ; //
RECT rc ; GetWindowRect( hStatic , &rc ) ;
MapWindowRect( NULL , hWnd , &rc ) ; // try use this,including windowsx.h
FillRect( hdc , &rc , GetStockObject( WHITE_BRUSH )) ;
// Draw your own text here.
return 1 ; // return non-zero value if processed.
}
break ; // though it's no longer necessary...
if you dont use static control no longer, you can try..
RECT rc = { 5,5,65,20 } ; // this is your static control !
char sztext[256] ;
....without making static control...
case WM_MOUSEMOVE :
{ POINT pt = { LOWORD(lParam) , HIWORD(lParam) } ;
if( PtInRect( &rc , pt )
{ wsprintf( sztext , "x=%d y=%d" , pt.x, pt.y) ;}
}
break ;
...
case WM_PAINT :
// draw the "sztext"
// if you like draw rectangle border with ,
// "Rectangle()" or , to be more fashionable, you can use DrawFrameControl(), or DrawEdge() is also interesting.
return 0 ;
// case WM_ERASEBKGND :
// break ; // no more needed !! brabo-
# 19 Re: Mouse Co-ordinates in Static Control
Hi, kawasaki056. With respect, I disagree with just about everything in your post.
OK then !
if you are going to draw using "DrawText" , no longer you do not have to use "Static Control" !!
The first issue is frankly unrelated to the second, and DrawText could have been used with a static control. I think you might have become confused over what the OP wanted to display in the static control; he actually didn't want to display anything. It was the main window where he wanted to display text. His intended use for the static was merely to identify/define the region over which he was interested in the mouse coordinates.
you settele your own coodinates , left,top,right,bottom. and that is your original Static control ! static controls is just useful because you dont need handle "WM_PAINT" and "WM_ERASEBKGND" . to show the texts you use SetWindowText or, SetDlgItemText( may be it's macro...?) , well, WM_SETTEXT is also available.. but you are already doing what jobs this control does.
Again, this only makes sense if the OP wanted to display text in the static control. He didn't.
you looks choosing way what makes things worse ...
flicks => removes WM_CLIPCHILDREN , then yes, strange drawing like mentioned.
it does occur . very funny though .
ok if you want to stick to your way, handle WM_ERASEBKGND of parent window.and , you yourself "erase bkground" , with, well, FillRect . the coordinates is needed for this func, so, in parent proc,
He did not make things "worse". Quite the contrary, he made things much more object-oriented. Each object does exactly what what it should and no more. The child window merely senses when the mouse is inside it, stores the mouse coordinates, and then tells the main window that there are new mouse coordinates for display. It's the main window that's responsible for all drawing, as it should be.
Frankly, the method outlined in your post is very much "procedural thinking" and not well-suited to programming in Windows (and other OO-like frameworks).
case WM_ERASEBKGND:
{ HDC hdc = (HDC)wp ; //
RECT rc ; GetWindowRect( hStatic , &rc ) ;
MapWindowRect( NULL , hWnd , &rc ) ; // try use this,including windowsx.h
FillRect( hdc , &rc , GetStockObject( WHITE_BRUSH )) ;
// Draw your own text here.
return 1 ; // return non-zero value if processed.
}
break ; // though it's no longer necessary...
This code is unnecessary. The window class defines its own background brush, which is used automatically when the window erases its background. And I would recommend againt the use of MapWindowRect. This is a WinCE macro, and I wouldn't use it because of the WinCE dependency. Anyway, it simply calls MapWindowPoints twice, and since MapWindowPoints is a standard GDI function, why not use MapWindowPoints?
if you dont use static control no longer, you can try..
RECT rc = { 5,5,65,20 } ; // this is your static control !
char sztext[256] ;
....without making static control...
case WM_MOUSEMOVE :
{ POINT pt = { LOWORD(lParam) , HIWORD(lParam) } ;
if( PtInRect( &rc , pt )
{ wsprintf( sztext , "x=%d y=%d" , pt.x, pt.y) ;}
}
break ;
...
case WM_PAINT :
// draw the "sztext"
// if you like draw rectangle border with ,
// "Rectangle()" or , to be more fashionable, you can use DrawFrameControl(), or DrawEdge() is also interesting.
return 0 ;
// case WM_ERASEBKGND :
// break ; // no more needed !! brabo-
This actually looks fine, provided that the WM_MOUSEMOVE code is in the child window and the WM_PAINT code is in the main window. In fact, it's nearly identical to the code given by me above.
With respect to the flickering issue, I strongly believe that it's clearly desireable to define the WS_CLIPCHILDREN style to overcome this problem. That's what it's there for. It's more ordinary to see the style in dialog classes, where there are many child windows (all the buttons, edit controls, static text/icon controls, etc are all each an individual child window), but it's perfectly acceptable to use WS_CLIPCHILDREN whereever there are child windows.
But it's equally true that the flicker problem occurs because we invalidate the entire client rectangle of the main window. We know the entire window doesn't need invalidation (just the part of the window with text needs to be invalidated), so our programming is "lazy" since we didn't confine invalidation to precisely the area that needs it. This is easily remedied, and instead of posting the code, I invite our OP to figure out how to invalidate just the text area of the main window, so as to avoid flicker without the need for WS_CLIPCHILDREN.
Regards,
Mike
# 20 Re: Mouse Co-ordinates in Static Control
I am posting my code with the latest "drawing" additions.
I've hit a snag.
The 1st thing that the program is supposed to do is load 2 arrays
(I have put this in Initialize function).
Next, 2 lines are to be drawn in the "Static" from the data obtained in Initialize
(I have put this in DrawBezier function).
I call these 2 functions in WM_CREATE of MainWndProc.
They do execute because if I uncomment the MessageBox calls in these
2 functions they both display, however my two lines are not drawn in
my Static control.
I thought there was a problem in my "drawing" code, however I tested using the exact same code
in the WM_LBUTTONDOWN of MainWndProc and there it works.
This I find very strange.
Even stranger, is that when I uncomment the 2 MessageBox calls in Initialize and DrawBezier,
the 2 lines don't draw when I press the left mouse button :confused:
Can someone see what I am doing wrong.
kawasaki056:
Being a complete rookie at all of this, I am confused as to what you are asking/telling me.
I appreciate your response however. :)
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
const char g_szMainWindowClassName[] = "myMainWindowClass";
const char g_szStaticWindowClassName[] = "myStaticWindowClass";
// Foward declarations
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK StaticWndProc(HWND, UINT, WPARAM, LPARAM);
void Initialize(HINSTANCE);
void DrawBezier(HWND);
// Global variables
HINSTANCE hInst;
HWND hwnd;
HWND hStatic;
POINT g_pt = {0,0};
int xPt[3], yPt[3];
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc; // for Main Window Class
WNDCLASSEX wcex; // for Static Window Class
//HWND hwnd;
MSG Msg;
hInst = hInstance; // Store instance handle in our global variable
// Register the Main Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szMainWindowClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Main Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Register the Static Window Class
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)StaticWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(255, 255, 204));
wcex.lpszMenuName = NULL;
wcex.lpszClassName = g_szStaticWindowClassName;
wcex.hIconSm = NULL;
if(!RegisterClassEx(&wcex))
{
MessageBox(NULL, "Static Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Create the Main Window
hwnd = CreateWindowEx(
NULL,
g_szMainWindowClassName,
"Static Control Test",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Main Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Display the Main Window
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Create message loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return static_cast<int>(Msg.wParam);
}
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
HDC hdc2;
switch (message)
{
case WM_CREATE:
hStatic = CreateWindow(g_szStaticWindowClassName, NULL, WS_CHILD|WS_VISIBLE|WS_BORDER,
125, 10, 250, 250, hWnd, NULL, hInst, NULL);
Initialize(hInst);
DrawBezier(hWnd);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
char MousePos[32];
RECT rRect;
wsprintf(MousePos, "x (%d), " " y (%d)", g_pt.x, g_pt.y);
SetRect(&rRect, 0, 0, 225, 200);
SetBkColor(hdc, 0x003FFF00);
DrawText(hdc, MousePos, -1, &rRect, NULL);
EndPaint(hWnd, &ps);
break;
case WM_LBUTTONDOWN:
DrawBezier(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
//
break;
case WM_MOUSEMOVE:
g_pt.x = (LOWORD(lParam));
g_pt.y = (HIWORD(lParam));
InvalidateRect( GetParent( hWnd ), NULL, TRUE );
break;
case WM_LBUTTONDOWN:
MessageBox(NULL, "WM_LBUTTONDOWN", "StaticWndProc", MB_ICONEXCLAMATION | MB_OK);
DrawBezier(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
void DrawBezier(HWND hWnd)
{
//MessageBox(NULL, "DrawBezier", "For debugging", MB_ICONEXCLAMATION | MB_OK);
int ax, bx, cx, ay, by, cy, xt, yt, t, i;
// Draw control lines
HDC hdc2;
hdc2 = GetDC(hStatic);
MoveToEx(hdc2, xPt[0], yPt[0], NULL);
LineTo(hdc2, xPt[1], yPt[1]);
MoveToEx(hdc2, xPt[2], yPt[2], NULL);
LineTo(hdc2, xPt[3], yPt[3]);
ReleaseDC(hStatic, hdc2);
}
void Initialize(HINSTANCE hInst)
{
//MessageBox(NULL, "Initialize", "For debugging", MB_ICONEXCLAMATION | MB_OK);
xPt[0] = 20; xPt[1] = 40; xPt[2] = 40; xPt[3] = 20;
yPt[0] = 20; yPt[1] = 80; yPt[2] = 160; yPt[3] = 180;
}
mmscg at 2007-11-9 13:31:55 >

# 21 Re: Mouse Co-ordinates in Static Control
This line:
int xPt[3], yPt[3];
should be
int xPt[4], yPt[4]; // <<-- you have four elements in the array, not three
As for drawing the Bezier, remember what we're telling you: don't draw anywhere except in the WM_PAINT handler (at least for now, anyway).
What are you trying to do? Draw a Bezier whenever and wherever you click?
Mike
# 22 Re: Mouse Co-ordinates in Static Control
This line:
int xPt[3], yPt[3];
should be
int xPt[4], yPt[4]; // <<-- you have four elements in the array, not three
In VB Dim xPt(3) As Integer would create a 4 element array (0, 1, 2, & 3)
As for drawing the Bezier, remember what we're telling you: don't draw anywhere except in the WM_PAINT handler (at least for now, anyway).
I did think about this, but I don't think there is any other way in this case...
at least none that I can think of.
What are you trying to do? Draw a Bezier whenever and wherever you click?
Not exactly...
When the program first executes it will draw a bezier based on the values
loaded into the 2 arrays. It will also draw with these "control" points.
When the user clicks and drags on any of these 4 control points, the bezier curve
will keep changing while being dragged.
Nothing too dramatic, just something I chose to do for my first win32 project that
I thought would require a mixed bag of techniques that I could learn.
mmscg at 2007-11-9 13:33:59 >

# 23 Re: Mouse Co-ordinates in Static Control
Hi
yes
This code is unnecessary.
I thought it too after posting. but,
knowing both of "ERASING" and "PAINTING" was essential I thought .
so I might write such message of no use.
but
And I would recommend againt the use of MapWindowRect. This is a WinCE macro, and I wouldn't use it because of the WinCE dependency. Anyway, it simply calls MapWindowPoints twice, and since MapWindowPoints is a standard GDI function, why not use MapWindowPoints?
? I don't know well about WinCE (I don't even know what it is )
but, MapWindowRect( a, b, c ) is just the same as MapWindowPoints( (a), (b) , (LPPOINT)(c), 2 ) in windowsx.h. Map..Rect is identical to Map..Points. it is not dependency nor not calling Map..Points twice. paramater IS "2".nonsense. or may be my computer differs from yours. but this suggestion I did because I just thought many of variations of functions will perhaps be helpful to him. because when knowing little people are blind.
well ,,,, what is win ce...and,.
yes, ee, hey! at last mouse captured ?
oh ok ! but, creating "registering new window class" is so often the case ? it's rather rare case, isnt it?
why are dening my "WM_PARENTNOTIFY" ? this is not the method of chatching other application's mouse pointer , is it ? hah? I am explaning about both , using WM_PARENTNOTIFY to get the mouse message of "owning" chind windows and, using GetCapture() to get the mouse position of "other app's".
the best way of catching mouse event , nor "newly registering class" nor of course subclassing way , which can be thought of as a solution of whole topics,
is WM_PARENTNOTIFY I believe and it is.
but now, what I was to tell mmscg was,you can use PolyBezier() function if you draw Bezier.and, perhaps your project meets problem..when want to erase the line previously drawn (and left being drawn)
so in such situation, examine SetROP2() function. draw a line, and call SetROP2() function and performed , again draw just exactly overlapping the previous line, then the line disappear ! in detail , listen to Mike !
# 24 Re: Mouse Co-ordinates in Static Control
Attached are 2 screenshots
(I had to zip them however because they were a little larger than the maximum allowed);
the 1st is from my working VB app, and
the second from my win32 C++ app as it's present point.
If the user clicks and drags any of the 4 control points (black dots at end of blue lines),
the resultant red Bezier curve will keep updating while dragging continues.
I think I am almost there in my C++ version, except that I can't figure out why the
lines will not draw at WM_CREATE, only at WM_LBUTTONDOWN.
I realize that the initial display of the Bezier and control lines could easly be coded into WM_PAINT,
but this won't work for subsequent draws required by the "live" feature.
(This is why I needed the mouse co-ordinates inside the Static Control, which is now working :D )
kawaski056:
but now, what I was to tell mmscg was,you can use PolyBezier() function if you draw Bezier.and, perhaps your project meets problem..when want to erase the line previously drawn (and left being drawn)
My project is only for the reason to help learn C++ and win32, and not really to draw the Bezier... although I did not know this function existed... I will check it out.
Here is my current code:
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <math.h>
const char g_szMainWindowClassName[] = "myMainWindowClass";
const char g_szStaticWindowClassName[] = "myStaticWindowClass";
// Foward declarations
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK StaticWndProc(HWND, UINT, WPARAM, LPARAM);
void Initialize(HINSTANCE);
void DrawBezier(HWND);
// Global variables
HINSTANCE hInst;
HWND hwnd;
HWND hStatic;
POINT g_pt = {0,0};
int xPt[4], yPt[4];
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc; // for Main Window Class
WNDCLASSEX wcex; // for Static Window Class
//HWND hwnd;
MSG Msg;
hInst = hInstance; // Store instance handle in our global variable
// Register the Main Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szMainWindowClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Main Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Register the Static Window Class
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)StaticWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(255, 255, 204));
wcex.lpszMenuName = NULL;
wcex.lpszClassName = g_szStaticWindowClassName;
wcex.hIconSm = NULL;
if(!RegisterClassEx(&wcex))
{
MessageBox(NULL, "Static Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Create the Main Window
hwnd = CreateWindowEx(
NULL,
g_szMainWindowClassName,
"Cubic Bezier",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Main Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Display the Main Window
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Create message loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return static_cast<int>(Msg.wParam);
}
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
HDC hdc2;
switch (message)
{
case WM_CREATE:
hStatic = CreateWindow(g_szStaticWindowClassName, NULL, WS_CHILD|WS_VISIBLE|WS_BORDER,
125, 10, 250, 250, hWnd, NULL, hInst, NULL);
Initialize(hInst);
DrawBezier(hWnd);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
char MousePos[32];
RECT rRect;
wsprintf(MousePos, "x (%d), " " y (%d)", g_pt.x, g_pt.y);
SetRect(&rRect, 0, 0, 225, 200);
SetBkColor(hdc, 0x003FFF00);
DrawText(hdc, MousePos, -1, &rRect, NULL);
EndPaint(hWnd, &ps);
break;
case WM_LBUTTONDOWN:
DrawBezier(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
//
break;
case WM_MOUSEMOVE:
g_pt.x = (LOWORD(lParam));
g_pt.y = (HIWORD(lParam));
InvalidateRect( GetParent( hWnd ), NULL, TRUE );
break;
case WM_LBUTTONDOWN:
MessageBox(NULL, "WM_LBUTTONDOWN", "StaticWndProc", MB_ICONEXCLAMATION | MB_OK);
DrawBezier(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
void DrawBezier(HWND hWnd)
{
//MessageBox(NULL, "DrawBezier", "For debugging", MB_ICONEXCLAMATION | MB_OK);
float ax, bx, cx, ay, by, cy, xt, yt, t;
int i;
// Calculate
cx = 3 * (xPt[1] - xPt[0]);
bx = 3 * (xPt[2] - xPt[1]) - cx;
ax = xPt[3] - xPt[0] - cx - bx;
cy = 3 * (yPt[1] - yPt[0]);
by = 3 * (yPt[2] - yPt[1]) - cy;
ay = yPt[3] - yPt[0] - cy - by;
// ?
// Draw control lines
HDC hdc2;
hdc2 = GetDC(hStatic);
HPEN bluePen;
//whitePen = CreatePen(PS_SOLID, 1, RGB(255,255,255));
bluePen = CreatePen(PS_SOLID, 1, 0x00FF0000);
SelectObject(hdc2,bluePen);
MoveToEx(hdc2, xPt[0], yPt[0], NULL);
LineTo(hdc2, xPt[1], yPt[1]);
MoveToEx(hdc2, xPt[2], yPt[2], NULL);
LineTo(hdc2, xPt[3], yPt[3]);
DeleteObject(bluePen);
ReleaseDC(hStatic, hdc2);
//// Draw Bezier curve
HDC hdc3;
hdc3 = GetDC(hStatic);
for (t = 0; t <= 1; t += 0.001)
{
xt = ax * pow (t, 3) + bx * pow (t, 2) + cx * t + xPt[0];
yt = ay * pow (t, 3) + by * pow (t, 2) + cy * t + yPt[0];
// plot a single point -> maybe could use Polyline somehow?
SetPixel(hdc3, xt, yt, 0x000000FF);
}
ReleaseDC(hStatic, hdc3);
// display co-ordinates
HDC hdc4;
hdc4 = GetDC(hStatic);
HPEN blackPen;
HBRUSH blackBrush;
blackPen = CreatePen(PS_SOLID, 1, RGB(0,0,0));
blackBrush = CreateSolidBrush(RGB(0,0,0));
SelectObject(hdc4, blackPen);
SelectObject(hdc4, blackBrush);
for (i = 0; i <= 3; i ++)
{
Ellipse(hdc4, xPt[i]-1, yPt[i]-1, xPt[i]+3, yPt[i]+3); // draw the circle
SetBkMode(hdc4, TRANSPARENT);
//SetBkColor(hdc4, RGB(255,255,255)); // doesn't seem to work
SetTextColor(hdc4, RGB(0,0,0));
TextOut(hdc4, xPt[i], yPt[i], "x,y", 12);
}
DeleteObject(blackBrush);
DeleteObject(blackPen);
ReleaseDC(hStatic, hdc4);
}
void Initialize(HINSTANCE hInst)
{
//MessageBox(NULL, "Initialize", "For debugging", MB_ICONEXCLAMATION | MB_OK);
xPt[0] = 25; xPt[1] = 170; xPt[2] = 110; xPt[3] = 20;
yPt[0] = 60; yPt[1] = 20; yPt[2] = 150; yPt[3] = 210;
}
mmscg at 2007-11-9 13:35:57 >

# 25 Re: Mouse Co-ordinates in Static Control
Very encouraging results -- keep it up !!
Anyway, when you have difficulties like this, it always pays to read the documentation carefully. Here's what MSDN says about WM_CREATE (from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowmessages/wm_create.asp ):
The WM_CREATE message is sent when an application requests that a window be created by calling the CreateWindowEx or CreateWindow function. (The message is sent before the function returns.) The window procedure of the new window receives this message after the window is created, but before the window becomes visible.
So, the reason that the bezier doesn't draw in WM_CREATE is that the window isn't visible yet. The main function of the CreateWindowEx call (in your main application) is to create the window, not to show it; that's the purpose of the next call in your main application, which is ShowWindow.
Now, if you had put the DrawBezier function in WM_PAINT, then it would have been called right after the first WM_PAINT message.... Hmm, but I digress ;) ...
Mike
PS: If you should decide to put DrawBezier in your WM_PAINT handler, then from the looks of your screen-shot, you should put it in the WM_PAINT handler for the child window, not the main window.
# 26 Re: Mouse Co-ordinates in Static Control
...The window procedure of the new window receives this message after the window is created, but before the window becomes visible.
Funny, I had the same problem (similar anyway) in coding the VB version and
I had to move Initialize and DrawBezier from the Form_Load event to the Form_Activate event for the same reason.
Maybe I am not understanding properly WM_PAINT, so I will move those functions there and see what happens.
BTW, in looking over my VB version I noticed both the Form and PictureBox controls has a ClipControls property...
I never knew what these properties actually did... now I know. :D
mmscg at 2007-11-9 13:38:00 >

# 27 Re: Mouse Co-ordinates in Static Control
Oh yeah -- instead of that funky DrawBezier function of yours, you can use the Windows GDI function of PolyBezier. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/linecurv_2d2q.asp (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/linecurv_2d2q.asp) and (for a simple example) http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/pantdraw_6xf2.asp
Mike
# 28 Re: Mouse Co-ordinates in Static Control
OK, I moved
Initialize(hInst);
DrawBezier(hWnd);
from WM_CREATE of MainWndProc to WM_PAINT of StaticWndProc
It displays perfectly when program is executed. :D
I will check out the links to the PolyBezier function... thanks!
I will try implementing these after I get this port working...
making changes "the way things should be done in C++"
mmscg at 2007-11-9 13:39:59 >

# 29 Re: Mouse Co-ordinates in Static Control
IT WORKS! :D
I have a lot of bugs to fix however.
The first is that I can't find a way to clear the "Static" between "paints".
As it is right now, when I drag, I get a mess of lines all over.
I've searched using the keywords GDI and clear, erase etc. but can find anything.
Also, for debugging, I like to sometimes display a message box at a certain point,
If I display text:
MessageBox(NULL, "text1", "title", MB_OK);
it works OK,
but if I try to display a value,
MessageBox(NULL, SomeIntegerVariable, "title", MB_OK);
I get errors... how do I fix this?
mmscg at 2007-11-9 13:41:04 >

# 30 Re: Mouse Co-ordinates in Static Control
The first is that I can't find a way to clear the "Static" between "paints".
As it is right now, when I drag, I get a mess of lines all over.
I've searched using the keywords GDI and clear, erase etc. but can find anything.
You need to do what Kawasaki056 suggested and look at the SetROP2() functions. For situations like yours, where you're drawing and erasing lines quickly, one common solution when you're ready to draw a new line is to re-draw the old line first, but with an XOR raster operation. The XOR causes the old line to be erased when it's re-drawn. Then it's safe to draw the new line.
Of course, you can simply fill the entire client rectangle with a blank solid rectangle between each draw of a new line. That's very easy and straight-forward, and might be perfect for your simple case where the only graphic being displayed in the entire window is the line. But in more complex situations where there are many items displayed with the line, this technique tends to result in flicker since the entire client area needs to be redrawn.
You should probably start with the second technique first, since raster ops are somewhat complicated.
Also, for debugging, I like to sometimes display a message box at a certain point,
If I display text:
MessageBox(NULL, "text1", "title", MB_OK);
it works OK,
but if I try to display a value,
MessageBox(NULL, SomeIntegerVariable, "title", MB_OK);
I get errors... how do I fix this?
MessageBox requires a LPCTSTR (like a string), so when you give it SomeInteger, it treats the integer as if it were the address of the string (basically, it displays whatever is at the memory location that corresponds to the value of the integer).
You need to format a string with your integer, like
char msg[100];
sprintf( msg, "Integer = %d", SomeInteger ); // the order of args is probably wrong
MessageBox( NULL, msg, "title", MB_OK );
Mike
# 31 Re: Mouse Co-ordinates in Static Control
OK, thanks!
For erasing, how about?
SendMessage(hWnd, WM_ERASEBKGND, NULL, NULL);
Now I tried this and it didn't work, but I thought I might have made a sublte mistake with one of the paramter values.
So before tossing it aside, I thought I would check.
mmscg at 2007-11-9 13:43:07 >

# 32 Re: Mouse Co-ordinates in Static Control
Project is about 95% complete (I used the blank solid rectangle method to erase).
One major flaw (that I wouldn't even know where to begin to start/look to correct it)
is the major flicker (no flicker isn't the right word).
The window is continually updating itself even with no mouse activity
(this explains the hourglass I would always see while working on this),
but if the mouse isn't being moved then I don't understand what event is happening
that is causing my program to draw and re-draw.
Here is my code:
(note I drew the "erasing" rectangle a little inside the main window
so I could see what was happemumg behind it)
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <math.h>
const char g_szMainWindowClassName[] = "myMainWindowClass";
const char g_szStaticWindowClassName[] = "myStaticWindowClass";
// Foward declarations
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK StaticWndProc(HWND, UINT, WPARAM, LPARAM);
void Initialize(HINSTANCE);
void DrawBezier(HWND);
bool IsControlPt(int, int);
int ControlPtNum;
// Global variables
HINSTANCE hInst;
HWND hwnd;
HWND hStatic;
POINT g_pt = {0,0};
int xPt[4], yPt[4];
bool IsDragging;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc; // for Main Window Class
WNDCLASSEX wcex; // for Static Window Class
//HWND hwnd;
MSG Msg;
hInst = hInstance; // Store instance handle in our global variable
// Register the Main Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szMainWindowClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Main Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Register the Static Window Class
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)StaticWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(255, 255, 204));
wcex.lpszMenuName = NULL;
wcex.lpszClassName = g_szStaticWindowClassName;
wcex.hIconSm = NULL;
if(!RegisterClassEx(&wcex))
{
MessageBox(NULL, "Static Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Create the Main Window
hwnd = CreateWindowEx(
NULL,
g_szMainWindowClassName,
"Cubic Bezier",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Main Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Display the Main Window
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Create message loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return static_cast<int>(Msg.wParam);
}
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
HDC hdc2;
switch (message)
{
case WM_CREATE:
hStatic = CreateWindow(g_szStaticWindowClassName, NULL, WS_CHILD|WS_VISIBLE|WS_BORDER,
125, 10, 250, 250, hWnd, NULL, hInst, NULL);
Initialize(hInst);
//DrawBezier(hWnd);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
char MousePos[32];
RECT rRect;
wsprintf(MousePos, "x (%d), " " y (%d)", g_pt.x, g_pt.y);
SetRect(&rRect, 0, 0, 225, 200);
SetBkColor(hdc, 0x003FFF00);
DrawText(hdc, MousePos, -1, &rRect, NULL);
EndPaint(hWnd, &ps);
break;
case WM_LBUTTONDOWN:
//DrawBezier(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
//Initialize(hInst);
DrawBezier(hWnd);
break;
case WM_MOUSEMOVE:
g_pt.x = (LOWORD(lParam));
g_pt.y = (HIWORD(lParam));
InvalidateRect( GetParent( hWnd ), NULL, TRUE );
int myX;
int myY;
myX = (int)g_pt.x;
myY = (int)g_pt.y;
//if ((IsControlPt(g_pt.x, g_pt.y)) & (wParam == MK_LBUTTON))
if ((IsControlPt(myX, myY)) & (wParam == MK_LBUTTON))
{
xPt[ControlPtNum] = myX;//g_pt.x;
yPt[ControlPtNum] = myY;//g_pt.y;
DrawBezier(hWnd);
IsDragging = TRUE;
}
else
{
IsDragging = FALSE;
}
break;
case WM_LBUTTONDOWN:
//
break;
case WM_LBUTTONUP:
DrawBezier(hWnd);
IsDragging = FALSE;
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
void DrawBezier(HWND hWnd)
{
float ax, bx, cx, ay, by, cy, xt, yt, t;
int i;
// Calculate
cx = 3 * (xPt[1] - xPt[0]);
bx = 3 * (xPt[2] - xPt[1]) - cx;
ax = xPt[3] - xPt[0] - cx - bx;
cy = 3 * (yPt[1] - yPt[0]);
by = 3 * (yPt[2] - yPt[1]) - cy;
ay = yPt[3] - yPt[0] - cy - by;
// Clear "PictureBox"
PAINTSTRUCT ps2;
HDC hdc5;
hdc5 = GetDC(hStatic);
HBRUSH LtYelBrush;
LtYelBrush = CreateSolidBrush(RGB(255, 255, 204));
SelectObject(hdc5, LtYelBrush);
Rectangle(hdc5, 5, 5, 243, 243);
DeleteObject(LtYelBrush);
ReleaseDC(hStatic, hdc5);
// Draw control lines
HDC hdc2;
hdc2 = GetDC(hStatic);
HPEN bluePen;
bluePen = CreatePen(PS_SOLID, 1, 0x00FF0000);
SelectObject(hdc2,bluePen);
MoveToEx(hdc2, xPt[0], yPt[0], NULL);
LineTo(hdc2, xPt[1], yPt[1]);
MoveToEx(hdc2, xPt[2], yPt[2], NULL);
LineTo(hdc2, xPt[3], yPt[3]);
DeleteObject(bluePen);
ReleaseDC(hStatic, hdc2);
// Draw Bezier curve
HDC hdc3;
hdc3 = GetDC(hStatic);
for (t = 0; t <= 1; t += 0.001)
{
xt = ax * pow (t, 3) + bx * pow (t, 2) + cx * t + xPt[0];
yt = ay * pow (t, 3) + by * pow (t, 2) + cy * t + yPt[0];
// plot a single point
SetPixel(hdc3, xt, yt, 0x000000FF);
}
ReleaseDC(hStatic, hdc3);
// display co-ordinates
HDC hdc4;
hdc4 = GetDC(hStatic);
HPEN blackPen;
HBRUSH blackBrush;
blackPen = CreatePen(PS_SOLID, 1, RGB(0,0,0));
blackBrush = CreateSolidBrush(RGB(0,0,0));
SelectObject(hdc4, blackPen);
SelectObject(hdc4, blackBrush);
for (i = 0; i <= 3; i ++)
{
Ellipse(hdc4, xPt[i]-1, yPt[i]-1, xPt[i]+3, yPt[i]+3); // draw the circle
char myText[32];
wsprintf(myText, "x (%d), " " y (%d)", xPt[i], yPt[i]);
SetBkMode(hdc4, TRANSPARENT);
SetTextColor(hdc4, RGB(0,0,0));
TextOut(hdc4, xPt[i], yPt[i], myText, 20);
}
DeleteObject(blackBrush);
DeleteObject(blackPen);
ReleaseDC(hStatic, hdc4);
}
void Initialize(HINSTANCE hInst)
{
xPt[0] = 25; xPt[1] = 170; xPt[2] = 110; xPt[3] = 20;
yPt[0] = 60; yPt[1] = 20; yPt[2] = 150; yPt[3] = 210;
}
bool IsControlPt(int X, int Y)
{
int i;
if(IsDragging)
{
return TRUE;
}
for (i = 0; i <= 3; i ++)
{
if ((abs(X - xPt[i])) < 5 & (abs(Y - yPt[i])) < 5)
{
SetCursor(LoadCursor(NULL,IDC_CROSS));
ControlPtNum=i;
return TRUE;
}
else
{
//SetCursor(LoadCursor(NULL,IDC_ARROW));
//return FALSE;
}
}
SetCursor(LoadCursor(NULL,IDC_ARROW)); //Can this go in if/then/else block?
return TRUE; //Ditto
}
edit:
This has to be the worst C++ program ever written :D ; CPU usage is at 100%
I checked the VB version and it runs at 1%
mmscg at 2007-11-9 13:44:08 >

# 33 Re: Mouse Co-ordinates in Static Control
...One major flaw (that I wouldn't even know where to begin to start/look to correct it) is the major flicker (no flicker isn't the right word).
The window is continually updating itself even with no mouse activity (this explains the hourglass I would always see while working on this), but if the mouse isn't being moved then I don't understand what event is happening that is causing my program to draw and re-draw.
...This has to be the worst C++ program ever written :D ; CPU usage is at 100%
I checked the VB version and it runs at 1%
The cause of the continuous udating, and the 100% CPU usage, is the failure to bracket your WM_PAINT code with calls to BeginPaint and EndPaint. These two functions are almost always needed in a WM_PAINT handler. BeginPaint is particularly important because (among other jobs) it tells Windows to validate all regions that were previously invalidated. Remember, Windows sends your app a WM_PAINT message if there invalidated regions in the window, so unless you tell Windows to validate the regions, it will continuously send your app WM_PAINT messages.
The other thing that BeginPaint does is to give you a hDC to draw into. You should use that during your paintings.
You need to do two things. First, add calls to BeginPaint and EndPaint in the WM_PAINT handler for the child. Second, re-write the DrawBezier code so that it uses the hDC that's returned by BeginPaint. Your code should look like this:
LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps; // <<-- note new variable
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
DrawBezier(hdc); // <<-- note that DrawBezier's parameter is now a hDC
EndPaint(hWnd, &ps);
break;
Regards,
Mike
# 34 Re: Mouse Co-ordinates in Static Control
I made the changes you instructed, and everything works great!
CPU usage is a respectable 1% :D
With respect to flicker:
Yellow background: no flicker
Blue control point lines: no flicker
Red Bezier spline: minimal flicker
Black control points and co-ordinate text: pretty bad flicker
Any tricks to get rid of this remaining flicker?
Also I tried to run the code in the this link you provided me:
http://msdn.microsoft.com/library/d...ziermethods.asp
When I went to build, Immediately the Microsoft header filed popped into my Design Window and hundreds of errors refrenced this file.
What could have I possibly done wrong?
mmscg at 2007-11-9 13:46:13 >

# 35 Re: Mouse Co-ordinates in Static Control
I made the changes you instructed, and everything works great!
CPU usage is a respectable 1% :D Good!
With respect to flicker:
Yellow background: no flicker
Blue control point lines: no flicker
Red Bezier spline: minimal flicker
Black control points and co-ordinate text: pretty bad flicker
Any tricks to get rid of this remaining flicker?
Yes, but first you need to improve your existing code, after which you can re-evaluate to see if you still need those "tricks".
First, you seem dead-set determined to draw outside of your WM_PAINT handler. As stated above, you need to do this sometimes, but so far, I can't see the need in your code.
Move all drawing into WM_PAINT. It's already there for the main window, but in the child window you call DrawBezier from within the WM_MOUSEMOVE handler. Don't. Simply invalidate the child's window after you determine the new control points and the IsDragging flag. Essentially, replace your call to DrawBezier with a call to InvalidateRect( hWnd /* i.e., the child's hWnd */ , NULL, TRUE );
If you do this you'll probably eliminate a double-drawing of the bezier, once in WM_MOUSEMOVE and then again in WM_PAINT (although I frankly can't tell if your code will actually draw the bezier twice or just once). Also, since the call to BeginPaint will result automatically in a send of the WM_ERASEBKGND message, you will probably be able to eliminate the code in DrawBezier that draws a blank rectangle. Try it and see.
Next, please make certain that DrawBezier is using only a single hDC, namely, the one obtained from the BeginPaint call, and that it's not GetDC'ing a bunch of hDC's as shown in your last code sample. There's no reason to Get and then Release a bunch of hDC's. A single one is enough, and getting so many is probably slowing your code and needlessly so.
If after all this you still have unacceptable flicker, then post back here with a sample of your latest code and we'll work some more.
As a preview, one common technique for avoiding flicker is a "double-buffering" technique, where you draw your entire window in an off-screen memory device context, and then BitBlt (essentially a specialized and customizable copy) the memory directly to the screen. This avoids the flicker, which is caused by the momentary erasure of the entire window (which exposes the background) followed by the very-quick drawing of your graphics; double-buffering avoids this since the background never gets exposed.
You can search MSDN for CreateCompatibleDC and CreateCompatibleBitmap and BitBlt if you feel a need for a preview, but try the above improvements first; you'll want them anyway.
Also I tried to run the code in the this link you provided me:
http://msdn.microsoft.com/library/d...ziermethods.asp
When I went to build, Immediately the Microsoft header filed popped into my Design Window and hundreds of errors refrenced this file.
What could have I possibly done wrong?
That link was bad (sorry); it mistakenly linked into a section of the GDI+ library that explained the DrawBezier function, rather linking to a section of the GDI library that explained the PolyBezier function (as intended). I've edited the post to fix the link. Your build errors were probably link errors caused by not linking to the GDI+ library, which you don't need if you use the regular GDI PolyBezier function.
Regards,
Mike
# 36 Re: Mouse Co-ordinates in Static Control
Just in case you'd be interested in double buffering.. here is a thread
http://www.dev-archive.com/forum/showthread.php?t=314873
# 37 Re: Mouse Co-ordinates in Static Control
First, you seem dead-set determined to draw outside of your WM_PAINT handler. As stated above, you need to do this sometimes, but so far, I can't see the need in your code.
It must be a VB mindset.
...once in WM_MOUSEMOVE and then again in WM_PAINT (although I frankly can't tell if your code will actually draw the bezier twice or just once).
I saw this too... I thought twice (if for each mouse move message there is a paint message sent).
Because of this I had tried removing the DrawBezier call from the WM_PAINT handler completely (just to see :D )
Basically, when moving the mouse, both apps look identical,
however,
when moving another form overtop the Bezier Window, the app with nothing in the WM_PAINT handler is far superior...
in fact no flicker at all.
Perhaps this will provide some clue as to what message is being (or not being) sent that helps create no flicker.
One problem however, the form passing over it acts as an eraser, and if the app is minimized,
when restored there is no graphics, also at startup... you have to press the mouse to kick-start it.
I am going to make the new changes you suggest, and see what difference they make.
Just so you can see the present state of flicker, I am posting the latest code before these new revisions
(both methods; DrawBezier in and removed from WM_PAINT).
DrawBezier in Static's WM_PAINT
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <math.h>
const char g_szMainWindowClassName[] = "myMainWindowClass";
const char g_szStaticWindowClassName[] = "myStaticWindowClass";
// Foward declarations
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK StaticWndProc(HWND, UINT, WPARAM, LPARAM);
void Initialize(HINSTANCE);
void DrawBezier(HDC);
bool IsControlPt(int, int);
// Global variables
HINSTANCE hInst;
HWND hwnd;
HWND hStatic;
POINT g_pt = {0,0};
HDC hdc;
int xPt[4], yPt[4];
bool IsDragging;
int ControlPtNum;
int LbtnCtr;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc; // for Main Window Class
WNDCLASSEX wcex; // for Static Window Class
MSG Msg;
hInst = hInstance; // Store instance handle in our global variable
// Register the Main Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szMainWindowClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Main Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Register the Static Window Class
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)StaticWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(255, 255, 204));
wcex.lpszMenuName = NULL;
wcex.lpszClassName = g_szStaticWindowClassName;
wcex.hIconSm = NULL;
if(!RegisterClassEx(&wcex))
{
MessageBox(NULL, "Static Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Create the Main Window
hwnd = CreateWindowEx(
NULL,
g_szMainWindowClassName,
"Cubic Bezier",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Main Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Display the Main Window
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Create message loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return static_cast<int>(Msg.wParam);
}
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_CREATE:
hStatic = CreateWindow(g_szStaticWindowClassName, NULL,
WS_CHILD|WS_VISIBLE|WS_BORDER,
125, 10, 250, 250, hWnd, NULL, hInst, NULL);
Initialize(hInst);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
char MousePos[32];
RECT rRect;
wsprintf(MousePos, "x (%d), " " y (%d)", g_pt.x, g_pt.y);
SetRect(&rRect, 0, 0, 225, 200);
SetBkColor(hdc, 0x003FFF00);
DrawText(hdc, MousePos, -1, &rRect, NULL);
EndPaint(hWnd, &ps);
break;
case WM_LBUTTONDOWN:
char msg[100];
LbtnCtr++;
wsprintf( msg, "Left Mouse button pressed %d time(s) in Main Window", LbtnCtr );
MessageBox( NULL, msg, "MessageBox Test", MB_OK );
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
DrawBezier(hdc);
EndPaint(hWnd, &ps);
break;
case WM_MOUSEMOVE:
g_pt.x = (LOWORD(lParam));
g_pt.y = (HIWORD(lParam));
InvalidateRect( GetParent( hWnd ), NULL, TRUE );
if ((IsControlPt(g_pt.x, g_pt.y)) & (wParam == MK_LBUTTON))
{
xPt[ControlPtNum] = g_pt.x;
yPt[ControlPtNum] = g_pt.y;
DrawBezier(hdc);
IsDragging = TRUE;
}
else
{
IsDragging = FALSE;
}
break;
case WM_LBUTTONUP:
DrawBezier(hdc);
IsDragging = FALSE;
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
void DrawBezier(HDC hdc)
{
float ax, bx, cx, ay, by, cy, xt, yt, t;
int i;
// Calculate
cx = 3 * (xPt[1] - xPt[0]);
bx = 3 * (xPt[2] - xPt[1]) - cx;
ax = xPt[3] - xPt[0] - cx - bx;
cy = 3 * (yPt[1] - yPt[0]);
by = 3 * (yPt[2] - yPt[1]) - cy;
ay = yPt[3] - yPt[0] - cy - by;
// Clear "PictureBox"
HDC hdc5;
hdc5 = GetDC(hStatic);
HBRUSH LtYelBrush;
LtYelBrush = CreateSolidBrush(RGB(255, 255, 204));
SelectObject(hdc5, LtYelBrush);
RECT rect = { 0, 0, 248, 248 };
FillRect(hdc5, &rect, LtYelBrush);
DeleteObject(LtYelBrush);
ReleaseDC(hStatic, hdc5);
// Draw control lines
HDC hdc2;
hdc2 = GetDC(hStatic);
HPEN bluePen;
bluePen = CreatePen(PS_SOLID, 1, 0x00FF0000);
SelectObject(hdc2,bluePen);
MoveToEx(hdc2, xPt[0], yPt[0], NULL);
LineTo(hdc2, xPt[1], yPt[1]);
MoveToEx(hdc2, xPt[2], yPt[2], NULL);
LineTo(hdc2, xPt[3], yPt[3]);
DeleteObject(bluePen);
ReleaseDC(hStatic, hdc2);
// Draw Bezier curve
HDC hdc3;
hdc3 = GetDC(hStatic);
for (t = 0; t <= 1; t += 0.001)
{
xt = ax * pow (t, 3) + bx * pow (t, 2) + cx * t + xPt[0];
yt = ay * pow (t, 3) + by * pow (t, 2) + cy * t + yPt[0];
// plot a single point
SetPixel(hdc3, xt, yt, 0x000000FF);
}
ReleaseDC(hStatic, hdc3);
// display co-ordinates
HDC hdc4;
hdc4 = GetDC(hStatic);
HPEN blackPen;
HBRUSH blackBrush;
blackPen = CreatePen(PS_SOLID, 1, RGB(0,0,0));
blackBrush = CreateSolidBrush(RGB(0,0,0));
SelectObject(hdc4, blackPen);
SelectObject(hdc4, blackBrush);
for (i = 0; i <= 3; i ++)
{
Ellipse(hdc4, xPt[i]-1, yPt[i]-1, xPt[i]+3, yPt[i]+3); // draw the control point
char myText[32];
wsprintf(myText, "x (%d), " " y (%d)", xPt[i], yPt[i]);
SetBkMode(hdc4, TRANSPARENT);
SetTextColor(hdc4, RGB(0,0,0));
TextOut(hdc4, xPt[i], yPt[i], myText, strlen(myText)); // write the co-ordinates
}
DeleteObject(blackBrush);
DeleteObject(blackPen);
ReleaseDC(hStatic, hdc4);
}
void Initialize(HINSTANCE hInst)
{
xPt[0] = 25; xPt[1] = 170; xPt[2] = 110; xPt[3] = 20;
yPt[0] = 60; yPt[1] = 20; yPt[2] = 150; yPt[3] = 210;
}
bool IsControlPt(int X, int Y)
{
int i;
if(IsDragging)
{
return TRUE;
}
for (i = 0; i <= 3; i ++)
{
if ( ( abs(X - xPt[i]) < 5 ) && ( abs(Y - yPt[i]) < 5 ) )
{
SetCursor(LoadCursor(NULL,IDC_CROSS));
ControlPtNum=i;
return TRUE;
}
}
SetCursor(LoadCursor(NULL,IDC_ARROW));
return FALSE;
}
kirants:
When all the bugs are ironed out of this, I will look at double-buffering... thanks!
edit
Couldn't post both codes, as they exceed character limit.
mmscg at 2007-11-9 13:49:11 >

# 38 Re: Mouse Co-ordinates in Static Control
MikeAThon:
Made all the changes you suggested... everything works awesome!! :D
There is still flicker of the textual control co-ordinates, but I guess this is just inherent with Windows.
I think I am satisfied this is as good as it can be with conventional methods.
I would however, when I am completely finished with this little app, try and add double-buffering;
both to learn, and to see the difference this technique will make.
Oh... now with these changes made, this method is as good as the "other" method
(DrawBezier removed from WM_PAINT) with regards to another Window being dragged over top this one.
The only way to get my mind to understand DrawBezier only being called once in WM_PAINT,
is to replace the words InvalidateRect with DrawBezier in my mind.
Perhaps there is something else you could say about InvalidateRect that might help me to understand;
if not maybe with repitition... I will try another graphics program after this.
Also, since the call to BeginPaint will result automatically in a send of the WM_ERASEBKGND message, you will probably be able to eliminate the code in DrawBezier that draws a blank rectangle. Try it and see.The "erasing" rectangle is in fact no longer required.
With regards to hDC:
I've used one in the Main Window Proc (released immediately) and then another in the Static Window Proc
and don't release it until WM_DESTROY handler (new handler I added) so it is avaliable for the duration of
the program.
I'm not sure if this is what you meant, so I am posting my code for you to have a look.
A few miscellaneous questions:
On the subject of cleaning things up, I have 2 WNDCLASSEX structures wcxM and
wcxC, is this right or should I only have one?
C++'s AND operator. I require this twice in my program, and when searching
the net I find 2: & and &&. I used one of each hoping one would make the code fail.
The program runs as it should so I don't know which to use.
Or doesn't it matter?
Finally, I have that silly green background color behind my mouse co-ordinate display in the main window.
I tried using COLOR_APPWORKSPACE+1 (as I did in window creation), but that didn't work.
I know I could play with the RGB macro and maybe find the same (or similar) color, but I was wondering if
there was an api or something to get the exact color.
Any other corrections/improvements I should be making?
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <math.h>
const char g_szMainWindowClassName[] = "myMainWindowClass";
const char g_szChildWindowClassName[] = "myChildWindowClass";
// Foward declarations
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM);
void Initialize(HINSTANCE);
void DrawBezier(HDC);
bool IsControlPt(int, int);
// Global variables
HINSTANCE hInst;
HWND hMain;
HWND hChild;
HDC hdcMain;
HDC hdcChild;
POINT g_pt = {0,0};
int xPt[4], yPt[4];
bool IsDragging;
int ControlPtNum;
int LbtnCtr;
PAINTSTRUCT ps;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcxM; // for Main Window Class
WNDCLASSEX wcxC; // for Child Window Class
MSG Msg;
hInst = hInstance; // Store instance handle in our global variable
// Register the Main Window Class
wcxM.cbSize = sizeof(WNDCLASSEX);
wcxM.style = 0;
wcxM.lpfnWndProc = MainWndProc;
wcxM.cbClsExtra = 0;
wcxM.cbWndExtra = 0;
wcxM.hInstance = hInstance;
wcxM.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcxM.hCursor = LoadCursor(NULL, IDC_ARROW);
wcxM.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;
wcxM.lpszMenuName = NULL;
wcxM.lpszClassName = g_szMainWindowClassName;
wcxM.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wcxM))
{
MessageBox(NULL, "Main Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Register the Child Window Class
wcxC.cbSize = sizeof(WNDCLASSEX);
wcxC.style = CS_HREDRAW | CS_VREDRAW;
wcxC.lpfnWndProc = (WNDPROC)ChildWndProc;
wcxC.cbClsExtra = 0;
wcxC.cbWndExtra = 0;
wcxC.hInstance = hInstance;
wcxC.hIcon = NULL;
wcxC.hCursor = LoadCursor(NULL, IDC_ARROW);
wcxC.hbrBackground = CreateSolidBrush(RGB(255, 255, 204));
wcxC.lpszMenuName = NULL;
wcxC.lpszClassName = g_szChildWindowClassName;
wcxC.hIconSm = NULL;
if(!RegisterClassEx(&wcxC))
{
MessageBox(NULL, "Child Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Create the Main Window
hMain = CreateWindowEx(
NULL,
g_szMainWindowClassName,
"Cubic Bezier",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
if(hMain == NULL)
{
MessageBox(NULL, "Main Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Display the Main Window
ShowWindow(hMain, nCmdShow);
UpdateWindow(hMain);
// Create message loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return static_cast<int>(Msg.wParam);
}
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
hChild = CreateWindow(g_szChildWindowClassName, NULL,
WS_CHILD|WS_VISIBLE|WS_BORDER,
125, 10, 250, 250, hWnd, NULL, hInst, NULL);
Initialize(hInst);
break;
case WM_PAINT:
hdcMain = BeginPaint(hWnd, &ps);
char MousePos[32];
RECT rRect;
wsprintf(MousePos, "x (%d), " " y (%d)", g_pt.x, g_pt.y);
SetRect(&rRect, 0, 0, 225, 200);
//SetBkColor(hdcMain, COLOR_APPWORKSPACE+1);
SetBkColor(hdcMain, 0x003FFF00); //match main window background
DrawText(hdcMain, MousePos, -1, &rRect, NULL);
EndPaint(hWnd, &ps);
ReleaseDC(hWnd, hdcMain);
break;
case WM_LBUTTONDOWN:
char msg[100];
LbtnCtr++;
wsprintf( msg, "Left Mouse button pressed %d time(s) in Main Window", LbtnCtr );
MessageBox( NULL, msg, "MessageBox Test", MB_OK );
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
hdcChild = BeginPaint(hWnd, &ps);
DrawBezier(hdcChild);
EndPaint(hWnd, &ps);
//ReleaseDC(hWnd, hdcChild); // Release the DC where?
break;
case WM_MOUSEMOVE:
g_pt.x = (LOWORD(lParam));
g_pt.y = (HIWORD(lParam));
InvalidateRect( GetParent( hWnd ), NULL, TRUE );
if ((IsControlPt(g_pt.x, g_pt.y)) & (wParam == MK_LBUTTON))
{
xPt[ControlPtNum] = g_pt.x;
yPt[ControlPtNum] = g_pt.y;
//DrawBezier(hdcChild);
IsDragging = TRUE;
InvalidateRect( hChild , NULL, TRUE ); // same as call to "DrawBezier"?
}
else
{
IsDragging = FALSE;
}
break;
case WM_LBUTTONUP:
//DrawBezier(hdcChild);
IsDragging = FALSE;
InvalidateRect( hChild , NULL, TRUE ); // same as call to "DrawBezier"?
break;
case WM_DESTROY:
ReleaseDC(hWnd, hdcChild);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
return 0;
}
void DrawBezier(HDC hdc)
{
float ax, bx, cx, ay, by, cy, xt, yt, t;
int i;
HBRUSH LtYelBrush = CreateSolidBrush(RGB(255, 255, 204));
HPEN blackPen = CreatePen(PS_SOLID, 1, RGB(0,0,0));
HBRUSH blackBrush = CreateSolidBrush(RGB(0,0,0));
HPEN bluePen = CreatePen(PS_SOLID, 1, 0x00FF0000);
// Calculate
cx = 3 * (xPt[1] - xPt[0]);
bx = 3 * (xPt[2] - xPt[1]) - cx;
ax = xPt[3] - xPt[0] - cx - bx;
cy = 3 * (yPt[1] - yPt[0]);
by = 3 * (yPt[2] - yPt[1]) - cy;
ay = yPt[3] - yPt[0] - cy - by;
//// Clear "PictureBox"
//SelectObject(hdc, LtYelBrush);
//RECT rect = { 0, 0, 248, 248 };
//FillRect(hdc, &rect, LtYelBrush);
// Draw control lines
SelectObject(hdc,bluePen);
MoveToEx(hdc, xPt[0], yPt[0], NULL);
LineTo(hdc, xPt[1], yPt[1]);
MoveToEx(hdc, xPt[2], yPt[2], NULL);
LineTo(hdc, xPt[3], yPt[3]);
// Draw Bezier curve
for (t = 0; t <= 1; t += 0.001)
{
xt = ax * pow (t, 3) + bx * pow (t, 2) + cx * t + xPt[0];
yt = ay * pow (t, 3) + by * pow (t, 2) + cy * t + yPt[0];
// plot a single point
SetPixel(hdc, xt, yt, 0x000000FF);
}
// display co-ordinates
SelectObject(hdc, blackPen);
SelectObject(hdc, blackBrush);
for (i = 0; i <= 3; i ++)
{
Ellipse(hdc, xPt[i]-1, yPt[i]-1, xPt[i]+3, yPt[i]+3); // draw the control point
char myText[32];
wsprintf(myText, "x (%d), " " y (%d)", xPt[i], yPt[i]);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(0,0,0));
TextOut(hdc, xPt[i], yPt[i], myText, strlen(myText)); // write the co-ordinates
}
DeleteObject(blackPen);
DeleteObject(bluePen);
DeleteObject(LtYelBrush);
DeleteObject(blackBrush);
}
void Initialize(HINSTANCE hInst)
{
xPt[0] = 25; xPt[1] = 170; xPt[2] = 110; xPt[3] = 20;
yPt[0] = 60; yPt[1] = 20; yPt[2] = 150; yPt[3] = 210;
}
bool IsControlPt(int X, int Y)
{
int i;
if(IsDragging)
{
return TRUE;
}
for (i = 0; i <= 3; i ++)
{
if ( ( abs(X - xPt[i]) < 5 ) && ( abs(Y - yPt[i]) < 5 ) )
{
SetCursor(LoadCursor(NULL,IDC_CROSS));
ControlPtNum=i;
return TRUE;
}
}
SetCursor(LoadCursor(NULL,IDC_ARROW));
return FALSE;
}
mmscg at 2007-11-9 13:50:12 >

# 39 Re: Mouse Co-ordinates in Static Control
Your code looks good!! Nice work.
The difference between & and && is that the former is a bit-wise and operator whereas the latter is a logical and operator. Your use of && is correct in the code as shown. You might use the bit-wise & operator on bit-fields where you are trying to test or set a single bit. For example, if a variable named dwStyle contained the current style bits for a window, then
dwStyle &= ~WS_VISIBLE
would trun off the WS_VISIBLE style.
The only way to get my mind to understand DrawBezier only being called once in WM_PAINT, is to replace the words InvalidateRect with DrawBezier in my mind.
That's not correct. InvalidateRect tells Windows that the client area is no longer valid and needs to be re-painted. That's all. It's not as if Windows will then call your WM_PAINT function. Instead, Windows just continues to do whatever it's doing, posting messages (such as WM_MOUSEMOVE) to your app etc. Then, when Windows determines that there are no further messages to post to your app, it checks to see if there are any invalid areas. If there are (and of course there are because of the InvalidateRect call), then Windows posts a WM_PAINT message to you app, which responds by executing the WM_PAINT handler and re-paints your window.
InvalidateRect is only one of many different ways that will result in invalidated areas in your window. Another way is if your app is minimized and then maximized. Or if another window covers yours and then is removed. All of these willl result in invalidated areas in your windw, and all of them will (eventually) result in Windows posting a WM_PAINT message to your app. That's why you should put all your drawing code in the WM_PAINT handler, since that will be the code that's executed whenever your app needs to be re-painted.
Mike
# 40 Re: Mouse Co-ordinates in Static Control
I kinda liked your "silly green background". But if you want to get rid of it, you need to do two things.
First, you need to fix the definition of the background brush in your WNDCLASS. Change
wcxM.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;
to this
wcxM.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE+1); // note the parenthesis
This will set the backgrond brush properly to COLOR_APPWORKSPACE. Then, to match that in your text background color, change
SetBkColor(hdcMain, 0x003FFF00);
to this
SetBkColor(hdcMain, ::GetSysColor( COLOR_APPWORKSPACE ) );
You probably will not like the new, darker color of the background. Go look at the colors defined in the documentation for GetSysColor, and choose the one you like.
Alternatively, instead of setting any background color for text, you can set the background drawing mode to transparent, so that text will draw itself with no background color. Replace
SetBkColor(hdcMain, 0x003FFF00);
with
SetBkMode( hdcMain, TRANSPARENT);
Regards,
Mike
# 41 Re: Mouse Co-ordinates in Static Control
Your code looks good!! Nice work.Thank you, but I wouldn't have been able to do it if you hadn't the patience to see me through it.
That's not correct. InvalidateRect tells Windows that the client area is no longer valid and needs to be re-painted.
...
...put all your drawing code in the WM_PAINT handler, since that will be the code that's executed whenever your app needs to be re-painted.But since the call to DrawBezier is in the WM_PAINT handler, and InvalidateRect causes the the code inside the WM_PAINT handler to be executed, everytime I call InvalidateRect my DrawBezier will be executed so I considered it be be essentially the same.
Maybe I am still misunderstanding?
My VB code had 2 calls to DrawBezier, and I have replaced these with two InvalidateRects.
I suppose if I do a few more of these graphics programs it might make sense to me.
I wanted to add a bit more to this app; see if I could link the GDI and GDI+ Beziers you pointed out to me to the movement of my Beziers.
Perhaps, this additional graphic programming will help make things more clear to me.
A day or two to add this and then double-buffering...
mmscg at 2007-11-9 13:53:20 >

# 42 Re: Mouse Co-ordinates in Static Control
To reduce flicker even further, try moving all your text-drawing code to the front of DrawBezier, like this:
void DrawBezier(HDC hdc)
{
float ax, bx, cx, ay, by, cy, xt, yt, t;
int i;
HBRUSH LtYelBrush = CreateSolidBrush(RGB(255, 255, 204));
HPEN blackPen = CreatePen(PS_SOLID, 1, RGB(0,0,0));
HBRUSH blackBrush = CreateSolidBrush(RGB(0,0,0));
HPEN bluePen = CreatePen(PS_SOLID, 1, 0x00FF0000);
// display co-ordinates
SelectObject(hdc, blackPen);
SelectObject(hdc, blackBrush);
for (i = 0; i <= 3; i ++)
{
Ellipse(hdc, xPt[i]-1, yPt[i]-1, xPt[i]+3, yPt[i]+3); // draw the control point
char myText[32];
wsprintf(myText, "x (%d), " " y (%d)", xPt[i], yPt[i]);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(0,0,0));
TextOut(hdc, xPt[i], yPt[i], myText, strlen(myText)); // write the co-ordinates
}
// Calculate
cx = 3 * (xPt[1] - xPt[0]);
bx = 3 * (xPt[2] - xPt[1]) - cx;
ax = xPt[3] - xPt[0] - cx - bx;
cy = 3 * (yPt[1] - yPt[0]);
by = 3 * (yPt[2] - yPt[1]) - cy;
ay = yPt[3] - yPt[0] - cy - by;
//// Clear "PictureBox"
//SelectObject(hdc, LtYelBrush);
//RECT rect = { 0, 0, 248, 248 };
//FillRect(hdc, &rect, LtYelBrush);
// Draw control lines
SelectObject(hdc,bluePen);
MoveToEx(hdc, xPt[0], yPt[0], NULL);
LineTo(hdc, xPt[1], yPt[1]);
MoveToEx(hdc, xPt[2], yPt[2], NULL);
LineTo(hdc, xPt[3], yPt[3]);
// Draw Bezier curve
for (t = 0; t <= 1; t += 0.001)
{
xt = ax * pow (t, 3) + bx * pow (t, 2) + cx * t + xPt[0];
yt = ay * pow (t, 3) + by * pow (t, 2) + cy * t + yPt[0];
// plot a single point
SetPixel(hdc, xt, yt, 0x000000FF);
}
DeleteObject(blackPen);
DeleteObject(bluePen);
DeleteObject(LtYelBrush);
DeleteObject(blackBrush);
}
Better?? Do you know why it's better?
Mike
PS: It's better because the background (remember, it's the very-quick exposure of the background that causes flicker) is exposed for a shorter period of time. Of course, it's now exposed behind the other graphics for a longer time, which should increase the flicker of the other graphics, but in fact, because those graphics are thinner and a lesser color difference against the background, it's less noticeable.
# 43 Re: Mouse Co-ordinates in Static Control
But since the call to DrawBezier is in the WM_PAINT handler, and InvalidateRect causes the the code inside the WM_PAINT handler to be executed, everytime I call InvalidateRect my DrawBezier will be executed so I considered it be be essentially the same.
InvalidatRect doesn't cause the WM_PAINT handler to be executed. There's an indirect causation. InvalidateRect invalidates the window's area. Then, at some i