Previous page Select page Next page

Improving the "peg" handler - step 1

Compile your program and run it. Remember what happens when we don't have a resize handler to move the button relative to the edge of the window? Take a look at how it reacts now. Pretty cool! The thing is, however, that you would want to use this sort of funtionality in many of your programs and, right now, it's a bit "hard-wired" into this program. You'd have to cut and paste each time you wanted to use it and perhaps even change the names of the labels and so on. This really flies in the face of good programming practice, so we rally should try to improve it - and learn some more new stuff in the process.

The first thing we shoud know is that it is not generally considered to use "magic numbers" in our code. These are absolute numbers which appear, often for no apparent reason, in our programs and our handler has a couple of them. Can you see where they are? Yes, it's the number "4" we used for our two borders. Now, you would probably say "big deal" and, certainly, it is a very small transgression. But imagine that you did not write this code. Also imagine that there were no comments in the code. If you were to look at the handler to work out what it was doing, you see me put the width of the button into EAX and then add 4 to it. You'd be saying "why did he do that?". It is much better practice to declare a label and initialise it to the value you want. So, declare a byte called (say) "pegBorder" in the .Data setion, initialise it to 4 and then use this variable instead of the "4" in your handler. The declaration looks like this, now:

	MESSAGES	DD WM_CREATE, OnCreate
			DD WM_CLOSE, OnClose
			DD WM_SIZE, OnResize
	hButton		DD ?
	pBox		RECT
	pegBorder	DD 4

See if you can do the code for the handler yourself before looking at my solution below. Remember that the behaviour of the program (if you got the code right!) should be completely unaffected but an you see that the code is more "readble"? You don't have to puzzle over the use of "4" in the handler. You have something now called "pegBorder" and that helps the code to become more "self-documenting".

OnResize:
    UseData winMainProcedure

	; 1. Get a handle on the button
	Invoke GetWindowItem, [hWnd], IDC_WINMAIN_BTNEXIT
	Mov [hButton], Eax

	; 2. Get the new size of the window's client area
	Invoke GetClientRect, [hWnd], Addr pBox

	; 3. Get the width of the button and subtract it from the width of the
	;    client area, together with any border
	Invoke GetWidth, [hButton]	; Get the button's current width
	Add Eax, [pegBorder]		; A border of 4
	Mov Ebx, [pBox.right]		; Retrieve the parent's width
	Sub Ebx, Eax			; Subtract the button's (plus border) width from it.

	; 4. Set the "left" position of the button to the above value
	Invoke SetLeft, [hButton], Ebx	; Set the button's left position to the new value

	; 5. Get the height of the button and subtract it from the height of the
	;    client area, together with any border
	Invoke GetHeight, [hButton]	; Get the button's current height
	Add Eax, [pegBorder]		; A border of 4
	Mov Ebx, [pBox.bottom]		; Retrieve the parent's height
	Sub Ebx, Eax			; Subtract the button's (plus border) height from it.

	; 6. Set the "top" position of the button to the above value
	Invoke SetTop, [hButton], Ebx	; Set the button's top position to the new value

	; 7. Return back to the main event loop
	Return (TRUE)
; End OnResize
EndU

Did you remember the square brackets? Using a variable instead of a magic number has another benefit. In your code, you might be doing this addition several times. If you decided that 4 wasn't enough, you would have to go through your code and change all fours to whatever was the new value. Big danger of missing one or two. But with this approach, all you have to do is to change the initialisation value. Go ahead and try that - change the 4 to 8, for example, and then recompile. You didn't have to change a single line of code in the handler. But a word of warning - how big can you make the border using this approach? The answer is 255. You should know the reason for this - it's because we declared "pegBorder" as a byte and bytes can only hold values in the range 0-255. If you really need to have borders beyond that value, you could delare it as a DW. Try this - initialise the DB to 256 and compile it. It'll fail with a "too big" message. Now change the type to "DW", leaving the 256 as the initial value and recompile. It works. Of course, if your window is only 200 pixels wide, the button will appear to have disappeared - you will have placed it to far to the left to be visible! Same goes for the height. Keep that sort of thing in mind.

Improving the "peg" handler - step 2

Have you considered that you might want to peg an object like this on an ad hoc basis? In other words not when you are resizing but for some other reason like someone clicks a button which says "Peg exit button". That would mean you couldn't use the code in a resize handler because the object would be pegged even when you didn't press the button. You might be surprised to learn that you already have all the knowledge to do something about this. Let's do it this way. We want to peg our button exactly like we have done in the example on the previous page but we want our "pegging" code to be a separate routine - not in the resize handler. Wow! How do we do that? It's so simple. Just rename the handler in our program to something like "pegObject" and then make a new resize handler where all we do is to Invoke pegObject. That's all there is to it. Every time the window is resized, your OnResize handler will be called by the main event loop and the handler will call "pegObject". The "pegObject" routine is going to look exactly as it did on the previous page (apart from the name change) and your new "OnResize" handler will look like so:

	OnResize:
		 Invoke pegObject
	; End OnResize

You don't even have to use a UseData instruction, since you don't need any event loop information in order to call our "pegObject" routine. The other thing to notice is that you did not have to declare the "pegObject" routine in the .Data section like you did with "OnResize". Why not? Because you are not associating "pegObject" with an event. The "OnResize" handler takes care of that and it is already declared.

Previous page Select page Next page