Why is my child window unresponsive to mouse events? - c++

I have created a customized static window which displays a bitmap image, this window is the child window of some other window. Now I want to capture mouse events for this window, so that I can provide functionality to crop the image.
But the problem is Mouse events are not passed to this child window....
following is a code snippet of the WndProc of child window ..
WNDPROC origStatProc;
// Variable which stores the handle of BITMAP image
HBITMAP hBitmap=NULL;
LRESULT CALLBACK dispWndProc(HWND hwnd,UINT msg, WPARAM wParam, LPARAM lParam)
{
static HDC hdc;
static PAINTSTRUCT paintSt;
static RECT aRect;
switch(msg)
{
case WM_PAINT:
{
hdc = BeginPaint(hwnd,&paintSt);
GetClientRect(hwnd,&aRect);
if(hBitmap!=NULL)
{
HDC memDC = CreateCompatibleDC(hdc);
if(memDC!=NULL)
{
BITMAP bmp;
GetObject(hBitmap,sizeof(bmp),&bmp);
SelectObject(memDC,hBitmap);
SetStretchBltMode(hdc,HALFTONE);
StretchBlt(hdc,0,0,aRect.right,aRect.bottom,
memDC,0,0,bmp.bmWidth,bmp.bmHeight,
SRCCOPY);
DeleteObject(&bmp);
ReleaseDC(hwnd,memDC);
}
}
// the code for painting
EndPaint(hwnd,&paintSt);
}
break;
case STM_SETIMAGE:
{
InvalidateRect(hwnd,&aRect,true);
}
break;
case WM_LBUTTONDOWN:
{
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
char xstr[10];
_itoa(xPos,xstr,10);
MessageBox(NULL,xstr,"X Value ",MB_OK);
}
break;
default:
return origStatProc(hwnd,msg,wParam,lParam);
}
return 0;
}
Can anyone tell me what else I need to capture mouse events inside this Child window...

The window class that you use for the window will determine certain default behaviors for the window. The Static window class is particularly difficult to work with, because Windows makes assumptions that the window will never change its contents, and won't interact with the user in any way. You'll probably find that the WM_LBUTTONDOWN is being passed to the parent window.

If I remember correctly: static windows declare themselves to be 'invisible' to mouse clicks by returning HTTRANSPARENT in response to WM_NCHITTEST. Because of this, windows passes the mouse click on to the parent. If you want to process the mouse clicks in the statics, you'll need to also override this behavior to return HTCLIENT instead.

I have called DefWndProc() instead of origStatProc(hwnd,msg,wParam,lParam) and the problem is solved....
anyWays thanks to everyone....

Related

How to draw on standart button WinApi

