I'm sort of new to c++ and i'm trying to create a game.
I have a 2d array RECT_GRID of rectangles.
I have a 2d array GRID of unsigned short.
I fill the rectangle array during WM_CREATE
The WM_PAINT event paints rectangles for all the elements in the array. The color of the rectangle is based on the value of GRID[x][y]
I made it so when the down key is pressed, It changes the color of one of the rectangles by setting GRID[1][XMOVE] = to a different color
then it invalidates the client rectangle
Basically what happens is, it works well for a while, but eventually it just stops drawing stuff. I checked my XMOVE variable during debug, I checked by grid values and stuff and everything is fine. When I remove the for loop from the paint event and focus on 1 specific rectangle, it never fails, but if I try to redraw all of them at once, after about 20 times, it stops painting things. What could cause this? I'm new to c++ and I bet I'm not painting properly and causing an overflow or something.
If anyone could explain what's going wrong, or a proper way to do this, I'd really appreciate it. I could not find anything like this example on Google.
Thanks
EDIT:
I'm using 3 global brushes
HBRUSH A;
HBRUSH B;
HBRUSH C;
and when I modify them, I always say A = MakeBrush(NUM);
ami I using brushes properly?
My first guess, if you're a total GDI/C++ newbie, is that you are probably creating a lot of Pens and Brushes. These are constrained resources in Windows. You can only create so many of them before you start to tax your resources. So either make your Brushes and Pens and Windows, etc all at once and re-use them, or dispose of them properly when you're done. I recommend getting a copy of "the Bible" (http://www.amazon.com/Programming-Windows%C2%AE-Fifth-Microsoft/dp/157231995X/ref=sr_1_1?ie=UTF8&s=books&qid=1252788457&sr=8-1) and reading the chapters in there about drawing.
EDIT: It doesn't sound like you're modifying your brushes properly, but since I can't see the code for MakeBrush, I don't know. You're probably creating a lot of brushes behind the scenes and you don't even know it. Seriously, get a copy of Petzold's book and spend an hour or two. You'll end up with more hair on your head later! ;-)
You'll notice in all GDI examples, a 'CreatePen' or 'CreateSolidBrush' will be followed by a delete object. This is because they are limited resources in windows, and you can run out of them if you don't delete them when you're finished.
PAINTSTRUCT Ps;
HDC hDC = BeginPaint(hWnd, &Ps);
HBRUSH NewBrush = CreateSolidBrush(RGB(250, 25, 5));
SelectObject(hDC, NewBrush);
Rectangle(hDC, 20, 20, 250, 125);
DeleteObject(NewBrush);
EndPaint(hWnd, &Ps);
If you forget to include the 'DeleteObject' call, then you will have problems after you use up all the brushes available. It's actually fun to try =)
Related
In my small, small and humble c++ Win32 GUI application, I got to the point of being able to draw a grid :
This is in preparation for displaying spectral data, the 36 (floats) measurement points returned from my device. I still have to get the axis labeled... This is my code for drawing the grid so far :
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, HBRUSH (COLOR_WINDOW + 1));
bool Retour;
pen = (HPEN)GetStockObject(TRANSPARENT);
SelectObject(hdc, pen);
Brosse = CreateSolidBrush(RGB(240, 240, 240));
SelectObject(hdc, Brosse);
Retour = LineTo(hdc, 100, 500);*/
Retour = Rectangle(hdc, 350, 150, 750, 375);
for (int i = 380; i <= 730; i=i+10) { // 36 vertical lines
MoveToEx(hdc, i, 175, NULL);
LineTo(hdc, i, 350);
}
for (int i = 175; i <= 350; i = i + 17) {
MoveToEx(hdc, 375, i, NULL);
LineTo(hdc, 730, i);
}
DeleteObject(pen);
DeleteObject(Brosse);
EndPaint(hWnd, &ps);
As you can see, I located the code to draw the grid inside the WM_PAINT procedure. It made sense to locate the code there to experiment with GDI instructions but also because the grid needs to be visible at launch time as part of the interface.
My question is how should I go "updating" the graph when, after pressing the "Measure Sample" button, I have access to the data? I gather that the code to draw on the grid does not necessarily needs to be located inside the WM_PAINT procedure, does it? According to my limited understanding, as long as I can get a Device Context handle (hDC), I should be good to go? All I need to plot is a "line" uniting the 36 data points from left to right. Not sure I should use simple MOVE_TO and LINE_TO for this purpose? I think there is a way to draw a "smoothed" line that passes through all 36 data points?
One last thing, if I may... I'm going at this in a very elementary way because I'm intimidated by the idea of using a "graphic library" and objects. But I suspect they would make my life a lot easier while offering umpteen options that will take me too long to figure out, I'll bet, and implement?
Thank you so much in advance for your kind help and patience.
You should always do all your drawing in your WM_PAINT handler. That way, the window will redraw correctly when something that was covering it is removed, for example.
When your graph data changes, call InvalidateRect to prompt a redraw.
To determine what to redraw, you will need to pass suitable variables to your WM_PAINT handler. Start with MoveTo and LineTo (or Polyline or PolyPolyline) and get that working first. You can then investigate smoothing algorithms if you think you need them.
You should place all of your custom painting code in the WM_PAINT handler of the window, yes. You can call a separate function for painting the graph if you like. Pass it the HDC you get from BeginPaint(...) and your spectral data. However, that function needs to be called from the WM_PAINT handler.
If you handle painting in WM_PAINT then to update the graph when the data changes you would call InvalidateRect(...) on the window. InvalidateRect tells Windows essentially "this window has changed and needs to be repainted". It will drive a call to the paint handler.
One problem you are going to run into is that your graph is going to flicker. It may not be too noticeable. It may not bother you, but the problem is that in each call to the WM_PAINT handler you are erasing the graph in the screen device context; this erasure will be visible. A solution to this problem would be to "double-buffer" the graph: create an offscreen bitmap, paint into that when data changes, and paint the offscreen bitmap to the screen in WM_PAINT. You could also make the graph a separate child window that manages the offscreen bitmap, etc., if you are ever going to need multiple graphs or just to add some modularity to the code.
As for whether you'd have an easier time with a graphics library ... possibly, but more to the point: you would have an easier time using a higher level GUI library than the Win32 API. If you only care about Windows, consider the .Net frameworks WinForms or WPF. Otherwise, consider Qt.
I'm currently experiencing an error with my program.
My program has two rectangles, each of them surrounded by a groupbox.
Currently I'm trying two place text in each of them.
I've already got text in the first rectangle - fine (with DrawText()).
If I'm trying to display text in the second rectangle (also with DrawText()), the text is not displaying.
Here's the code I'm using:
hdc = BeginPaint(hwnd, &ps);
SelectObject(hdc, font);
DrawText(hdc, lpcwstr, wcslen(lpcwstr), &rect, DT_LEFT);
DrawText(hdc, lpcwstr, wcslen(lpcwstr), &rect_2, DT_LEFT);
EndPaint(FindWindow(NULL, L"Statistik (seit dem 01.01.2015)"), &ps);
Unfortunately I have no idea why the text is not displaying as expected.
Does anybody of you have a suggestion why this doesn't work?
[SOLUTION]
I was able to figure out what the problem was.
Not visible in the snippet, I posted initially, but the coordinates I set for the rectangles, were wrong.
Sadly I were not aware that all values are coordinates.
The only difference between the two calls is the RECT that you pass. The obvious conclusion is that you initialised one of the rectangles incorrectly.
You should restore the original font before calling EndPaint. And the call to FindWindow should be removed. Pass hwnd. Finally, your code performs no error checking at all. That's always imprudent.
INTRODUCTION AND RELEVANT INFORMATION:
I have implemented complex painting of the main window’s background and its child static controls.
The picture below shows how it looks.
Static controls have SS_NOTIFY style, which is important to mention, as certain things happen when user clicks on them.
At this point, actions activated when clicking on them, are not relevant.
Both main window, and static controls, have gradient backgrounds, which were made through usage of GradientFill(...) API.
Top banner of the main window is created with gray brush, and the grid lines were created with LineTo(...) and MoveTo(...) API.
Map on the orange static control, and the top left logo are EMF files, top right logo is PNG file, and other pictures are bitmaps.
Orange static control has 4 child static controls which are owner drawn and also have SS_NOTIFY style.
It was the only way I could have thought of, which enabled me to draw the control the way it was asked of me ( if I can improve on this, please suggest it, I will accept any reasonable suggestion ).
In order to paint the orange static control, I have decided to paint its background in WM_CTLCOLORSTATIC handler, and to owner draw child static controls in a subclass procedure.
Notifications received from child static controls are also handled in the orange static controls subclass procedure, since I didn’t know how to forward them to the parent window, but are omitted since they are also irrelevant at this moment.
I have decided to provide link to the demo project, instead of making this post quite lengthy with code snippets.
I have tried to submit demo application as small and simple as it is possible.
I did not skimp on the commentaries, so I believe everything is well covered and explained in source code.
If there are still questions please leave a comment and I will reply as soon as possible ( usually immediately, or in the same day, at least ).
Here is the link to the demo project:http://www.filedropper.com/geotermistgrafika_1
Important update:
/==========================================================/
Text bellow in square brackets was the original part of the question, but is now omitted since the project had memory leaks.The above link links to an improved version.
[ Updated in response to member xMRi's comment: This link should be fine: http://www.filedropper.com/geotermistgrafika ]
/==========================================================/
I work on Windows XP, using MS Visual Studio C++ and pure Win32 API.
One note: since Express edition of VS has no resource editor, resource file and resource header were created using ResEdit from here: http://www.resedit.net/.
PROBLEM:
When I resize my window, static controls slightly flicker.
MY EFFORTS TO SOLVE PROBLEM:
I believe that my code has no memory leaks-therefore I doubt this is the problem, but being inexperienced, I would highly appreciate if my assumption can be somehow confirmed.
I think that I have properly handled WM_ERASEBKGND, and I have excluded styles CS_VREDRAW and CS_HREDRAW from my window class-therefore flickering should not be caused because of this.
I have forgot to mention, that my window has WS_CLIPCHILDREN style, so I am mentioning that now, in response to the comment bellow made by member Roger Rowland.
I have implemented double buffering for both handlers, in order to avoid flickering.
QUESTIONS:
How can I modify code in demo project to get rid of flickering?
I need advice on how to optimize both WM_PAINT and WM_CTLCOLORSTATIC handlers, so my painting code gets more efficient and faster.
A small note for second question:
I was thinking to improve my code by drawing the entire picture on the main window’s background, and to put transparent static controls on top of the part of the picture that corresponds that static controls background.
That way, I would only return NULL_BRUSH in my WM_CTLCOLORSTATIC handler, and do all the work in the WM_PAINT.
Am I on the right track with this idea? Could this work ?
Thank you.
Regards.
Firstly, your App is leaky as hell. Haven't looked for leaks, but most of them should be in WM_CTLCOLORSTATIC as you forget to delete HBITMAP's(use this neat freeware http://www.nirsoft.net/utils/gdi_handles.html to look for gdi leaks).
Secondly, your code is way to big. I noticed that you didn't use functions, maybe because you don't know what they are capable of. For example I would use:
void DrawBackground(HDC &hDC, SOMEINFOSTRUCT GradientInfo, LPCTSTR Text);
to simplify your code a lot.
Anyway enough of lecturing, let's go back to your problem. In WM_CTLCOLORSTATIC you must return brush, you want to paint background with. What you're doing now is painting background manually using Bitblt(), then return NULL brush and program paints it on your already painted background. Instead of painting it yourself, let the brush do the job.
Simply instead of the last Bitblt() use CreatePatternBrush(), but then you need to take care of this Brush and here is what you should do:
HBRUSH TempBrush = NULL; //Create global brush
//Some Code....
case WM_CTLCOLORSTATIC:
{
if (TempBrush != NULL)
{
DeleteObject(TempBrush);
TempBrush = NULL;
}
//Let's skip to the end....
GradientFill( MemDC, vertex3, 3, &gTriangle, 1,
GRADIENT_FILL_TRIANGLE );
TempBrush = CreatePatternBrush(bmp);// these 3 line should be at the
//end of every if
DeleteDC(MemDC); // or put them once outside if's
DeleteObject(bmp); // also if you delete HDC first, you don't need to
//unselect hbitmap
}
return (LRESULT)TempBrush;
}
break;
case WM_CLOSE:
{
if (TempBrush != NULL)
{
DeleteObject(TempBrush);
TempBrush = NULL;
}
//.......
I've created two windows with CreateWindowEx(), and now I'd like to stick them side-by-side, so that whenever one is moved, the other one moves relative to the other.
What's the right way of implementing this?
At the moment, I'm using this piece of code:
case WM_MOVING: // When the main window is being moved
GetWindowRect(hWnd, &rWnd); // Get the current position of the main window
MoveWindow(hwStats, rWnd.right - 1, rWnd.top, 140, 450, 1); // Move the other window relative to the main window
return 1; // WM_MOVING is handled by the application
break; // Done.
The problem with this, is that whenever I move the window, the other window is dragged a few pixels behind.
Now, it doesn't look too bad, but I'd really prefer if it looked a bit more solid.
To fix this problem, I needed to change the case from WM_MOVING to WM_MOVE, and the function MoveWindow() to SetWindowPos().
Thank you to Martin James, who told me about "Windows API Docking". That was very helpful.
i have the following code ...
case WM_PAINT:
{
hdc = BeginPaint(hwnd,&paintSt);
temphdc = hdc;
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;
hBitmap is a global variable which is assigned at some place in the code.... Image is displayed but disappears whenever I minimize the window....
can anyone explain this ?
thanks in advance,
Your cleanup code is all wrong, you are leaking handles badly. Should be readily visible in TaskMgr.exe, Processes tab. View + Select Columns and tick GDI Objects. This code stops working when the GDI object handle count reaches 10,000. Yes, likely to happen when you resize the window since there will be a flurry of paint requests.
Don't delete the BITMAP, it is just as struct. Restore the old bitmap handle you got back from SelectObject() before you delete the memDC. Don't use ReleaseDC, DeleteDC is required. Pay attention to the return value of these functions, they tell you when you messed up but that can't work if you never check with an assert.
GDI programming is painful with these explicit cleanup rules. Consider a class library to take care of this kind of drudgery, they never get it wrong.
I guess somehow hBitmap is changing to null while minimize.
Posting the code where you are assigning and referring hBitmap will help to identify the issue I think.