Page tree
Skip to end of metadata
Go to start of metadata

Overview

The article Scripting How-To: Adding a Custom Dialog, talks about how to perform some simple dialog initialization, such as populating a list box, or setting a default value for a text box control. Much more capability is included in Sax VB in terms of manipulating a dialog after it is presented to the user, and this page will talk about these capabilities. This discussion will assume you are already familiar with general dialog development, including the content here:  Scripting How-To: Adding a Custom Dialog

Put simply, dynamic dialog control is all about the DialogFunc. A DialogFunc is a special type of function that is associated with the dialog GUI by entering a name in the field, like this:

NOTE: To get to this dialog, you can do one of two things:

  • After Insert->UserForm, just start typing a name. The editor will assume that you are specifying a "Caption" for the dialog and open this form, putting the name you typed in the "Caption" field (and, therefore, making this the caption on your dialog when it is opened at run-time).
  • After Insert->UserForm, double-click anywhere on the form that is not occupied by another control and this form will open.

By setting the "Dialog Function" field to "DialogFunc", we are making the required link between the GUI and the event handlers. When you apply the changes from the dialog editor to the code, you will get a prompt:

We recommend you always say "Yes". What happens when you say "Yes" is that a VB function is added at the end of your code, and it is populated in a very specific way. VERY IMPORTANT: A Dialog Function MUST take the form that is generated automatically as skeleton code. The individual cases in the Select Case block are predefined and can not be changed. We can, however, add event handlers within this framework and create many types of actions by our dialogs.

Our example below will illustrate how to use all the dynamic events available to a dialog. The general functionality will be that the user will select a number from a drop-down list and the selected number of dialog controls will be shown in the GUI. If the user chooses 1, then one text label and one text box will be displayed. If the user chooses 2, then two of each control will be displayed. We will dynamically control what the text labels say, we will enable/disable controls, and we will turn controls' visibility on and off.

Getting Started

To start, paste the following code into a new code module:

Option Explicit
Dim i As Integer
Dim nums(4) As String

' Code Module
Sub Main
	For i = 1 To 4
		nums(i) = CStr(i)
	Next i

	Begin Dialog UserDialog 510,231,"My Dialog",.DialogFunc ' %GRID:10,7,1,1
		Text 20,14,170,14,"Pick a number from 1 to 4:",.Text5
		DropListBox 90,35,90,70,nums(),.num
		Text 280,14,210,14,"MsgOut",.msgOut
		Text 290,70,50,14,"Box 1",.Text1
		Text 290,98,50,14,"Box 2",.Text2
		Text 290,126,50,14,"Box 3",.Text3
		Text 290,154,50,14,"Box 4",.Text4
		TextBox 350,70,90,21,.TextBox1
		TextBox 350,98,90,21,.TextBox2
		TextBox 350,126,90,21,.TextBox3
		TextBox 350,154,90,21,.TextBox4
		OKButton 200,203,90,21
	End Dialog
	Dim dlg As UserDialog
	Dialog dlg


End Sub

Rem See DialogFunc help topic for more information.
Private Function DialogFunc(DlgItem$, Action%, SuppValue&) As Boolean
	Select Case Action%
	Case 1 ' Dialog box initialization
	Case 2 ' Value changing or button pressed
		Rem DialogFunc = True ' Prevent button press from closing the dialog box
	Case 3 ' TextBox or ComboBox text changed
	Case 4 ' Focus changed
	Case 5 ' Idle
		Rem Wait .1 : DialogFunc = True ' Continue getting idle actions
	Case 6 ' Function key
	End Select
End Function

If you run this code, you will see the following dialog:

All we have done so far is to place the controls, but we have not yet specified any behavior for the dialog. To do that, we will edit the DialogFunc to change how the dialog looks based on certain events.

The DialogFunc

As mentioned above, a skeleton version of the DialogFunc was created when we first set up the dialog GUI. If you look at the code, you will see that DialogFunc is simply a Select Case block of code. Each Case statement corresponds to a specific action taken in the dialog GUI. The skeleton code includes comments for each Case statement, so let's look at them one at a time...