I want to draw a simple square on a button.
I created a regular window and a regular button in it. Now, in the window procedure of my window, in the WM_PAINT message, I get the HDC of my button and draw a square:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_PAINT)
{
PAINTSTRUCT ps;
HDC my_hdc = BeginPaint(hWnd, &ps);
//---------------------------------------------
HDC my_button_HDC = GetDC(GetDlgItem(hWnd, 11)); //Get HDC my button
Rectangle(my_button_HDC, 5, 5, 30, 30);
//---------------------------------------------
EndPaint(hWnd, &ps);
}
WinMain()
{
//standart code create window and button...
}
When creating a window, a square does not appear on the button. It appears ONLY when I move my window down outside of the screen and lift it up.
But, as soon as I resize the window or click on the button, the square disappears again.
I don't understand why this is happening.
You are drawing on the button only when its parent window is being painted (also, you are leaking the button's HDC). Resizing the window does not always trigger a repaint.
But even when it does, when the button itself paints itself, it will draw over anything you have already drawn.
The correct way to draw on a standard Win32 button is to either:
give the button the BS_OWNERDRAW style, and then have its parent window handle the WM_DRAWITEM message.
if ComCtl32.dll v6 is being used (see Enabling Visual Styles), the parent window can instead handle the NM_CUSTOMDRAW message, BS_OWNERDRAW is not needed. See Custom Draw for more details.

How to repaint a child window only once every frame using WinAPI?

I'm using Direct3D and WinAPI to create windows and render 3D objects in the client area. I use the standard Windows message loop to invalidate the rectangle of the render window, and in the message handler of the render window, I perform a render call in Direct3D when processing the WM_PAINT message:
BOOL bRet;
HWND mainWnd; // Main window
HWND renderWnd; // Child of mainWnd, takes up a portion of the client area
// to be used as a Direct3D render target
MSG msg = {};
while ((bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if(bRet != -1)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
InvalidateRect(renderWnd, nullptr, false);
}
}
// ...
// Window procedure used by renderWnd
LRESULT renderWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
// ...
case WM_PAINT:
{
// Perform Direct3D rendering
// ...
}
break;
// ...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
This setup seems to work correctly as long as my app only has one window, as InvalidateRect is called each frame and ensures that the client area needs to be redrawn, which in turn will result in a Direct3D draw call. It doesn't feel very elegant though, especially once I try to create multiple windows, as I'd have to invalidate their rectangles in that same piece of code, even if the features that the windows serve otherwise have nothing to do with each other (some of the windows might not even exist at any one time).
That said, my question is this: is it possible to have a window invalidate part of its client area exactly once each frame (assuming it's currently not minimized, etc.)? Maybe through the use of the message queue? Referring back to the above code segment: I'd want some way for mainWnd (perhaps in its own window procedure) to call InvalidateRect on renderWnd exactly once each frame.
Thanks in advance!
EDIT: small error in the code sample
Note that the 'standard' way to handle this is something more like this:
// Main message loop
MSG msg = {};
while (WM_QUIT != msg.message)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Update your simulation/animation/etc. based on elapsed time
// -or-
// multiple fixed time-steps.
//
// Then render one frame.
}
}
…
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
This model is a 'flat-out' rendering model where you will render as fast as the system can do it, limited by Present details like refresh rate and automatic throttling if you have 3 or more frames ready.
You should take a look at GitHub for some more details like implementing WM_ENTERSIZEMOVE / WM_EXITSIZEMOVE.
The only time you'd implement WM_PAINT / InvalidateRect to render is if you are doing an editor or some 'mixed' UI and Direct3D application.
So with the help of the comments on the original post, I figured out that the best option was to use the Timers provided by WinAPI to schedule a repaint of the render window at the required framerate. Going to mark this as solved.

Win32 C++ Custom painting subclassed radiobutton initial background issue

I am trying to draw a radiobutton with a transparent background using a subclassed radiobutton & WM_PAINT. I know how to do this using the TransparentBlt function & an off-screen (back)buffer.
My problem is that intially the radiobutton by default draws some text & circle (see image 1). My WM_PAINT message only consists of BeginPaint() and EndPaint(). When i minimize the window and afterwards activate the window again, the default text & circle get replaced by a black square like you would expect (see image 2).
The yellow window is also painted in the same manner using WM_PAINT.
Both procedures return 1 for WM_ERASEBKGND and return 0 for WM_PAINT as required for custom painting & double buffering.
Is this normal behaviour? I found a "fix" by using WS_EX_TRANSPARENT, but I would like to understand first why the radiobutton initially gets painted like that to determine if this is the right fix for me.
Thanks in advance.
Radiobutton creation:
MControlRect rect(0, 0, 100, 20);
unsigned long style = WS_CHILD | BS_AUTORADIOBUTTON;
if (isGroupStarter) {
style += WS_GROUP;
}
::HWND hWnd = _create(pControlParent, WC_BUTTON, style, rect);
::WNDPROC systemProc = (::WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);
::SetWindowLong(hWnd, GWL_WNDPROC, (long)customRadiobuttonProcedure);
::SetWindowLong(hWnd, GWL_USERDATA, (long)systemProc);
::UpdateWindow(hWnd);
::ShowWindow(hWnd, SW_SHOW);
Radiobutton procedure:
switch (msg) {
case WM_ERASEBKGND:
{
return 1;
break;
}
case WM_NCPAINT:
{
return 0;
break;
}
case WM_PAINT:
{
::PAINTSTRUCT ps;
::HDC hdc = ::BeginPaint(hWnd, &ps);
::EndPaint(hWnd, &ps);
return 0;
break;
}
}
::WNDPROC defaultWindowProc = (::WNDPROC)::GetWindowLong(hWnd, GWL_USERDATA);
return ::CallWindowProc(defaultWindowProc, hWnd, msg, wParam, lParam);
Image 1: initial radiobutton painted
Image 2: radiobutton after minimize > show again

