April 16, 2024, 01:33:22 PM

News:

Own IWBasic 2.x ? -----> Get your free upgrade to 3.x now.........


5a. IWBasic Controls

Started by LarryMc, August 08, 2011, 11:46:05 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

LarryMc

August 08, 2011, 11:46:05 AM Last Edit: August 08, 2011, 08:07:17 PM by LarryMc
Part A

As was said about opening a window in IWBasic being extremely easy, so is adding a control to a window.  We will use the same methodology to explore a control. Controls are created with the following command line:

QuoteCONTROL parent, type, title, left, top, width, height, style_flags, id

This is simply a call to a subroutine that is sent nine parameters (pieces of information) to use to do whatever the subroutine has to do to open our control.  Let's look at those parameters.

parent

Unlike a window, all controls must have a parent window. This parameter will be the WINDOWS type UDT variable name of the control's parent.

type

An IWBasic predefined constant variable identifying the type of control to create. The available IWBasic  control types are:
@BUTTON
@CHECKBOX
@RADIOBUTTON
@EDIT
@LISTBOX
@COMBOBOX
@STATIC
@SCROLLBAR
@GROUPBOX
@RICHEDIT
@LISTVIEW
@STATUS
@SYSBUTTON
@RGNBUTTON
@TREEVIEW

title

The text of the control

left, top, width, height

The location and size of the control in pixels.  Where the location parameters of a window were relative to the screen, the location of a control is relative to the upper left corner of the parent windows client area.

style-flags

This is a single UINT whose individual bits each have some specific impact on the appearance or functionality of the control. Usually, to make things easier to read, each bit is given a CONST definition representing one bit and various bits are or'd together with the' |' operator.

id

A unique UNIT type variable, constant, or literal number that identifies the specific control.
_____________________

Notably absent in the list of passed parameters is a handler variable. Controls both send and receive messages. All messages that are sent by controls are automatically routed to their parent's message handler by the OS.

Let's modify example ex_6.iwb to show how to add a simple button to our window and see what message we can generate.
Ex_7.iwb
openconsole
DEF w1 as WINDOW
OPENWINDOW w1,0,0,350,350,@MINBOX|@MAXBOX|@SIZE,NULL,"Simple Window",&main
CONTROL w1,@button,"1st",40,40,50,20,0,1
CONTROL w1,@button,"2nd",140,40,50,20,0,2
WAITUNTIL w1 = 0
closeconsole

SUB main
SELECT @MESSAGE
CASE @IDCONTROL
? "control message"
CASE @IDCLOSEWINDOW
CLOSEWINDOW w1
ENDSELECT
RETURN 0
ENDSUB
END

In ex_7.iwb we have added two buttons immediately after the OPENWINDOW command.  In the windows handler all but two predefined messages have been removed. In the CASE @IDCONTROL portion of the handler there is a print statement.

Compile and run the example.  Make sure the console window is not covered by the "Simple Window".  Click on various locations in the client area of window including on the two buttons.  Notice that when either of the buttons are clicked that the "control message" text is printed in the console window.  This indicates that each time an event occurs involving a control that an @IDCONTROL message is sent to its parent window.

But this isn't telling us which control has had an event.  IWBasic provides us that information.  When an @IDCONTROL is received IWBasic loads a system variable with the identify of the control sending the message. This system variable is @CONTROLID. We can modify our example so that this can be seen.

Ex_8.iwb
openconsole
DEF w1 as WINDOW
OPENWINDOW w1,0,0,350,350,@MINBOX|@MAXBOX|@SIZE,NULL,"Simple Window",&main
CONTROL w1,@button,"1st",40,40,50,20,0,1
CONTROL w1,@button,"2nd",140,40,50,20,0,2
WAITUNTIL w1 = 0
closeconsole
END

SUB main
SELECT @MESSAGE
CASE @IDCONTROL
? @CONTROLID
CASE @IDCLOSEWINDOW
CLOSEWINDOW w1
ENDSELECT
RETURN 0
ENDSUB

When this example is ran the ID used when the control was created is printed when the control is clicked.  But let's be a little more precise in our test. Press the left mouse button over one of the buttons and hold it down.  Notice nothing is printed.  Let the button up. Notice that the buttons ID is printed.  Right click on a button.  Nothing happens.  So, the button only sends a message when the left mouse button is released.  This specific operation has been programmed into the button.  We know that there is a left button pressed event because we see the appearance of the button change when we press the button.  So, somewhere there was a decision made to have the button change its appearance when the button was pressed and then again when it was release but only for the left mouse button.  And somewhere the code to do this has to reside.

Before we go further, let's take another look at where the values for these system variables are coming from.  In the Messages and Message Handlers - Part C section we learned that all messages are ultimately sent with the SendMessage API command. Also, we learned that when a window handler receives a message, IWBasic loads @MESSAGE, @WPARAM  and @LPARAM with the message's information.

In our example we know that @MESSAGE contains a value equal to @IDCONTROL, which tells us a control is send a message. Let's modify our example to print  @WPARAM  and @LPARAM.

In ex_8.iwb change this line:
? @CONTROLID
to
? @CONTROLID, @WPARAM, @LPARAM

Compile and run.  Click on the two buttons and observe the results in the console window.

We see that @WPARAM contains the clicked control's ID from when it was created.  @LPARAM contains a large number. The value appears to be constant for a specific control.

In the window discussion we stated that the OS doesn't know anything about IWBasic variable names and such.  The OS assigns handles to windows when they are created.  The number contained in @LPARAM is the control's handle.
We can prove this very easily. IWB contains a function, GETCONTROLHANDLE, where the parent window and control ID variables are passed in and the OS handle is returned.

