i know this can be done, although - it may not be as simple as it ought to be :(
this is basic stuff that maybe MS left out
i know Hutch's QE does it - but that may be different because it is a rich edit control
any ideas ?
during class registration, i am using this code to get a black brush (per MichaelW):
INVOKE GetStockObject,BLACK_BRUSH
mov wc.hbrBackground,eax
at the moment, i am trying NULL_BRUSH instead (which is interesting by itself - lol)
then process the WM_ERASEBKGND message - i am thinking maybe BitBlt the user-selected background color
i'll let you know how it goes :bg
in the mean time - maybe some other member already knows a better way ?
well - i learned that setting the hbrBackground member of WNDCLASSEX to NULL will get you the WM_ERASEBKGND message
if i then process the message by returning TRUE with no fill code, it is similar to using NULL_BRUSH
now all i need is a fast way to fill the rectangle :bg
working on it....
well i have it up and running - it works great
but - i have a question about handling the WM_ERASEBKGND message
the msdn documentation is a little sketchy (again)
i have to figure that the erase handler may only be required to erase part of the client area
so - to find out what part, i use BeginPaint and EndPaint, just like a WM_PAINT handler
now, the BeginPaint docs say that those functions should only be used to process WM_PAINT messages
but, at the end of the WM_ERASEBKGND docs, under "Other Resources", it lists BeginPaint and PAINTSTRUCT
is it safe to assume that i am doing it the right way ?????
Why not setting wc.hbrBackground to zero and clear the background while drawing (WM_PAINT,...). On the other hand, you can use GetUpdateRect() to receive the rect, that must be update.
that's what i did - GetUpdateRect - with a NULL so that it doesn't send another WM_ERASEBKGND message
works great :bg
Quote from: dedndave on January 09, 2011, 12:52:26 PM
now all i need is a fast way to fill the rectangle :bg
working on it....
What about ExtFloodFill or FillRect?
well - i am using CreateCompatibleDC, DIB, BitBlt
it is pretty fast
although, those methods sound easier
i may have to try them out
Hi Dave,
I don't mean to sound pedantic but this seems like a lot of work for a simple task. Since you are setting wc.hbrBackground I assume that you are using a private window class. In that case changing the background color is as easy as:
invoke SetClassLong,[hwnd],GCL_HBRBACKGROUND,[hNewBrush]
; OR
invoke SetClassLong,[hwnd],GCL_HBRBACKGROUND,COLOR_BTNFACE+1
nothing pedantic there, Edgar - lol
i am here to learn
and - it is a lot of code for what should be something simple
i didn't realize that i could alter the registered class
as i have it now, i am setting the wc.hbBackground to NULL
but, if i don't have to have an erase handler, that would be great
i will try it out a little later - have to go take care of dad for a while
Family comes first :thumbu
Here's a little demo that allows you to select the color of the window when the app starts. You can actually do it any time but I didn't feel like writing the code for that so I just put the color selection in the WM_CREATE handler. Demonstrates the method though.
Edgar
Dave,
You try and line it up so that only one window upgrades in the client area of your main window otherwise you get flickering when you resize the window. Now in the case of where you have another control fully covering the client area you set the main window background brush to NULL so it does not keep repainting under the other control.
thanks Edgar - i appreciate the help
Hutch - it's a simple single-client app - lol
the problem is...
... i am a total n00b at this stuff :bg
i feel like i have read half the msdn site, but i know i haven't even put a dent in it
Dave,
After a while it gets better when your head stops hurting (from banging it against the wall). The basic design of how the OS creates a window is to set the class background brush in the WNDCLASSEX structure. The code that Edgar posted is how you change it later by changing the class attribute. You can blame Win32 on the old VAX guys who stuffed as much as they could into structures.
There are other ways to set the colour of the client area like sizing a rectangle set with your chosen colour but the WNDCLASSEX background brush performs a lot better.
With the class attribute each time Windows must update the display (if a window has been overlapped by another for example) it just picks the colour from the CLASS that the window was created with and fills the area that has been messed up.
LATER: Here is a simple demo on setting the client area color. I have marked the code in the WndProc so you can easily find it.
Hi Hutch,
You can potentially run out of resources if you don't delete the brushes. The return value of SetClassLong is the old brush handle which is no longer needed. So you should think about adding a DeleteObject. Also InvalidateRect does not need a RECT if you're adding the entire client area
invoke CreateSolidBrush,0000FF00h ; GREEN as COLORREF number
mov hBrush, eax
invoke SetClassLong,hWin,GCL_HBRBACKGROUND,hBrush
invoke DeleteObject, eax ; <<<< Delete the old brush <<<<<<<<<<<
invoke InvalidateRect,hWin,NULL,TRUE
I was trying to pass Dave a simple enough example without extra the complexity. The cleanest way to do this is as follows.
Brush handles in the .DATA? section
.data?
redbrush dd ?
greenbrush dd ?
bluebrush dd ?
Set the brush colors early in the code section
mov redbrush, rv(CreateSolidBrush,000000FFh)
mov greenbrush, rv(CreateSolidBrush,0000FF00h)
mov bluebrush, rv(CreateSolidBrush,00FF0000h)
Use the brushes when you change color.
case 2001
invoke SetClassLong,hWin,GCL_HBRBACKGROUND,greenbrush
invoke GetClientRect,hWin,ADDR Rct
invoke InvalidateRect,hWin,ADDR Rct,TRUE
Ditch the brushes on exit although it does not matter there.
A test piece to test the difference between a selected object and a brush handle shows an interesting result.
push esi
mov esi, 1000000
@@:
invoke SetClassLong,hWin,GCL_HBRBACKGROUND,redbrush ; set the class attribute
;;;; invoke DeleteObject,eax
invoke GetClientRect,hWin,ADDR Rct ; get the client area rectangle
invoke InvalidateRect,hWin,ADDR Rct,TRUE ; force repaint by invalidating it
invoke SetWindowText,hWnd,str$(esi)
sub esi, 1
jnz @B
pop esi
This code will lock up the display with or without DeleteObject. The difference is with it the display is not updated after the loop finishes when you use DeleteObject() on the old brush handle. It just happens to be that the brush handle set in the WNDCLASSEX structure is not selected into a device context so there does not appear to be any purpose in trying to delete a non selected object.
Quote from: hutch-- on January 10, 2011, 04:18:20 AM
I was trying to pass Dave a simple enough example without extra the complexity.
I wasn't saying your way was wrong just suggesting that you delete your brushes. The issue in the loop is SetWindowText, remove that and the loop runs fine, however the difference in available physical memory between using DeleteObject and not using it is around 434KB on my system, not much considering a million iterations but good coding practice to avoid it all the same. You are right that creating the brushes in global memory solves the problem of deleting them after each change perhaps Dave could use that approach.
QuoteIt just happens to be that the brush handle set in the WNDCLASSEX structure is not selected into a device context so there does not appear to be any purpose in trying to delete a non selected object.
Every object created by the Windows Executive consumes resources whether it is used or not and you never delete an object while it's selected into a device context, that's verbotten.
nice try, Hutch
but, i'm afraid you're too late - i already complicated the hell out of it
maybe you didn't see the first few posts of the thread :lol
as for whether or not to delete the brushes - or to use pre-made ones - i think it depends on how they are used
the plan here was to offer the user a color chooser dialog and let them pick any color
that being the case, deleting the old brush is probably a good idea
This is the one you will have fun with if you put it into a large loop.
mov redbrush, rv(CreateSolidBrush,000000FFh)
The propblem as I see it is the same API call to set the class brush can be used with pre-defined system brushes with no difference in how they are passed to the SetClassLong API and you clearly do not delete the system brushes.
The rough rule with GDI based functions is if you select the object you delete it after its used. The WNDCLASSEX structure reference says this.
Quote
The operating system automatically deletes class background brushes when the class is freed. An application should not delete these brushes, because a class may be used by multiple instances of an application.
Quote from: hutch-- on January 10, 2011, 05:53:19 AM
This is the one you will have fun with if you put it into a large loop.
mov redbrush, rv(CreateSolidBrush,000000FFh)
The propblem as I see it is the same API call to set the class brush can be used with pre-defined system brushes with no difference in how they are passed to the SetClassLong API and you clearly do not delete the system brushes.
The rough rule with GDI based functions is if you select the object you delete it after its used. The WNDCLASSEX structure reference says this.
Quote
The operating system automatically deletes class background brushes when the class is freed. An application should not delete these brushes, because a class may be used by multiple instances of an application.
That's an incorrect assumption. You are not deleting a class brush, once you use SetClassLong the brush it returns is no longer a class brush, its just a normal brush and should be deleted. Once you change the class background brush the old one is not deleted when UnregisterClass is executed, only the one returned by GetClassLong is deleted. You can verify this by creating a class, assigning it a different brush then checking to see if the returned brush is destroyed when the window is destroyed and the class is unregistered, it isn't, I checked :wink
also - i read somewhere in all that documentation that...
it is ok to attempt to delete a system brush because the attempt is ignored and system brushes are not deleted
Dave,
One of the lessons I have learnt with Windows API coding is be careful about deallocating what has not been allocated. Often it just does not matter and it will be silently ignored but there are also times when it trashes the app so I have long been careful to match what goes up with what goes down.
When you start working with GDI graphics, the simple rule is write your code so that anything you select as an object is deselected and replaced with the original once you have finished with it. It sounds a bit complicated but the logic is simple enough.
oldobject = SelectObject(newobject)
; use new object
SelectObject oldobject
DeleteObject newobject
Its basically a replace then restore operation and particularly with GDI objects that are selected if you mess this up you get a memory leak that gets worse as the function is called more often. In 16 bit Win3.? it would crash the OS after a while.
You avoid most problem in this area and similar by designing the allocation and deallocation at the same time them write the code inbetween. Tracking down leaks after code has been written is no joy at all so you code a style that saves you the problem. It is also worth checking your return values to make sure the deallocation worked correctly.
well - there is no need to delete any system brushes, actually
i can create a brush to begin with, so i don't have to use GDI stock brushes at all
i have Edgar's code implemented - and it works great
every bit as fast or maybe faster than my original DIB/BitBlt code - which was extensive
(both are so fast, you'd have to measure to tell a difference)
this thing is almost done :P
i really appreciate all the help, especially the explanations, that you guys have provided :U