Case 1: Dialog Initialization

Any code added in Case 1 is executed when the dialog is first created. Since the default selection in the drop-down box is "1", let's look at how we can turn off the controls for selections "2", "3", and "4".

There are VB keywords to handle specific actions. In our case, we'll start by examining enable/disable and visibility on/off for these controls.

Enable/Disable

The DlgEnable statement handles this action. So, let's add the following statement to Case 1 of the DialogFunc:

DlgEnable "Text2", False

Run the script and look at the text label for item 2. It should be disabled, as shown below:

The DlgEnable statement was set to operate on the Text control whose Field parameter is "Text2", and the enable status is set to False, or not enabled.

Note that this, and all other commands like it, are only valid within a dialog function. If you try this in the main code block, an error will be generated. In short, dynamic dialog functions must exist in a DialogFunc. Also, note that there is regular VB help available for all the dynamic dialog commands. Search for "dlg" and the commands should appear for help information.

Visible/Invisible

In the same manner, we can set whether a control is visible or invisible. Add the following line of code, also in the Case 1 code block:

DlgVisible "TextBox2", False

When you run this modification, you will see that the text box that corresponds to the disabled label is now invisible (not just disabled):

Text Labels

Finally, lets look at how we can change the string displayed by a text label. In the upper right corner of the dialog, up to now you have seen "MsgOut", which is the default label specified in the dialog definition (Text 280,14,210,14,"MsgOut",.msgOut). Again, there is a specific command to perform this task dynamically. Add the following line of code, again in the Case 1 code block:

DlgText "MsgOut", "The [default] selection is 1"

Run the code and you will see that the message in the upper right now matches the default selection of "1":

Wrapping Up Initialization

With these three commands, we already have a powerful set of tools with which to work. Since the default selection is "1", let's complete the initialization code by turning off all the controls for boxes 2, 3, and 4. Just for argument's sake, let's make the labels invisible and the text boxes disabled. The DialogFunc should now look like this:

Rem See DialogFunc help topic for more information.
Private Function DialogFunc(DlgItem$, Action%, SuppValue&) As Boolean
	Select Case Action%
	Case 1 ' Dialog box initialization
		DlgVisible "Text2", False
		DlgVisible "Text3", False
		DlgVisible "Text4", False
		DlgEnable "TextBox2", False
		DlgEnable "TextBox3", False
		DlgEnable "TextBox4", False
		DlgText "MsgOut", "The [default] selection is 1"
	Case 2 ' Value changing or button pressed
		Rem DialogFunc = True ' Prevent button press from closing the dialog box
	Case 3 ' TextBox or ComboBox text changed
	Case 4 ' Focus changed
	Case 5 ' Idle
		Rem Wait .1 : DialogFunc = True ' Continue getting idle actions
	Case 6 ' Function key
	End Select
End Function

...and the dialog should appear like this:

Case 2: Value Changing or Button Pressed

This section of code handles events that are caused by pushing a command button or changing the value of a control, such as a drop-down list, selecting a different radio button option, changing the state of a checkbox, etc. In general, this section handles all controls that return a value, as opposed to a string. We will use this section to update the dialog when the user selects a different value in the drop-down box.

Since a dialog can have many controls, and there is only one section to handle events from these controls, there must be a mechanism to let the DialogFunc know which control is sending the event. This is handled in the arguments defined with DialogFunc itself (DlgItem$, Action%, SuppValue&). This will be explained in detail below.

Updating the Dialog GUI

Let's start with the case where the user changes the drop-down selection to "2". The desired behavior is to activate the labels and text boxes for the first two items, leaving the third and fourth items disabled and invisible. Also, we'll want to update the "MsgOut" tect control. From a coding standpoint, we want to react to a change in drop-down state, and we need to know which control changed and what its new value is. Fortunately, this framework is automatically built into DialogFunc. Here's how it works:

When we run the code and change the selection in the drop-down box (say from "1" to "2"), DialogFunc is automatically called. Let's look at the definition for DialogFunc:

