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.
Related
Upon color change, i listen to WM_CTLCOLORSTATIC and act accordingly:
LRESULT ProcessWindowMessage(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
switch (uMsg)
{
case WM_CTLCOLORSTATIC:
LRESULT lBrush = ::DefWindowProc(hWnd, uMsg, wParam, lParam); // get default brush used so far
::SetBkMode((HDC)wParam, TRANSPARENT);
::SetTextColor((HDC)wParam, RGB(m_color.red, m_color.green, m_color.blue));
return lBrush;
}
}
This works well with regular static-texts: labels and so on, but has no effect on regular radio buttons.
During my debugging attempts, I've tried listening to:
WM_DRAWITEM - doesn't receive any events
WM_CTLCOLORBTN - receive events only for regular push buttons( OK / CANCEL)
WM_CTLCOLOREDIT - doesn't receive any events.
I'm subclassing to another window not generated / created by me, but constructed by my process.
#igal k said SetWindowTheme does not work. Since the comment is not enough for the sample code. I post it as an answer.
First the result.
Code:
OnInitDialog:
::SetWindowTheme(GetDlgItem(IDC_RADIO1)->GetSafeHwnd(), L"wstr", L"wstr");
HBRUSH CMFCApplication1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
// Call the base class implementation first! Otherwise, it may
// undo what we're trying to accomplish here.
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
// Are we painting the IDC_MYSTATIC control? We can use
// CWnd::GetDlgCtrlID() to perform the most efficient test.
if (pWnd->GetDlgCtrlID() == IDC_RADIO1)
{
// Set the text color to red
pDC->SetTextColor(RGB(255, 0, 0));
// Set the background mode for text to transparent
// so background will show thru.
pDC->SetBkMode(TRANSPARENT);
// Return handle to our CBrush object
hbr = m_brush;
}
return hbr;
}
If you are interested to draw the full radio button, what you have to do is set a custom window proc for the radio button.In that proc you get the WM_PAINT and WM_ERASEBKGND messages.
In the erase background you can just fill the background color for the control.
In WM_PAINT you do the drawing of the control , get the window text of the control and do a DrawText with color set to whatever you need using SetTextColor
Draw an image of radio (circle and a dot inside).You can get all the state variables of the control and draw according to it, like whether its selected it or not. Whenever you need a repaint, ie new text is set, just call invalidateRect to force a repaint..
I am writing a game in openGL, in C++ at windows.
I need to add some menus and sub-menus at the top left corner of the window.
I've seen the popup menus on right click but I want the menus to be as any program like firefox, etc.
How can I achieve this?
Edit #1:
I've added a popup menu when a RMB is pressed with these:
glutCreateMenu(MenuSelect);
glutAddMenuEntry("Option1Name", option1);
glutAddMenuEntry("Option2Name", option2);
glutAttachMenu(GLUT_RIGHT_BUTTON);
and into the MenuSelect function I am printing some text.
You have at least two choices:
use some Windows API (or MFC), or QT, this will draw menus on top of OpenGL (using GDI, or other technology)
use some third party library that will draw GUI in OpenGL (using its rendering commands)
For the latter option and for simple GUI I suggest http://www.antisphere.com/Wiki/tools:anttweakbar
I had the same problem and I solved it using Win32API.
Use the CreateMenu function to create the menu, then use the SetMenu function to set the menu to the window.
Because when menu is set, the drawing area of the program will be reduced, causing the coordinates to be offset when the mouse is clicked. We need to resize the window using the AdjustWindowRect and MoveWindow functions.
Because opengl will take over the callback function of the window, the menu command we set cannot be accepted by the program, we need to use the SetWindowLongA function to set the callback function of the window to the function we defined, and use the CallWindowProc function in the last line of the function we defined to call the opengl callback function.
Here is a example code:
#define IDM_MENU_1 (1) // menu id
static WNDPROC gl_wnd_proc; // legacy opengl window proc function
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDM_MENU_1: // Do menu command here...
}
}
}
// Here we have to use `CallWindowProc` function
return CallWindowProc(gl_wnd_proc,hWnd, msg, wParam, lParam);
}
int main()
{
// do `glutInit` first ...
// 1. Create and set menu
HMENU hMenu = CreateMenu();
AppendMenuA(hMenu, MF_STRING, IDM_MENU_1, "Menu 1");
HWND hWnd = FindWindowA("GLUT", NULL); // "GLUT" is opengl window class name
SetMenu(hWnd, hMenu);
// 2. Adjust window size
RECT rt = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
AdjustWindowRect(&rt, GetWindowLongA(hWnd, GWL_STYLE), TRUE);
MoveWindow(hWnd, 0, 0, rt.right - rt.left, rt.bottom - rt.top, TRUE);
// 3. Set window callback proc function
gl_wnd_proc = (WNDPROC)SetWindowLongA(hWnd, GWLP_WNDPROC, (LONG)MyWndProc);
// do `glutMainLoop`
}
As for sub-menus, its creation method also continues to use CreateMenu and AppendMenu, you can refer to Win32 API C++ Menu bar
i would like to copy entire client window size of some window to my HDC, but when the window is minimized the source of all color bits is always empty RGB(0,0,0);
How to do this without activating the window?
You can use this function:
BOOL PrintWindow(
HWND hwnd,
HDC hdcBlt,
UINT nFlags);
http://msdn.microsoft.com/en-us/library/dd162869%28v=vs.85%29.aspx
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....
im trying to create ball animation using gdi but i can't get it working.
i created a ball using this
Graphics graphics(hdc);
Pen pen(Color(255, 0, 0, 255));
graphics.DrawEllipse(&pen, sf , 0, 10, 10);
i have while loop that loops and adds 1 to sf value basicly like this sf++;
than i try to repaint the window(it doesn't work) so ill end up with more than one circle ;/
here is the loop( the loop is int WM_PAINT)
while(sd==1)//sd equals 1
{
sf++;
onPaint(hdc);
InvalidateRect (hWnd, NULL, TRUE);// this should repaint the window but it doesn't
UpdateWindow(hWnd);
}
thanks in advance
Rami
In order to achieve animation I would suggest you use a timer. For example:
int OnCreate(HWND window, WPARAM wParam, LPARAM lParam)
{
SetTimer(window, TIMER_ID, 1000, 0);
return 0;
}
now window will receive WM_TIMER messages every second (1000ms). You should handle them:
int OnTimer(HWND window, WPARAM wParam, LPARAM lParam)
{
if(TIMER_ID == wParam)
{
/*do stuff*/
InvalidateRect(window, NULL, TRUE);//force window to repaint
}
return 0;
}
then you need to handle WM_PAINT message to do the drawing
int OnPaint(HWND window, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC dc = BeginPaint(&ps);
Graphics graphics(hdc);
graphics.Draw...
EndPaint(&ps);
return 0;
}
You do realize that you are incrementing sf in a loop with a conditional of (sd == 1), right? That will of course just loop infinitely or never be entered because the value of sd is not being changed in any way. Have you used the debugger at all here? Why would you need such a loop anyway? You should not be calling OnPaint in a loop.
If you want more than one circle, just draw them all before returning from the function. Maintain a collection of data that will be used to draw the circles in the OnPaint handler.
InvalidateRect sends a WM_ERASEBKGND message, and if you don't have a hbrBackground (brush to repaint the background) member of the WNDCLASS structure defined when you create the window it won't redraw the background unless you handle the WM_ERASEBKGND message yourself.
If that isn't the problem, then maybe because you are calling UpdateWindow directly instead of polling and handling messages, the WM_ERASEBKGND message never gets handled. Try overwriting the previous circle with the background colour before drawing the new one.
Or call SendMessage with WM_ERASEBKGRND as the message.
I found an example on msdn which shows how to draw stuff in pure win32.
You should not call Invalidate or Updatewindow in WM_PAINT, as UpdateWindow sends a new WM_PAINT-event, and invalidates get accumulated until the next wm_paint event.
You should divide your Code into two functions, one to perform the movement and the other to draw your circle at the current location.
Your Mover-function can be called from anywhere (perhaps in a timer handler function?) and should end with
InvalidateRect (hWnd, NULL, TRUE);
UpdateWindow(hWnd);
In order to mark your client area for redrawal and notify your window to redraw itself.
Your Draw()-function should read the position set with your mover function, and just a draw a circle around this location.
(Sidenote: If you want to minimize flicker and get smooth animation, have a look at double buffering once you get your basic animation up and running)
UPDATE
You were missing the UpdateWindow command in your Update-function
Your OnPaint-Function is only called when a WM_PAINT-message is received by your application, so you need to send those.
UpdateWindow serves this purpose
VOID update(HDC hdc,HWND hWnd)
{
sf++;
FillRect(hdc,rect,(HBRUSH)(COLOR_WINDOW+1));
InvalidateRect (hWnd, NULL, TRUE);
UpdateWindow(hWND);//<- This Line sends a wm_paint-message to your window in order to make it redraw itself
}
//i didn't do any changes to the onPaint functon but here is the code for it
VOID onPaint(HDC hdc)
{
Graphics graphics(hdc);
Pen pen(Color(255, 0, 0, 255));
graphics.DrawEllipse(&pen, sf , 0, 10, 10);
}
//here is the while loop
while(sd==1)
{ onPaint(hdc);
update(hdc,hWnd);
}