In ex_8.iwb change this line:
? @CONTROLID
to
? @CONTROLID, @WPARAM, @LPARAM, GETCONTROLHANDLE(w1,@CONTROLID)

Compile and run.  Click on the two buttons and observe the results in the console window. In each case the first two numbers are the same (control's ID ) and the last two numbers are the same (the control's OS handle).

So far, our testing of controls has involved only buttons.  We really need to try it with another type control to see if our "rule" is correct.  We'll use a simple edit control for this purpose.

Taking our last, modified version of ex_8.iwb and adding the edit control results in:
Ex_9.iwb
openconsole
DEF w1 as WINDOW
OPENWINDOW w1,0,0,350,350,@MINBOX|@MAXBOX|@SIZE,NULL,"Simple Window",&main
CONTROL w1,@button,"1st",40,40,50,20,0,1
CONTROL w1,@button,"2nd",140,40,50,20,0,2
CONTROL w1,@edit,"3rd",240,40,50,20,0,3
WAITUNTIL w1 = 0
closeconsole
END

SUB main
SELECT @MESSAGE
CASE @IDCONTROL
? @CONTROLID,@WPARAM,@LPARAM,GETCONTROLHANDLE(w1,@CONTROLID)
CASE @IDCLOSEWINDOW
CLOSEWINDOW w1
ENDSELECT
RETURN 0
ENDSUB

Compile and run.  Right off the bat you notice a difference.  Two lines are printed before you have had a chance to do anything.  As before with the buttons the last two numbers match on each line (the control's OS handle).  But notice that the first two numbers don't match.  However, the first number is the id we assigned to the edit control when it was created so that part is correct.  We had said earlier that the @CONTROLID variable was set to the value of the @WPARAM when it was sent in the message via the wparam argument.  It appears something else is going on here that wasn't seen with the button controls.  For our "rule" to be true the control's ID has to be contained in the second number(@WPARAM).
In order to reduce the clutter let's change this line:
? @CONTROLID,@WPARAM,@LPARAM,GETCONTROLHANDLE(w1,@CONTROLID)
to
? @CONTROLID,@WPARAM

In the declaration statement for the SENDMESSAGE command it states that the wparam parameter is a UNIT which means it is a 32 bit number.
Let's look at the lower 16 bits of @WPARAM.  We can accomplish this by ANDing the variable with 0xFFFF. To see the result, change
? @CONTROLID,@WPARAM
to
? @CONTROLID,@WPARAM & 0xFFFF, @WPARAM

Compile and run. Observe that the second number now properly reflects the control's IWB ID. So, our previous "rule" that the ID was contained in @WPARAM is true but we need to be more precise and say it is contained in the lower 16 bits of that variable.

We can just throw away the high order bits.  They have to be telling us something.  Let's discard the 16 low order bits and shift the high order bits down 16 bits.  This will give us the 16 high order bits as a 0 based number.  We accomplish this by simply dividing the variable by 0xFFFF.

Replace this line:
? @CONTROLID,@WPARAM & 0xFFFF, @WPARAM
with
? @CONTROLID,@WPARAM & 0xFFFF, @WPARAM / 0xFFFF

Compile and run. Notice the pattern of numbers.  When we run the example we get to lines (messages) without any activity from the User.
If we click in the edit control we get a third number  but only the first time it is clicked in a sequence.  We get a fourth number when we click outside the edit control but, again, only the first time in a sequence.  And finally, each time we change the contents of the edit control we get the two message sequence we got originally.

The following example shows what those changing numbers mean.
Ex_10.iwb
openconsole
DEF w1 as WINDOW
OPENWINDOW w1,0,0,350,350,@MINBOX|@MAXBOX|@SIZE,NULL,"Simple Window",&main
CONTROL w1,@button,"1st",40,40,50,20,0,1
CONTROL w1,@button,"2nd",140,40,50,20,0,2
CONTROL w1,@edit,"3rd",240,40,50,20,0,3
WAITUNTIL w1 = 0
closeconsole
END

SUB main
SELECT @MESSAGE
CASE @IDCONTROL
? "@ENUPDATE= ",@ENUPDATE
? "@ENCHANGE= ",@ENCHANGE
? "@ENSETFOCUS= ",@ENSETFOCUS
? "@ENKILLFOCUS= ",@ENKILLFOCUS
? @CONTROLID,@WPARAM & 0xFFFF,@WPARAM / 0xFFFF
CASE @IDCLOSEWINDOW
CLOSEWINDOW w1
ENDSELECT
RETURN 0
ENDSUB

This example has four extra print statements that print the values of four IWB system constants.  As with many other constants their names and values are based upon OS constants.  These constants, and more, for edit controls can be found in the IWB User's Guide under Windows Programming / Controls / Edit Controls.

Compile and run.  Perform the various activities and observe the nature of the messages.

Now, notice that each of the message variables start with @EN. The E is for edit controls and the N stands for notifications.  Just a windows  receive hundreds of messages directly so do controls.  Controls report many of their messages to their parent window to allow for responses. The messages are called notification messages.  They are still sent with the same SENDMESSAGE type message structure.

IWB provides us an easy way to obtain these notification message ids by doing what IWB does for us with the other message variables we've covered.
When IWB decodes the @WPARAM to put the control ID into @CONTROLID it puts the notification message ID in the IWB system constant  @NOTIFYCODE.
Change this line:
? @CONTROLID,@WPARAM & 0xFFFF,@WPARAM / 0xFFFF
to
? @CONTROLID,@WPARAM / 0xFFFF,@NOTIFYCODE

Compile and run.  This confirms that @NOTIFYCODE contains the data from the high order bits of @WPARAM.

Not all controls send notification messages.  Click one of the buttons.  The notification message ID is 0.  That is because the sending of the message is enough by itself without any additional clarification (notification) information as to the type of event that occurred.
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library