Private Function DialogFunc(DlgItem$, Action%, SuppValue&) As Boolean

Once we understand how the arguments work, we can then understand how to process the events generated by the dialog.

  • DlgItem$ is the name of the control that has changed. In our case, DlgItem$ = "num", since "num" is the name of the drop-down control.
  • Action% is the argument used for the main Select Case block in DialogFunc. So, Action% = 1 for initialization, Action% = 2 for our current area of interest, and so on.
  • SuppValue& is the value, where appropriate, of the control. If we were concerned about a command button, then SuppValue& is irrelevant because all we can do with a command button is to push it. But, in our case with a drop-down control, we need to know which value was selected. This information is passed to DialogFunc through SuppValue&.

With this information, we can now write the code to process a change in the drop-down control's value. Let's start very simple: we will add code to simply tell us what drop-down selection was made, so we can verify program flow and operation. Add the following block of code to DialogFunc, but this time put it in the Case 2 section:

Select Case DlgItem$
	Case "num"
		If SuppValue& = 0 Then
			MsgBox("Item 1",vbOkOnly)
		ElseIf SuppValue& = 1 Then
			MsgBox("Item 2",vbOkOnly)
		ElseIf SuppValue& = 2 Then
			MsgBox("Item 3",vbOkOnly)
		ElseIf SuppValue& = 3 Then
			MsgBox("Item 4",vbOkOnly)
		End If
End Select

All we are doing here is making sure we can read the appropriate values properly. We do this by testing SuppValue& in a Select Case block. So, a VB message box is displayed, indicating what the selection was in the drop-down control. Note that the drop-down returns a value based on 0, so the drop-down reports states 0/1/2/3, corresponding to a choice in the list of 1/2/3/4.

Since we are getting proper reports, we can now make the real code changes needed for our dialog. The following code block shows the correct settings for the complete Case 2 section in DialogFunc:

Case 2 ' Value changing or button pressed
	Rem DialogFunc = True ' Prevent button press from closing the dialog box
	Select Case DlgItem$
		Case "num"
			If SuppValue& = 0 Then
				DlgVisible "Text2", False
				DlgVisible "Text3", False
				DlgVisible "Text4", False
				DlgEnable "TextBox2", False
				DlgEnable "TextBox3", False
				DlgEnable "TextBox4", False
				DlgText "MsgOut", "The selection is 1"
			ElseIf SuppValue& = 1 Then
				DlgVisible "Text2", True
				DlgVisible "Text3", False
				DlgVisible "Text4", False
				DlgEnable "TextBox2", True
				DlgEnable "TextBox3", False
				DlgEnable "TextBox4", False
				DlgText "MsgOut", "The selection is 2"
			ElseIf SuppValue& = 2 Then
				DlgVisible "Text2", True
				DlgVisible "Text3", True
				DlgVisible "Text4", False
				DlgEnable "TextBox2", True
				DlgEnable "TextBox3", True
				DlgEnable "TextBox4", False
				DlgText "MsgOut", "The selection is 3"
			ElseIf SuppValue& = 3 Then
				DlgVisible "Text2", True
				DlgVisible "Text3", True
				DlgVisible "Text4", True
				DlgEnable "TextBox2", True
				DlgEnable "TextBox3", True
				DlgEnable "TextBox4", True
				DlgText "MsgOut", "The selection is 4"
			End If
	End Select

All we are doing here is checking the value of SuppValue&, which contains the selection in the drop-down control, and setting the various dialog properties to match our choice. One interesting note here... The initialization code set the MsgOut text to "The [default] selection is 1". If we choose "1" in the drop-down, however, the MsgOut tect is "The selection is 1". This is correct. The only time DialogFunc executes Case 1 is when it is initialized, so we only see "[default]" in the message before it is changed for the first time.

These code changes should produce the following 4 states (betond the initialization state, which we have already discussed) if your dialog is operating properly:




Case 3: TextBox or ComboBox Changing

This section handles changes in a control that returns a string, rather than a value. In practice, you will likely find limited use for this section because the values in TextBox and ComboBox controls are available after the dialog is closed under normal circumstances. The only time you would want to use this section in DialogFunc is if you want to respond to a change in a TextBox or a ComboBox and update the dialog while it is still displayed.