C++ WinApi Fillrect() crashes (multiple rects)

i am trying to get used to WinApi and decided to make a GUI for a sudoku-generator i programmed.
It should adjust dynamicly to the windowsize the user chooses.
So far everything works as inteded, but if the WM_PAINT-msg is sent too often in a short window of time (eg changing the size of the window) the program crashes.
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
stringstream ss; //not used
RECT rect;
int w;
int h;
HBRUSH coluns=CreateSolidBrush(RGB(50,120,180));
HBRUSH colsel=CreateSolidBrush(RGB(80,150,220));
HBRUSH colmso=CreateSolidBrush(RGB(50,70,190));
switch (message)
{
case WM_SIZE: //
{
GetWindowRect(hwnd,&rect);
menu.wndw=rect.right-rect.left; //menu is a class to store important information
menu.wndh=rect.bottom-rect.top;
h=menu.wndh;
w=menu.wndw;
for(int i=1;i<10;i++)
{
for(int j=1;j<10;j++)
{
menu.feld[i][j].SetSpace((w/4)+((i-1)*(w/20))+i+(2*((i-1)/3)),(h/4)+((j-1)*(h/20))+j+(2*((j-1)/3)),(w/4)+((i)*(w/20))+i+(2*((i-1)/3)),(h/4)+((j)*(h/20))+j+(2*((j-1)/3)));
}
} //feld is a class wich exists in a 10x10 array with the 0s not being used
InvalidateRect(hwnd,NULL, TRUE);
}
break;
case WM_PAINT:
{
RECT re;
w=menu.wndw;
h=menu.wndh;
hdc = BeginPaint(hwnd,&ps);
re.left=(w/4)-4;
re.top=(h/4)-4;
re.right=(w/4)+9*(w/20)+18;
re.bottom=(h/4)+9*(h/20)+18;
FillRect(hdc,&re,CreateSolidBrush(RGB(0,0,0)));
for(int i=1;i<10;i++)
{
for(int j=1;j<10;j++)
{
re=menu.feld[i][j].GetSpace();
if(menu.feld[i][j].GetSelect()==uns)
if(FillRect(hdc,&re,coluns)==0)
MessageBox(hwnd, "fail","fail",0);
if(menu.feld[i][j].GetSelect()==mso)
if(FillRect(hdc,&re,colmso)==0)
MessageBox(hwnd, "fail","fail",0);
if(menu.feld[i][j].GetSelect()==sel)
if(FillRect(hdc,&re,colsel)==0)
MessageBox(hwnd, "fail","fail",0);
}
}
EndPaint(hwnd, &ps);
}
break;
http://www.pic-upload.de/view-22113118/Unbenannt.png.html
here is a picture of what the executed program looks like.
Now as described earlier the program will crash if u change the windowsize in a lot of small steps. After calling the MW_PAINT msg for ~10 times the window will just freeze with 1 of the rects being white instead of the desired color (random one, different every time).
my assumption is that i need to release some kind of resources because mby a stack will overflow or smth, but i have really no idea where i could have a leak in my program.
i would be very grateful if anyone could help me.
You create three brush handles every single time your window procedure executes. These handles are never tidied up. And then inside the WM_PAINT handler, you create a brush which you pass to FillRect and so can never destroy it.
So you leak three handles every time the window procedure executes (which happens a lot), and one more every time it handles WM_PAINT. Simply put, your program leaks like a sieve!
You should consider creating these brushes when the window is created, and destroying them when the window is destroyed. Or perhaps creating them inside the WM_PAINT handler, and destroying them as soon as you have finished using them. But since they have constant colors it is probably best to create 4 brushes up front, once and for all.
You are leaking GDI resources as member David Heffernan said.
Here is the example of how to properly use brushes in your application-pay attention to WM_COMMAND handler in that example.
If you do not use stock GDI objects you must delete them after you are done working with them.
Here is the simple example that fills window with red brush in WM_PAINT handler:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPain( hdc, &ps );
HBRUSH hbrRedBrush = CreateSolidBrush( RGB( 255, 0, 0 ) );
RECT r;
GetClientRect( hWnd, &r );
FillRect( hdc, &r, hbrRedBrush );
DeleteObject( hbrRedBrush ); //you must delete GDI object!
EndPaint( hWnd, &ps );
}
return 0L;
In your case, I would make 4 static brushes and rework my code a little, adding the proper cleanup in WM_CLOSE handler. Below are the suggested changes:
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
// add static before HBRUSH
static HBRUSH coluns=CreateSolidBrush(RGB(50,120,180));
static HBRUSH colsel=CreateSolidBrush(RGB(80,150,220));
static HBRUSH colmso=CreateSolidBrush(RGB(50,70,190));
static HBRUSH BlackBrush = CreateSolidBrush(RGB(0,0,0));
switch (message)
{
// this is the problematic handler
case WM_PAINT:
{
//the changed part
FillRect( hdc, &re, BlackBrush );
}
break;
case WM_CLOSE:
{
DeleteObject( BlackBrush );
DeleteObject( coluns );
DeleteObject( colsel );
DeleteObject( colmso );
// other clean up code
}
break;
IMPORTANT NOTE:
This time you used FillRect API, but next time you might load bitmaps and other stuff that require from you to restore HDC into original state after you are done with drawing.
You do that like this:
HBITMAP bmpOld = (HBITMAP)SelectObject( hdc, myBitmap );
// bmpOld stores the original state of the device context
// you do something with myBitmap
// then you return device context into original state
// by selecting the original value, bmpOld, back into device context
SelectObject( hdc, oldBmp );
DeleteObject( myBitmap );
Again, pay attention to WM_COMMAND handler in the above MSDN example to see how they did it.
Here is the link to a great Win32 API tutorial for beginners-give it a go.
In the end I recommend you this tool for detecting GDI leaks.
If you have further questions leave a comment and I will reply as soon as possible.
Best regards and good luck!

How do I properly move a window with a region?

I've just started looking into window regions, and I'm trying to create an elliptical window that I can move by dragging the client area. Unfortunately, when I drag the window, the window flashes back and forth from the ellipse to the normal window (as if I never called SetWindowRgn), and back, repeatedly and rapidly.
I read on MSDN that I have to call SetWindowRgn(nullptr);, then move the window, then reset the region, which I have already done in my code. I move the window by calling SetWindowPos with SWP_NOZORDER, SWP_NOSIZE, and SWP_NOREDRAW, and I've tried adding on all of SWP_NOSENDCHANGING, SWP_DEFERERASE, and SWP_NOCOPYBITS as well, to no avail.
Here's my window procedure, with emphasis on WM_MOUSEMOVE. I know it won't work if I release the button outside of the window; I was planning to deal with that after this worked. I also left out error checking. It's pretty obvious that the calls do work, though, because the window does move as I drag it.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static bool moving{};
switch (msg) {
case WM_DESTROY: {
PostQuitMessage(0);
return 0;
}
case WM_LBUTTONDOWN: {
moving = true;
return 0;
}
case WM_LBUTTONUP: {
moving = false;
return 0;
}
case WM_MOUSEMOVE: {
static POINT old{0, 0};
if (moving) {
RECT r;
GetWindowRect(hwnd, &r);
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
RECT newPos;
newPos.left = r.left + x - old.x;
newPos.top = r.top + y - old.y;
SetWindowRgn(hwnd, nullptr, FALSE);
SetWindowPos(hwnd, nullptr, newPos.left, newPos.top, 0, 0,
SWP_NOZORDER | SWP_NOSIZE | SWP_NOREDRAW
);
SetWindowRgn(hwnd, CreateEllipticRgn(200, 200, 600, 400), FALSE);
}
old.x = GET_X_LPARAM(lParam);
old.y = GET_Y_LPARAM(lParam);
return 0;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
I've also tried calling ValidateRgn(hwnd, nullptr); at the end of WM_MOUSEMOVE, which doesn't change anything. As well, I've tried wrapping the DefWindowProc call in a condition that just returns 0 if moving is set in order to see whether it was just some other message being sent that was messing with it, but that resulted in the window doing nothing when dragged. I then applied that condition to WM_PAINT and WM_ERASEBKGND handlers, but that resulted in the same flickering problem when dragged.
In order to test it more easily, here's full code (just rudimentary window creation etc). Am I going about moving the window the right way? Is there some message or something that I should handle, but don't? This is happening on Window 7 Ultimate N.
A much easier way to handle the move logic is to handle the WM_NCHITTEST message, returning HTCAPTION. This makes Windows handle all the move logic for you.