Previous page Select page Next page

Frames

Now, this is where it gets really interesting. We can make our pegging routine even more flexible. In effect, I could have called this section "Improving the peg handler - 3". Imagine that we have 2 buttons on the screen. If we wanted to peg both of them, we would have to have two separate peg routines because our code actually needs to know the ID of the button it is to peg - two buttons, two routines. Well, that's a bit inefficient. Or it would be, except for the Frame instruction. Now, frames are a whole ball game all to themselves and goAsm has a lot to say about it but all we need to know about them is to think of the EasyCode methods we have invoked. When we called GetWidth we specified the handle of the object whose width we wanted to get, didn't we? So why can't we tell our "pegObject" routine which window and which button?

It's quite simple, really. Go to your EasyCode "View code" window and scroll down until the cursor is between the end of the OnResize and the beginning of the pegObject routines. Hit Enter a couple of times to move them apart. Now type in:

	pegObject Frame hWnd, idControl

When you hit the Enter key at the end of the line, you will see that EasyCode puts in a Ret for you and an EndF to mark the end of the frame. Moreover, it underlines the end of the frame with a horizontal line. You should regard the Frame .. EndF as being like a pair of tall, brick walls. The rest of the program can't see in. The problem is that, if you're inside the frame, you can't see out. That means that any variables from outside the frame you want to work on have to be "thrown" over the wall. That's the purpose of the parameters in the code above. OK. Copy everything inside the old "pegObject" , except the UseData and EndU instructions, and paste into the new one, overwriting the Ret (we don't need that). You can also delete everything remaining of the old "pegObject".

We're almost ready to gobut firt we need to update our "OnResize" handler. The Invoke statement now needs to pass the handle of the window and the ID of the button to the "pegObject" routine. The ID is no problem but, as you know, the handle to the window is one of those things known inside the event loop, so we'll have to replace the UseData winMainProcedure instruction inside the "OnResize" handler. Don't forget - that means we will also have to add an EndU at the end, too. Now all we have to do is to add the two parameters to the end of the invocation. Remember that the "hWnd" will have to be enclosed in sqrae brackets but here's something neat - when you press the comma after the "pegObject", EasyCode will pop up a hint for you, just as it did for GetWidth to tell you the parameters that "pegObject" now needs! Anyway, to make sure you fully understand what we're trying to do, here's the code of the two routines:

	OnResize:
		 UseData winMainProcedure
		 Invoke pegObject, [hWnd], IDC_WINMAIN_BTNEXIT
		 Return (TRUE)
	; End OnResize
	EndU

	pegObject Frame hWnd, idControl
		; 1. Get a handle on the button
		Invoke GetWindowItem, [hWnd], [idControl]
		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 code
		Return (TRUE)
	EndF

So, what we're going to do now is to add another button to the window and we'll compile and run the program twice - once using the exit button and the second time using the other button. We should see that we can peg either button but with no changes to the "pegObject" routine! However, before we try anything smart, we have to ensure that the code still works as it did originally, so compile it and run it with our single button window. This is called a regression test and is intended to make sure that when we have changed something, we have not broken something else that worked perfectly well before the change. You should always do this.

If the regression test has gone OK, go to the EasyCode visual designer window and add another button. Call it "btnOK" and change the text to "OK". Doesn't matter where on the screen you put it but you should have something like the illustration shown here. This is another change, so do a regression test - compile it and run it. You should see two buttons on the screen but, when you resize it, btnOK should remain stationary but btnExit should move with the bottom right of the window. All is well. Now go to the resize handler and change the invocation to:

	OnResize:
		 UseData winMainProcedure
		 Invoke pegObject, [hWnd], IDC_WINMAIN_BTNOK
		 Return (TRUE)
	; End OnResize
	EndU

Recompile this and, when you run it, you should now find that it's the btnOK that is pegged and btnExit stays where it is! It's the same pegObject code but we are now able to tell it which button to peg and on which window. Terrific.

Local variables

Another great thing you can do inside frames is to create variables which not only are private to the frame but also do not exist outside it. This makes for good programs - we do not allow parts of the programs to see information they do not need to see. OK, what variables in pegObject are used only by pegObject and no other part of the program? The variables "pBox" and "hButton". So, we can remove them from the .Data section of the program and put a line directly after the frame instruction:

	Local hButton:D, pBox:RECT

Note the way in which we tell the assembler that the local variable called "pRect" is a Win32 structure called "RECT". This sort of approach gives us variables which can only be used inside the frame. But, of course, we could put in a line which invoked another routine somewhere else and pass oneof these variables to the second routine in its parameter list.

Previous page Select page Next page