At times, you’ll want to change the menus dynamically. The MenuMod application. which is based on the MenuBMP application, shows how this can be done.
VB6 at Work: The MenuMod Project
The MenuMod application lets you switch the menu items from bitmaps to text and back. The application has the same menu as the menuBMP application. plus third item, whose caption is Display Graphics (when the menu displays text) or Display Text (when the menu displays graphics). The Click event handler for this me u item calls the DisplayTextMenu() or DisplayBitmapMenu() function to switch the menu between text and graphics. In the Modifymenu() procedure, you use the MP_STRING flag to change the menu item to text, and you use the MF_BlTMAP flag to change the menu item to graphics.
Detecting Mouse Movements
A common event in programming with Visual Basic is the MouseMove event, which lets you monitor the movement of the mouse. The MouseMove event reports the coordinates of the mouse as it moves on a Form. In some situations, we want to monitor the coordinates of the point from within the Click or DblClick events (which don’t report where the m use was clicked) or even monitor the movement of the mouse outside a Form. To find out the location of the mouse at any time, use the GetCursorPos() function, which has the following syntax:
Priva oeclare Function GetCursorPos Lib ·user32· _
(lpPoint As POINTAPI) As Long
VB6 at Work: The MouseMov Project
The Mouselvlov application (shown in Figure 1’3.5) uses an endless loop to track the mouse, but also releases control to Windows with the DoEvents statement. ‘This allows Windows to process other events such as ending the program when the user clicks the Stop button. Without the DoEvents statement, we would not be able to stop the program.
The GetCursorPos() function returns a PointAPI variable, which you can pass to the WindowFromPoint() function_to get the handle of the window under the pointer. Once you’ve obtained the window handle, you can find out the class name of the window with the GetclassName() function, which returns the name of the window’s class. If the name is.SysListView32, the mouse is over the Wmdows Desktop. Otherwise, the mouse is over some other window.
The main portion of the code is the tracking loop. The code gets the current mouse position and uses this information to get the window handle and the class name. The class name is then displayed and updated each time the mouse moves over a new window.
The MouseMov Application
Code 13.7:
A related project is the previously discussed MousePos project, which you’ll find in this chapter’s folder on the CD.
Using the HitTest Method
Many controls have a HitTest method whose function is to retrieve an object at a specific pair of coordinates. We used this method in the LVWDemo project in Chapter 8, but now it’s time to look at this method and see how it’s used with the GetCursorPos() function.
The HitTest method applies only to a few controls that can act as containers for multiple objects. The ListView control, for example, can host many ListItem objects. When the user double-clicks the ListView control, the DblClick event is triggered, but this event doesn’t report where the mouse was clicked. To find out which item was double-clicked, use the GetCursorPos() function to retrieve the pointer’s coordinates and pass them to the control’s HitTest method, which will return a reference to the object at this location.
Let’s return to the LVWDemo project and look at the ListView control’s Dbl- Click event handler. The ListView control is populated with company names and related data. When these items are displayed as icons, as shown it\ Figure 13.6, we should be able to detect the item that was clicked or double-clicked. The single click is reported by the Item Click event, which reports the clicked item. The declaration of the ltemClick event handler is:
Private Sub listView1_ItemClick(ByVal Item As Comctllib.listItem)
However, there’s no ltemDblClick event and the declaration of the DblClick event is simply:
Private Sub listView1_OblClick()
Even though the DbIClick event doesn’t report the double-clicked item, it’s quite common in programming the ListView control to provide cope that reacts to the double-clicking of an item. Applications can indirectly detect the double-clicked item with the help of the GetCursorPos() function and the HitTest method. To detect the coordinates of the point where the mouse was double-clicked, follow the steps presented next.
First, declare the GetCursorPos() function and the POINTAPI data structure in a Module as:
Type POINTAPI
x As Long
y As LQng
End ·Type
Declare Functidh GetCursorPos Lib luser32″ Alias “GetCursorPos”_
(lpPoint As POINTAPI) As Long
In the DblClick event handler, call the GetCursorPos() function to find out the coordinates of the pointer where the button was double-clicked:
GetCursorPos dPoint
The members of the dPoint variable are the X and Y coordinates of the mouse pointer in pixels. They are screen coordinates. In other words, if you call the Get Cursorpos() event-from within the ListView control’s DblClick event, it will report a pair of coordinates. If you move the Form to another location on screen and then double-click the same icon, the coordinates will be drastically different. Therefore, we must map the screen coordinates to the control coordinates. To do so, we must subtract the Left and Top properties of the control and the Form from the values of the coordinates.returned by the GetCursorPos() function. We need two statements like the following ones:
X= dPoint.X – Me. Left – ListViewl.Left
Y = dPoint.Y – Me. Top – ListViewl.Top
These statements are not quite right because the coordinates dPoint.X and dPoint.Y are expressed in pixels and the other coordinates are expressed in twips. Before subtracting them, we must convert them to pixels with the ScaleX and ScaleYmethods. Here are the statements that map the screen coordinates to the internal coordinates of the ListView control:
X = dPoint.X – ScaleX(Me.Left + ListViewl. Left, vbTwips, vbPixels)
Y = dPoJnt.Y – ScaleY(Me.Top + ListViewl:Top,’vbTwips, vbPixels)
X and Y are the coordinates of the pointer in pixels the moment it was doubleclicked. The coordinates (0,0) correspond to the control’s upper-left comer. The variables X and Y must be passed to the HitTest method, which will return a reference to the double-clicked item. However, the HitTest method requires that the coordinates be specified in twips. The HitTest method is called as follows:)
Set LItem = ListViewl.HitTest(ScaleX(X, vbPixels, vbTwips),
ScaleY(Y, vbPixels, vbTwips))
The variables X and Y are first converted to twips, then passed to the HitTest method. The method returns a reference to the double-clicked item, which is stored in the ListItem variable. The ListItem variable must be declared as ListItem:
Dim ListItem As ListItem
Using the ListItem variable, you can access the properties of the double-clicked item. For example, you can highlight it from within your code with the following statement:
LItem.Selected = True
or read its subitems through the collection:
LItem.ListSubItems
If the mouse double-clicked an empty area of the ListView control rather than an item, the LItem variable will be Nothing. If you attempt to access its properties, a runtime error will be generated. To avoid the runtime error, use the On Error Resume Next statement and examine the value of the LItem variable.
Here’s the complete listing of the ListView control’s DblClick event handler. If you open the LVWDemo project, you’ll see that it’s adequately documented. You can reuse this code as is (without the statements that process the LItem variable’s members, of course) in your code to react to the double-click event on a ListView control.
The ListView Control’s DblClick Event Handler
Code 13.8:
This is not the only use of the HitTest method or even the most common one. However, it’s not a trivial one and that’s why I presented it in detail. The HitTest method was designed to be used primarily with the DragDrop event. The DragDrop event reports the coordinates of the point where an object is dropped. If the destination of the drag-and-drop operation is a ListView or TreeView control, we usually want to know the item on which the source object was dropped. The declaration of the Dragfrrop event is:
Private Sub TreeView1_DragDrop(Source As Control, x As Single, y As Single)
Since the coordinates of the point where the object was dropped are known, you can pass them to the HitTest method to find out on which item the source object was dropped. Notice that you need not convert or adjust the coordinates, since the DragDrop event reports them in the control’s internal coordinate system in twips.
Keeping a Window on Top
You’ve often seen applications that keep a window on top regardless of whether the window is active. Microsoft Word does this with its Find window. This is done with a single call to the SetWindowPos() API function, which is declared as follows:
The hWnd argument is the handle of the window, x and y are the coordinates of the window’s upper-left comer, and ex and cy are the window’s width and height. The hWndlnsertAfter argument is the handle of a window, after which the hWnd window is in the window list. It can also be one of the values shown in Table 13.5.
Flags argument is an integer that contain one or more of the flags shown in Table 13.6.
The sample application that demonstrates the SetWindowPos() function is called WinTop, and its, listing is shown next.
The WinTop Application
Code 13.9:
..
You can use this technique to create the Search & Replace window of the RTFPad project (see Chapter 9, More Advanced ActiveX Controls). This window remains on top and visible even when the user switches to the editor’s Form (see Figure 13.7). All you’ve to do in order to always keep the Search & Replace window on top is to insert the declaration of the SetWindowPos() function and a ‘couple of constants in a Module:
Also, change the code cf the Find menu command’s event handler, The line:
SearchForm.Show
SearchForm.Show
must be replaced by the following statement:
The values 470 and 155,which are hardcoded into the listing, are the dimensions of the Search & Replace window. The BorderStyle property of the Search- Form user also be set to Fixed, so that users can’t resize it. .