To illustrate this functionality, let's say the following sequence of events occurs:

  • The dialog is initialized and displayed
  • The user selects "2" in the drop-down box
  • The user puts the cursor in TextBox1 and enters a value
  • The user either tabs down to TextBox2 or simply clicks on TextBox2

As soon as one of these last actions occurs, the dialog will detect that TextBox1 has changed. To see this, add the following code to Case 3 of DialogFunc:

Select Case DlgItem$
	Case "TextBox1"
		MsgBox("TextBox1 has changed!  " & SuppValue& & "  characters", vbOkOnly)
	Case "TextBox2"
		MsgBox("TextBox2 has changed!  " & SuppValue& & "  characters", vbOkOnly)
End Select

When you run this, select either "1" or "2" for the drop-down control, then enter an alphanumeric string in TextBox1. If, after making these changes, you shift the focus away from TextBox1 (but don't click the OK button yet), you will get a VB message box telling you which TextBox has changed and the number of characters in the new string. Note that the number of characters is reported by SuppValue& in this case.

Since a ComboBox also lets the user enter text, it works in exactly the same way. It triggers the event when it loses focus after a text change, and the number of characters in the new string is reported by SuppValue&.

Case 4: Focus Changing

In any Windows application, "focus" is owned by the active control at any given time. In our case, the active control when the dialog is first opened is the "pick a number" listbox, so it has focus to start with. There may be times when we want to keep track of which control has focus and, perhaps as importantly, which control has lost focus. An example would be to validate the contents of a data box before proceeding. This capability is provided in DialogFunc by Case 4 of the Select Case block.

This portion of DialogFunc uses both DlgItem$ (contains the ID of the control gaining focus) and SuppValue& (contains the ID of the control losing focus). To capture both events, our code will use two nested Select Case blocks:

	Case 4 ' Focus changed
		Select Case DlgItem$
			Case "TextBox1"
				Select Case SuppValue&
					Case 0
					Case 1
						MsgBox("TextBox1 has focus.  " & vbTab & "DropListBox.num lost focus.", vbOkOnly)
					Case 2
					Case 3
					Case 4
					Case 5
					Case 6
					Case 7
					Case 8
				End Select
			Case "TextBox2"
				Select Case SuppValue&
					Case 0
					Case 1
						MsgBox("TextBox2 has focus.  " & vbTab & "DropListBox.num lost focus.", vbOkOnly)
					Case 2
					Case 3
					Case 4
					Case 5
					Case 6
					Case 7
						MsgBox("TextBox2 has focus.  " & vbTab & "TextBox1 lost focus.", vbOkOnly)
					Case 8
				End Select
		End Select

The outer Select Case loop (with DlgItem$ argument) determines which control is getting focus, while the inner loop (with SuppValue& argument) determines which control is losing focus. The few possibilities that are covered in the example include:

  • TextBox1 gains focus from the listbox
  • TextBox2 gains focus from the listbox
  • TextBox2 gains focus from TextBox1

Note that there are two ways shown to reference individual controls. One way is through the control's ID parameter, shown in the outer loop. The other way is through the control's "Z-order", shown in the inner loop. The best way to explain Z-order is that it's an integer that defines the order of controls gaining focus when the TAB key is used to move between them. In more full-featured editors, Z-order can be set manually for each control. In our editor, I believe Z-order is defined only by the order that the controls are defined in the Begin Dialog block in Sub Main.

When this code is executed and one of the above actions takes place, a MessageBox is displayed indicating which controls gained and lost focus. An example MessageBox is shown:

Complete Code Listing

Option Explicit
Dim i As Integer
Dim nums(4) As String

' Code Module
Sub Main
	For i = 1 To 4
		nums(i) = CStr(i)
	Next i

	Begin Dialog UserDialog 510,231,"My Dialog",.DialogFunc ' %GRID:10,7,1,1
		Text 20,14,170,14,"Pick a number from 1 to 4:",.Text5
		DropListBox 90,35,90,70,nums(),.num
		Text 280,14,210,14,"MsgOut",.msgOut
		Text 290,70,50,14,"Box 1",.Text1
		Text 290,98,50,14,"Box 2",.Text2
		Text 290,126,50,14,"Box 3",.Text3
		Text 290,154,50,14,"Box 4",.Text4
		TextBox 350,70,90,21,.TextBox1
		TextBox 350,98,90,21,.TextBox2
		TextBox 350,126,90,21,.TextBox3
		TextBox 350,154,90,21,.TextBox4
		OKButton 200,203,90,21
	End Dialog
	Dim dlg As UserDialog
	Dialog dlg
End Sub

Rem See DialogFunc help topic for more information.
Private Function DialogFunc(DlgItem$, Action%, SuppValue&) As Boolean
	Select Case Action%
	Case 1 ' Dialog box initialization
		DlgVisible "Text2", False
		DlgVisible "Text3", False
		DlgVisible "Text4", False
		DlgEnable "TextBox2", False
		DlgEnable "TextBox3", False
		DlgEnable "TextBox4", False
		DlgText "MsgOut", "The [default] selection is 1"
	Case 2 ' Value changing or button pressed
		Rem DialogFunc = True ' Prevent button press from closing the dialog box
		Select Case DlgItem$
			Case "num"
				If SuppValue& = 0 Then
					DlgVisible "Text2", False
					DlgVisible "Text3", False
					DlgVisible "Text4", False
					DlgEnable "TextBox2", False
					DlgEnable "TextBox3", False
					DlgEnable "TextBox4", False
					DlgText "MsgOut", "The selection is 1"
				ElseIf SuppValue& = 1 Then
					DlgVisible "Text2", True
					DlgVisible "Text3", False
					DlgVisible "Text4", False
					DlgEnable "TextBox2", True
					DlgEnable "TextBox3", False
					DlgEnable "TextBox4", False
					DlgText "MsgOut", "The selection is 2"
				ElseIf SuppValue& = 2 Then
					DlgVisible "Text2", True
					DlgVisible "Text3", True
					DlgVisible "Text4", False
					DlgEnable "TextBox2", True
					DlgEnable "TextBox3", True
					DlgEnable "TextBox4", False
					DlgText "MsgOut", "The selection is 3"
				ElseIf SuppValue& = 3 Then
					DlgVisible "Text2", True
					DlgVisible "Text3", True
					DlgVisible "Text4", True
					DlgEnable "TextBox2", True
					DlgEnable "TextBox3", True
					DlgEnable "TextBox4", True
					DlgText "MsgOut", "The selection is 4"
				End If
		End Select
	Case 3 ' TextBox or ComboBox text changed
		Select Case DlgItem$
			Case "TextBox1"
				MsgBox("TextBox1 has changed!  " & SuppValue& & "  characters", vbOkOnly)
			Case "TextBox2"
				MsgBox("TextBox2 has changed!  " & SuppValue& & "  characters", vbOkOnly)
		End Select
	Case 4 ' Focus changed
		Select Case DlgItem$
			Case "TextBox1"
				Select Case SuppValue&
					Case 0
					Case 1
						MsgBox("TextBox1 has focus.  " & vbTab & "DropListBox.num lost focus.", vbOkOnly)
					Case 2
					Case 3
					Case 4
					Case 5
					Case 6
					Case 7
					Case 8
				End Select
			Case "TextBox2"
				Select Case SuppValue&
					Case 0
					Case 1
						MsgBox("TextBox2 has focus.  " & vbTab & "DropListBox.num lost focus.", vbOkOnly)
					Case 2
					Case 3
					Case 4
					Case 5
					Case 6
					Case 7
						MsgBox("TextBox2 has focus.  " & vbTab & "TextBox1 lost focus.", vbOkOnly)
					Case 8
				End Select
		End Select
	Case 5 ' Idle
		Rem Wait .1 : DialogFunc = True ' Continue getting idle actions
	Case 6 ' Function key
	End Select
End Function
  • No labels