All Products | Support | Search | microsoft.com Guide Visual Basic Home | Events & Training | Product Support | MSDN Online | Developer Products | Product Information Technical Information Technical FAQ Technical Articles Online Community Tutorials Tips and Tricks Product Documentation Product Support Samples & Downloads News & Reviews Registration Past Tips of the Week The Visual Basic Tip of the Week is provided by ZD Journals, publishers of Inside Visual Basic, a monthly publication for Visual Basic users. Sign up for your free preview Issue today. Find out how to receive the Tip of the Week in e-mail! You can quickly navigate to past tips using the alphabetical topic list below. The tips are also organized chronologically further down on this page. A better way to gather multi-selected Visual Basic ListBox items A caveat for the FileSystemObject Delete methods in Visual Basic A new Format function Accept only numbers in a text field Add a new line to existing textbox text Add controls to a Visual Basic control array at run-time Add Dithered Backgrounds to your Visual Basic Forms Add pop-up menu support to the Rich Textbox control Add Web mouseover effects to Visual Basic Avoid API bit-masking comparison errors in Visual Basic Avoid cascading UserControl Change() events Avoid variable type comparison glitches in textboxes Confirm Screen Resolution Convert NULL Values to Empty Strings to Avoid Errors Compact and repair Jet databases with DAO Compact long Visual Basic filepaths with SHLWAPI library Correctly pass variant array parameters in Visual Basic Create templates from existing forms Creating a incrementing number box Creating a new context menu in editable controls Creating Professional Documents with Code Creating Win32 region windows Display animated GIFs in Visual Basic Display UserControl property settings in the Property Window Don't lose focus in UserControls Drag & Drop the real selected Visual Basic treeview node Dragging items from a list to another one Enabling the horizontal scrollbar in a RichTextbox control Enhancing Browser Page Load Performance Evaluate string formulas in Visual Basic Finding the last day of the month Force Visual Basic 6.0 to open maximized code windows Generate temporary Visual Basic files with API GETting the contents of a file Globally replace text using just 5 lines of Visual Basic code Hide the mouse cursor in a Visual Basic application How to build a data-shaped SQL clause for Visual Basic Implementing a "wait" function in Visual Basic Importing Registry settings Increase Your RAD-ness by Creating Your Own Visual Basic 5.0 Templates Increment and decrement dates with the [+] and [-] keys Keep Your Background Processes Running Keeping track of Visual Basic source code builds Labeling your forms Let DateDiff() determine if two dates are in the same month Let Visual Basic determine if the CD Rom drive contains media Link Lotus Notes to Visual Basic 6.0 Maintain a Visual Basic DBGrid's runtime column widths Manipulate your controls from the keyboard Measuring a text extent Move files to the Recycle Bin with Visual Basic and Windows API Nothing to declare? Opening a browser to your homepage Prevent drag and drop operations on Visual Basic treeview root nodes Parsing Using SPLIT Function Put your Visual Basic application into deep sleep Quick Custom Dialogs for DBGrid Cells Quick Text Select On GotFocus Read Registry values in Visual Basic without API Referring to a DE Command's recordset in Visual Basic 6.0 Replace lengthy path strings with Visual Basic object variables Retrieve a file's short name in Visual Basic without API Retrieve subitem values from a Visual Basic ListView control Scroll to a Visual Basic listview control's selected item Showing long ListBox entries as a ToolTip Simple file checking from anywhere Spell check RTF documents in Visual Basic Switching to Design View after Modifying ASP Script in Visual InterDev Take Advantage of Built-in OLE Drag and Drop Uncover internal DLL functions with Dependency Walker Update the Visual Basic 6.0 DataGrid control with recordset changes Use ADO's native OLEDB drivers instead of ODBC Use FileDSNs to ease ODBC Installs Use FreeFile to Prevent File Open Conflicts Use ParamArray to Accept an Arbitrary Number of Parameters Use Visual Basic's ADOX to determine if internal database objects exist Use the Timer control for longer than 1 minute Use the Visual Basic 6.0 Split function to count substrings Using Response.IsClientConnected Property to Determine Browser Connection Using Server.HTMLEncode() Function Using the Alias Option to Prevent API Crashes Visible Cues from a Minimized Form Writing to the Windows NT event log For the week of September 11th How to build a data-shaped SQL clause for Visual Basic Since it's introduction in ADO 2.0, data shaping has remained largely on the fringes of Visual Basic arcanum. Relegated to the back pages of musty manuals, you may have overlooked this useful aspect of ADO. If you're not familiar with datashaping, in essence, it lets you create recordsets within recordsets--or parent/child relationships--all with a single ADO object. This means no messy joins, no complicated filtering, and no need for spaghetti-code in presentation logic. Data shaping reduces the amount of traffic crossing a network, provides more flexibility when using aggregate functions, and reduces overhead when interfacing with leading-edge tools like XML. To create a shaped recordset, you use a standard SQL statement, along with three major keywords: SHAPE, APPEND, and RELATE, like so SHAPE {SELECT EmployeeID,FirstName, LastName FROM Employees} APPEND ({SELECT OrderID, shipname, EmployeeID FROM orders} AS SomeAlias RELATE EmployeeID TO EmployeeID); The SHAPE keyword specifies and defines the parent recordset. Once you create a parent object, you next APPEND the child recordset. This clause uses a similar syntax as SHAPE and contains the necessary SQL statement to create the child recordset within the parent. In addition to the child recordset's SQL statement, you must also indicate how you want the two recordsets to RELATE. For the week of August 28th Add pop-up menu support to the Rich Textbox control While Visual Basic provides an easy way to create shortcut menus, some controls such as rich text boxes don't inherently support pop-up menus. To overcome this obstacle, you can take advantage of the MouseDown() event. Visual Basic triggers this event when you press the mouse button. To provide a pop-up menu, create a shortcut menu, then simply call it in the control's MouseDown() event. To see how this works, add a Rich Textbox control to a sample form. Next, select Tools | Menu Editor from Visual Basic's IDE menu bar, and create a simple menu with one or two items. Then, switch to the form's code window, and enter Private Sub RichTextBox1_MouseDown(Button As Integer, _ Shift As Integer, X As Single, Y As Single) If Button = vbRightButton Then Me.PopupMenu Menu End IF End Sub For the week of August 21st Add Web mouseover effects to Visual Basic One common effect found in sites all across the Web is what's termed the mouseover effect. This effect usually provides some form of limited animation in a Web page element. For instance, when you move the mouse over a button, the button changes color, giving it a glowing appearance. Given an understanding of Visual Basic's mouse events, you can easily supply these same effects in your own applications. To imitate the same graphical effect in a Visual Basic project, use the MouseMove() event. As its name implies, this event lets you track the mouse's movement over controls, forms, and MDIForms. For forms, this event follows the syntax: Private Sub Form_MouseMove(button As Integer, _ shift As Integer, x As Single, y As Single) Where button stores the current state of the mouse buttons; shift indicates the state of [Shift], [Ctrl], and [Alt] keys; and x and y the current mouse coordinates. To illustrate, launch a new Visual Basic project and place a command button on the default form. Next, set the control's background color to yellow (&H0000FFFF&) and make the font 8 point. Finally, set the Style property to 1-Graphical so Visual Basic will display the color. Now add the following code Private Sub Command1_MouseMove(Button As Integer, _ Shift As Integer, X As Single, Y As Single) With Command1 .BackColor = vbGreen .FontSize = 12 End With End Sub Now, when you run the project, the button turns green and the font size increases when you move the mouse pointer over it. To turn the button back to its original state when you move the mouse pointer elsewhere, add the following event: Private Sub Form_MouseMove(Button As Integer, Shift As _ Integer, X As Single, Y As Single) With Command1 .BackColor = vbYellow .FontSize = 8 End With End Sub For the week of August 14th Move files to the Recycle Bin with Visual Basic and Windows API Want to delete a file and have it go into the recycle bin? The Kill statement permanently deletes the file. Instead, try the following: Private Declare Function SHFileOperation Lib _ "shell32.dll" (ByRef lpFileOp As _ SHFILEOPSTRUCT) As Long Private Const ERROR_SUCCESS = 0& Private Const FO_COPY = &H2 Private Const FO_DELETE = &H3 Private Const FO_MOVE = &H1 Private Const FO_RENAME = &H4 Private Const FOF_ALLOWUNDO = &H40 Private Const FOF_CONFIRMMOUSE = &H2 Private Const FOF_FILESONLY = &H80 Private Const FOF_MULTIDESTFILES = &H1 Private Const FOF_NOCONFIRMATION = &H10 Private Const FOF_NOCONFIRMMKDIR = &H200 Private Const FOF_RENAMEONCOLLISION = &H8 Private Const FOF_SILENT = &H4 Private Const FOF_SIMPLEPROGRESS = &H100 Private Const FOF_WANTMAPPINGHANDLE = &H20 Private Type SHFILEOPSTRUCT hwnd As Long wFunc As Long pFrom As String pTo As String fFlags As Integer fAnyOperationsAborted As Long hNameMappings As Long lpszProgressTitle As String ' FOF_SIMPLEPROGRESS End Type only used if Next create a function called Recycle, like so Public Sub Recycle(ByVal FileName As String) Dim CFileStruct As SHFILEOPSTRUCT With CFileStruct .hwnd = Me.hwnd .fFlags = FOF_ALLOWUNDO .pFrom = FileName .wFunc = FO_DELETE End With If SHFileOperation(CFileStruct) <> ERROR_SUCCESS Then 'An error occurred. End If End Sub To test the procedure, create a dummy text file, drop a command button onto a Visual Basic form, and then attach the following code Private Sub Command1_Click() Recycle "c:\test.txt" End Sub When you click the button, Windows asks if you want to move the file to the Recycle Bin. For the week of August 7th Avoid variable type comparison glitches in textboxes When you retrieve a numeric value from a textbox, you may want to compare it to some other preset value. For example, consider the following code: Select Case Text1.Text Case 1 to 12 MsgBox "Acceptable Value" Case Else MsgBox "Unacceptable Value" End Select You might think that this code would compare a numeric value in Text1 and return Acceptable Value for values 1 through 12, and Unacceptable Value for all other numbers. Instead, this code snippet displays the Acceptable Value message for only 1, 10, 11, and 12, but not 2, 3, 4, etc. That's because a textbox's Text property returns the value as a string, and so compares them as such in the Select Case statement. To avoid this unexpected glitch, convert the numbers with the Val() function. This function automatically converts a string into the appropriate type: integer, long, single, or double. The modified code would look as follows: Select Case Val(Text1.Text) Case 1 to 12 MsgBox "Acceptable Value" Case Else MsgBox "Unacceptable Value" End Select For the week of July 31st Put your Visual Basic application into deep sleep There may be occasions when you want an application to wait for a specified period of time without performing activity or wait loops. The Timer control can help, but only up to 60 seconds. The Sleep Windows API function lets you put the application to sleep. Enter the following declaration into a standard module. Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Then, simply call the function from your code, as in: Sleep 5000 In this case, the 5000 milliseconds puts the application to bed for 5 seconds. You can specify your own period of time, though. For the week of July 24th Compact and repair Jet databases with DAO Access's databases are typically reliable, but occasionally they can get damaged. There are numerous reasons why, such as powering down the computer without first closing the database file or the system simply crashing. Another source of problems for databases is excessive fragmentation-when records are deleted from a database file, the file becomes fragmented. Whatever the reason, once the database is damaged, end-users are often left stranded until technical support finds the time to help them. Fortunately, you can take measures to help users quickly recover from such problems, by providing built-in utilities that perform routine database maintenance. The DAO library provides two methods to perform the necessary maintenance that keeps Access databases running smoothly--RepairDatabase and CompactDatabase. The RepairDatabase method uses the syntax RepairDatabase dbname where dbname is a string representing the full pathname of the Access database (.MDB) that you need to repair. This CompactDatabase method uses the following syntax: CompactDatabase olddb, newdb, _ locale, options, password You specify the full path and filename of your source database as a string in the method's olddb property. Likewise, you specify the destination of the compacted database with the newdb property. So, to use these statements, you could add code similar to: RepairDatabase "C:\myData1.mdb" CompactDatabase "C:\myData1.mdb", App.Path & "\myDataNewName.mdb" If you use DA0 3.6, then you can remove the RepairDatabase statement. DAO 3.0 incorporates this functionality into the CompactDatabase method-you can no longer compact and repair databases as separate actions. For the week of July 17th Link Lotus Notes to Visual Basic 6.0 With the introduction of Lotus Notes and Domino Release 5.0.2b, you now have the ability to manipulate the Domino object model via COM. As a result, you can use VB 6.0 to take advantage of Lotus/Domino services and databases. To do so, however, you'll need Lotus Notes client version 5.02.b (or higher), Domino Designer client, or Domino Server. As with most Visual Basic object libraries, the programs need not be running to use them. Lotus has plans to make this runtime package distributable independent of its full software installation. Next, set a reference to the Notes back-end DLL, Lotus Domino Objects. This object model conforms to a hierarchy similar to CDONTS, as seen in the sample code, which displays a message box with the first name in a Lotus Notes' address book. Notice that before you can access any of the objects within the NotesSession, you must initialize a session first. Dim domSession As New Domino.NotesSession Dim domDatabase As New Domino.NotesDatabase Dim domDocument As NotesDocument Dim domViewEntry As NotesViewEntry Dim domView As NotesView Dim domViewNav As NotesViewNavigator Dim strName As String domSession.Initialize Set domDatabase = domSession.GetDatabase("", _ "names.nsf") Set domView = domDatabase.GetView("Contacts") ' This view contains the list of names Set domViewNav = domView.CreateViewNav Set domViewEntry = domViewNav.GetFirstDocument() Set domDocument = domViewEntry.Document strName = domDocument.GetItemValue("FullName")(0) MsgBox strName Set domViewEntry = Nothing Set domViewNav = Nothing Set domView = Nothing Set domDocument = Nothing Set domDatabase = Nothing Set domSession = Nothing For the week of July 10th Avoid cascading UserControl Change() events When you expand one of Visual Basic's built in controls, avoid building in a cascading Change event. This occurs when internal UserControl code changes a constituent control's value. Doing so triggers the constituent control's Change() event. Most likely, you'll have also exposed this event to the developer. If the developer has placed code in the UserControl's Change() event, then this will be triggered as well. For instance, suppose you create an ActiveX Control project and add a standard textbox as a constituent control. Next, you add the basic code: Private mstrText As String Public Event Change() Private Sub Text1_Change() MsgBox "Text1 Change" RaiseEvent Change End Sub Public Property Get Text() As String Text = mstrText End Property Public Property Let Text(Val As String) mstrText = Val End Property Private Sub UserControl_ExitFocus() Text1.Text = "Hello, " & Text1.Text End Sub Under this scenario, the UserControl will modify the textbox's text whenever it loses the focus. However, suppose a developer places the UserControl on a standard form and adds the following code: Dim X as Integer Private Sub UserControl11_Change() X = X + 1 MsgBox X End Sub Now, every time the UserControl loses focus, Visual Basic increments X by one-probably not what the developer had in mind. To avoid the unintentional consequences cascading Change events can cause, consider adding an internal public Boolean variable to the UserControl that tracks internal changes vs. external ones, like this: Private blnInternal as Boolean Private Sub Text1_Change() If Not blnInternal Then MsgBox "Text1 Change" RaiseEvent Change End If End Sub Private Sub UserControl_ExitFocus() blnInternal = True Text1.Text = "Hello, " & Text1.Text blnInternal = False End Sub For the week of July 3rd Accept only numbers in a text field Often, to ensure that users enter only numbers in a text field, you'll want to validate the text as they enter it. The textbox's Change() event provides the best place to do so. However, simply using the IsNumeric() function alone won't do the trick. For instance, suppose you created the following procedure Private Sub Text1_Change() If Not IsNumeric(Text1.Text) Then Text1.Text = "" End If Under these circumstances, if the user entered the number 333, the control wouldn't accept it because the beginning dash isn't a number. Instead, consider using the following function Private Sub Text1_Change() If Not ValidateNumeric(Text1.Text) Then Text1.Text = "" End If End Sub Private Function ValidateNumeric(strText As String) _ As Boolean ValidateNumeric = CBool(strText = "" _ Or strText = "-" _ Or strText = "-." _ Or strText = "." _ Or IsNumeric(strText)) End Function For the week of June 26th Display UserControl property settings in the Property Window At design-time, when you set a control's property value, Visual Basic often provides a list of valid settings in a dropdown list. To display such a list for your own custom UserControl property settings, you'll need to add an enumerated property to your ActiveX Control project. To do so, declare the property's potential values with the Enum statement, like so Public Enum UCBorderStyles UCBorderNone = 0 UCBorderFlat = 1 UCBorderThin = 2 UCBorderFrame = 3 UCBorderThick = 4 End Enum Once you've established the potential values, you'll need to use the enumerated type in the Property Let and Get statements, as in Private mintBorderStyle As UCBorderStyles Public Property Get BorderStyle() As UCBorderStyles BorderStyle = mintBorderStyle End Property Public Property Let BorderStyle(val As UCBorderStyles) mintBorderStyle = val End Property Now, when Visual Basic instantiates the control at design time, the valid UCBorderStyles will appear in the Property Window's dropdown list for the BorderStyle property. For the week of June 19th Don't lose focus in UserControls Typically, to determine when a UserControl gains and loses focus, you might think to use the standard GotFocus and LostFocus events. However, in UserControls these two events aren't always reliable. There are situations where a control will lose the focus without firing the LostFocus event (for example, when Alt+Tabbing to another application), and also get the focus without firing the GotFocus event. As a result, your best bet for monitoring the focus status is to insert code in the UserControl's EnterFocus and ExitFocus events. For the week of June 12th Uncover internal DLL functions with Dependency Walker While the Windows API functions are fairly well documented, you may come across other DLL files that remain a mystery. For example, suppose you want to incorporate the MIDAS digital audio player into your application. The help files that come with this free DLL list all the important external function names. However, because the DLL doesn't support Visual Basic, you must hook into the DLL with API declarations. The internal names supported in this library aren't in the help files. To unravel this mystery, you can use the Dependency Walker, a handy utility that comes with Visual Studio (appears as Depends on the Windows program menu). Simply point the utility to the DLL in question, and it will list all the internal file names that you'll need to refer to in your API declarations. For example, in the MIDAS DLL, you use a function called MIDASplayModule to play a sound module. However, the internal name for the function is _MIDASplayModule@8. As a result, you must declare the function in Visual Basic like so Declare Function MIDASplayModule Lib "midas11.dll" Alias _ "_MIDASplayModule@8" (ByVal module As Long, ByVal loopSong _ As Boolean) As Long Using the Dependency Walker on MIDAS, you could quickly and easily determine the library's internal function names. For the week of June 5th A better way to gather multi-selected Visual Basic ListBox items Conventional Visual Basic wisdom states that in order to gather the selected items from a multi-select ListBox, you should loop through all the items and test the Selected property. As with all loops, however, this can potentially bog down slower CPU's. As a much faster and more elegant alternative, you can use the SendMessage() API function instead. As you probably know, this function lets you send a message to one or more windows. The declaration statement conforms to the following syntax: Private Declare Function SendMessage Lib "user32" _ Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg _ As Long, ByVal wParam As Long, lParam As Any) As Long Since we want to gather the listbox's selected items, we'll send the LB_GETSELITEMS constant in the wMsg argument, which you declare like so: Private Const LB_GETSELITEMS = &H191 In essence, the LB_GETSELITEMS message fills an array with the index numbers of all the selected items. As a result, you must pass two additional arguments with SendMessage(). The first argument should contain the maximum number of selected items. To retrieve this value, you can simply use the listbox's SelCount property. The second argument should hold the array variable you want to fill with index values. The following example shows how you might use this function: Dim ItemIndexes() As Long, x As Integer, iNumItems As Integer iNumItems = ThisBox.SelCount If iNumItems Then ReDim ItemIndexes(iNumItems - 1) SendMessage ListBox1.hwnd, LB_GETSELITEMS, iNumItems, _ ItemIndexes(0) End If For x = 0 To iNumItems - 1 MsgBox ListBox1.List(ItemIndexes(x)) Next x After being passed to the SendMessage function, iNumItems holds the total number of selected items, and the ItemIndexes array holds the selected item index values. Notice, that you must pass a pointer to the ItemIndexes array, and not the array itself. Thus, we passed ItemIndexes(0) into the SendMessage function, not ItemIndexes(). For the week of May 22nd Spell check RTF documents in Visual Basic To spell check RTF documents, you could resort to embedding Word into your application. However, if you plan to use the WebBrowser control, there's no need to program Word for spell checking. That's because the WebBrowser control provides this functionality for you. To access it, you use the WebBrowser control's ExecWB() method with the OLECMDID_SPELL flag. To illustrate, add the WebBrowser control to a form, then add the following code to the form's Load() event WebBrowser1.Navigate "D:\Internet\spell.rtf" Replace the path with any string that points to an RTF file. Next, add a command button, and enter this code in its Click() event: WebBrowser1.ExecWB OLECMDID_SPELL, OLECMDEXECOPT_DODEFAULT Run the project. The WebBrowser displays the RTF file, and when you click the command button, VB runs through the normal spell check operation. For the week of May 15th Compact long Visual Basic filepaths with SHLWAPI library The PathCompactPath function in SHLWAPI provides one way to compact long filenames. It does so by replacing a portion of the pathname with an ellipsis (...). This function uses the following API declaration statement: Private Declare Function _ PathCompactPath Lib "shlwapi"_ Alias "PathCompactPathA" _ (ByVal hDC As Long, ByVal _ lpszPath As String, _ ByVal dx As Long) As Long As you can see, the PathCompactPath function requires three arguments. The first argument contains a device context handle. The second argument holds the address of the pathname you want to use. The third argument contains the width in pixels of the spot in which you want the pathname to fit. So, to place a compacted filename in a label named lblEllipsis, place the following in a command button's Click() event: Private Sub Command1_Click() Dim lhDC As Long, lCtlWidth As Long Dim FileSpec As String FileSpec = "C:\MyFolder\VisualBasic\MyReallyWayTooLongFolderName\" _ & "ButWhoCares\IhaveTheAPI.doc" Me.ScaleMode = vbPixels lCtlWidth = lblEllipsis.Width - Me.DrawWidth lhDC = Me.hDC PathCompactPath lhDC, FileSpec, lCtlWidth lblEllipsis.Caption = FileSpec End Sub For the week of May 8th Referring to a DE Command's recordset in Visual Basic 6.0 Each Command object in Visual Basic 6.0's new Data Environment also has an associated recordset. In code, you refer to this object by preceding the Command object's name with rs. So, for example, if your Data Environment contains a Command named cmdSomeQuery, then to refer to the resulting recordset, you'd use something like this: Set rst = DataEnvironment1.rscmdSomeQuery For the week of May 1st Read Registry values in Visual Basic without API Often you'll want to manipulate the Windows registry in Visual Basic without resorting to lengthy API calls. Fortunately you can with the Registry Access Functions (RegObj.dll) library. This DLL lets you create a Registry object with which you can manipulate specific keys. For example, in a previous tip we showed you how to use the WebBrowser control to display an animated GIF. Unfortunately, the WebBrowser is only available if the target machine also has Internet Explorer installed. As a result, you may want to display an alternative image if Internet Explorer isn't available. To determine if IE is installed on the target Windows 95 machine, first set a reference in your project to Registry Access Functions. Then use code similar to that below: Dim myReg As New Registry, KeyFound As Boolean Dim HasIE As Boolean, sValue As String sValue = "" KeyFound = myReg.GetKeyValue(HKEY_LOCAL_MACHINE, _ "Software\Microsoft\Windows\CurrentVersion\" & _ "App Paths\IEXPLORE.EXE", "Path", sValue) If KeyFound Then HasIE = (sValue <> "") If HasIE Then MsgBox sValue 'contains the path to IE For the week of April 10th Display animated GIFs in Visual Basic While the Picture ActiveX control offers a great way to display graphics, it only shows the first image in an animated GIF. To display a fully animated GIF, without rebuilding the entire graphic frame by frame, you can use the WebBrowser control (just keep in mind that this control isn't available to machines without IE 3.0 or greater). To do so, select the Microsoft Internet Controls component. When you do, the WebBrowser control appears on Visual Basic's toolbar. Drop the control onto a form, then in the form's Load() event place the following code: WebBrowser1.Navigate "C:\Internet\anim.gif" Where the filespec points to a valid animated GIF path or URL. When you run the program, Visual Basic displays the GIF. Unfortunately, the WebBrowser also displays a right-hand scroll bar--probably not what you want for a decorative image. Believe or not, you can turn this scrollbar off just like you would normally via HTML, as in: WebBrowser1.Navigate "about:<html><body scroll='no'> <img src='D:\Internet\anim.gif'></img></body></html>" Now when you run the form, Visual Basic displays the image sans scrollbar. For the week of April 3rd Use Visual Basic's ADOX to determine if internal database objects exist Soon after ADO's release, Microsoft created an extension to the object library called ActiveX Data Objects Extensions, or ADOX. This library includes most of the DAO features missing from standard ADO, including objects related to a database's schema. With ADOX you can easily determine if a database contains a specific table, view, or query. To do so, set a reference to Microsoft ADO Ext. for DDL and Security. For schema purposes, the ADOX consists of a Catalog object, which contains the object collections that describe the database, tables and views among them. To test for an existing table in the Tables collection, you can either iterate through the collection and check each item's name against the test string; or you can use our favorite method, as seen in the following code: Dim cat As ADOX.Catalog Dim tbl As ADOX.Table Dim con As New ADODB.Connection con.ConnectionString = "Provider=Microsoft.Jet.OLEDB.3.51;" _ & "Data Source=C:\Program Files\Microsoft Visual Studio\VB98\Biblio.mdb" con.Open Set cat = New ADOX.Catalog Set cat.ActiveConnection = con On Error Resume Next Set tbl = cat.Tables("MyTable") If tbl Is Nothing Then MsgBox "MyTable doesn't exist" Else MsgBox "MyTable exists" Set tbl = Nothing End If bar, or click on screen objects. For the week of March 27th Hide the mouse cursor in a Visual Basic application The ShowCursor API function provides a quick way to hide the mouse cursor. It takes the following declaration statement Private Declare Function ShowCursor Lib "user32" _ (ByVal bShow As Long) As Long When you enter 0 for the bShow argument, the mouse cursor disappears. Enter -1 to make the mouse reappear. Just be aware when you use this function, however, that just because you hide the mouse pointer, doesn't mean it's disabled. To see what we mean, add the above declaration and the following code to a form. Dim blnShow As Boolean Private Sub Form_Click() blnShow = Not bln ShowShowCursor blnShow End Sub Private Sub Form_Load() blnShow = True End Sub Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) If Not blnShow Then ShowCursor TrueEnd SubNow, run the project. When you click the form, Visual Basic toggles off the cursor's visibility. Click the form once more, and the mouse cursor reappears. If the mouse cursor had been truly disabled, you wouldn't have been able to make the mouse cursor reappear because the click wouldn't have registered. Now, click the form so the mouse cursor disappears, and move the mouse towards the IDE's menu bar. As the invisible mouse cursor passes over each enabled button, it raises, ready to be clicked. In fact, you can still use the mouse to choose options from the menu bar, or click on screen objects. For the week of March 20th Evaluate string formulas in Visual Basic If you've come to Visual Basic from Microsoft Access, you probably miss the handy Eval() method. This method evaluates a supplied string as though it were code. As a result, you could easily evaluate mathematical expressions passed as a string, like so: iResult = Eval("(2 * 3) + 5)") which fills iResult with the value 11. To achieve this same functionality in Visual Basic, you've no doubt resorted to complicated string parsing functions. Or perhaps you added Access' DLL to your project, which may have seemed like an awful lot of DLL for such a simple method.Now, though, you'll be happy to know that you can use the Eval() method without a lot of overhead. Microsoft provides this functionality in its Script Control. This very lightweight ActiveX component is available so you can add end-user scripting ability to your applications. However, as a result, it also comes with an Eval method. Adding this component to your project provides very little overhead and gives you the ability to evaluate mathematical strings.To download this OCX, visit http://msdn.microsoft.com/scripting/. Select the Script Control link, then follow the Download instructions. The following code shows the simple Visual Basic we added to a command button's click event. The SC1 script control evaluates a mathematical formula in the txtFormula textbox. Private Sub Command1_Click() MsgBox txtFormula & " = " & SC1.Eval(txtFormula) End Sub For the week of March 13th Scroll to a Visual Basic listview control's selected item The listview control is a great way to associate items with icons, pictures, or in report rows. This control allows you to programmatically select an item in the list. However, just because Visual Basic selects an item, doesn't always mean that it will be visible to a user. For example, if you have many items displayed in the control, you might need to use the scroll bar to display an item at the bottom of the list. Fortunately, the listview control also makes it easy to programmatically scroll to a selected item. Each item in the list has an EnsureVisible method. Just as you'd expect, when you call this method, it forces the control to display the item in the visible portion of the listview.To illustrate, drop a listview control onto a default form, then right-click on it and select Properties from the shortcut menu. In the Properties Pages dialog box, change the control's View property to 3 lvwReport. Next, click on the Column Headers tab, click Insert Column, and enter "Which Foo?" in the Text field. Click OK to complete the process. Finally, add the following code to the form: Private Sub Form_Load() Dim x As Integer With ListView1 For x = 1 To 20 .ListItems.Add Key:="Item " & x, Text:="Item" & x Next x .SelectedItem = .ListItems("foo20") .SelectedItem.EnsureVisible End With End Sub When you run the project, Visual Basic opens the form and displays the 20th item in this list. For the week of March 6th Avoid API bit-masking comparison errors in Visual Basic Often when you use API functions, you'll want to determine if the results contain a specific flag. To do so, you use the And operator and mask the specific flag for which you're testing. For example, suppose you want to determine if the current window is maximized. First, get the current window's settings with the GetWindowLong() API function, which you declare in a standard module like so View Source When you run the form and click the button, the code determines if the style value contains the flag's bit value. If it does, then the boolean comparison returns a number, which Visual Basic interprets as True. If not, the comparison returns 0, or false. Errors can occur, however, when you try to test for the exclusion of a specific flag. For example, suppose you want to perform actions when the window isn't maximized. You might think to simply use If Not (lWinStyle And WS_MAXIMIZE) Then (Of course, in this particular case, you could simply test for the WS_MINIMIZE flag, but there are times when API functions don't have a flag representing an opposing setting) Unfortunately, this condition statement always returns True. That's because when you use Not on a number, Visual Basic returns the opposite value of that number minus one. For instance, the expression Not 15 returns -16, which as you know Visual Basic interprets as True. With regard to the bit masking example, if the window is maximized, Not (lWinStyle And WS_MAXIMIZE) evaluates to a non-zero number, which again Visual Basic evaluates as True. To get around this minor glitch, you can use one of the following two statments: If Not Cbool(lWinStyle And WS_MAXIMIZE) Then or If Not ((lWinStyle And WS_MAXIMIZE)<>0) Then For the week of February 28th Correctly pass variant array parameters in Visual Basic If you create a procedure that will accept both standard arrays as well as those created from the Spit() and Array() functions, then you'll definitely want to type the argument as a variant, not an array, as in Sub FooList ( MyArrayList as Variant) instead of Sub FooList (MyArrayList() as Variant) Why? Well, both the Split() and Array() functions create variant arrays-that is, a variant variable filled with an array subtype. And even though you manipulate them the same way, Visual Basic doesn't consider an array of variants and a variant array the same thing. So, if your procedure expects a regular array and you pass it a variant array created from the Split() or Array() function, you'll get a type mismatch error. On the other hand, Visual Basic does let you pass a regular array into a variant parameter-which makes sense when you consider a variant's universal nature. For the week of February 21st Generate temporary Visual Basic files with API If you've ever used Word, or any other Office application, you probably noticed that each time you open a file, Office creates a temporary file to store changes. You may have wondered how to generate random temporary file names in your own Visual Basic application. To do so, use the GetTempFileName API function, which you declare in a standard module, like so Public Declare Function GetTempFileName Lib "kernel32" _ Alias "GetTempFileNameA" (ByVal lpszPath As String, _ ByVal lpPrefixString As String, ByVal wUnique As Long, _ ByVal lpTempFileName As String) As Long Pass the full path name in the lpszPath argument. The lpPrefixString lets you add a three letter prefix to the beginning of the filename, and wUnique tells Windows to either create a random file name (0 setting) or use the number you supply. The lpTempFileName, of course, contains the new temporary filename. As an example, place the API declaration above in a standard module, then add the following function Private Function GenTempName(sPath As String) Dim sPrefix As String Dim lUnique As Long Dim sTempFileName As String If IsEmpty(sPath) Then sPath = "D:\Articles\IVB" sPrefix = "fVB" lUnique = 0 sTempFileName = Space$(100) GetTempFileName sPath, sPrefix, lUnique, sTempFileName sTempFileName = Mid$(sTempFileName, 1, InStr(sTempFileName, Chr$(0)) - 1) GenTempName = sTempFileName End Function Now, open a new form and add the following code to its Click() event. (Replace D:\Articles\IVB with any valid path) MsgBox GenTempName("D:\Articles\IVB") For more information on generating temporary Visual Basic files with API, please see the following article in the Microsoft Knowlege Base: Q195763 - HOWTO: Use GetTempFileName API to Create a Unique Temporary File For the week of February 14th Globally replace text using just 5 lines of Visual Basic code If you've ever tried to implement a text search and replace function in Visual Basic, you probably resorted to lengthy parsing loops that used Instr() and an entire host of string functions. If so, you'll be glad to know that there's a much easier way. With the advent of VBScript 5.0, Microsoft introduced the Regular Expression engine, which you can also use in Visual Basic. If you've used Perl or JavaScript, you may be familiar with these pattern-matching powerhouses. In a nutshell, regular expressions let you define a pattern, literal or representative, which you can then match against a second string. For example, the literal pattern 'abc' as a regular expression would find a match in 'dabcef', 'abcdef', or 'defabc'. To further refine a regular expression, the Regular Expression engine offers a host of special metacharacters, similar to wildcard characters. For example, using these metacharacters, you could search for any word that began with a letter and ended in a digit. To view these metacharacters and their uses, visit Microsoft Windows Script Technologies. then search in the VBScript documentation for the RegExp object's Pattern property. To create a simple find and replace subroutine, first download the VBScript DLL from http://www.microsoft.com/msdownload/vbscript/scripting.asp. Once you've registered the dll on your system, in your Visual Basic project set a reference to Microsoft VBScript Regular Expressions. Now, via code, create a RegExp object, like so Set MyReg = new RegExp Next, set the object's properties. MyReg.IgnoreCase = True MyReg.Global = True MyReg.Pattern = "abc" Here, we've used a literal string to create a global, caseinsensitive pattern. Finally, you execute the object's Replace method on the target string, as in txtSearchText = MyReg.Replace(txtSearchText, "def") That's *all* there is to it! This example would replace "abc" with "def" wherever it occurred within txtSeartText. To execute the replace only on the first matching string, set the Global property to False. For the week of February 7th Retrieve a file's short name in Visual Basic without API Many times, you'll need to reference a file by it's 8.3 file naming convention. Chances are, you've seen these file names in MSDOS. For instance, under this convention, the Program Files folder becomes Progra~1. You'll be happy to know that you can retrieve this short path name without resorting to the GetShortPathName API function. As an alternative, the new Scripting Runtime library offers the ShortPath property, which it provides for both File and Folder objects. To obtain a file's short path name, simply add a project Reference to the Microsoft Scripting Runtime, then use code similar to: Private Sub Form_Load() Dim fsoFile As File, fso As FileSystemObject Set fso = New FileSystemObject Set fsoFile = fso.GetFile("C:\MyReallyLongName.txt") MsgBox fsoFile.ShortPath Set fsoFile = Nothing Set fso = Nothing End Sub For the week of January 31st A caveat for the FileSystemObject Delete methods in Visual Basic As you know, the Microsoft Scripting Runtime object library makes it easy to manipulate files and folders. With the DeleteFolder and DeleteFile methods, you can eliminate unwanted items. However, these methods will abort if they encounter an error--and they don't rollback the changes they made up to that point. For instance, if a method deletes two folders out of ten and an error occurs, Visual Basic aborts the operation and only the first two items will have been deleted. Several FileSystemObject methods operate this way. For the week of January 24th Maintain a Visual Basic DBGrid's runtime column widths The DBGrid is a great way to display data as a familiar gridstyle output. However, if you change the column widths on the grid at runtime, Visual Basic doesn't use these new widths the next time you run the application. Fortunately, the following procedure will maintain the column widths for you: Sub DBGridLayout(Operation As String) 'save width of columnsDim lWidth As Long Dim clm As Column Dim lDefWidth As Long l DefWidth = DBGrid1.DefColWidth For Each clm In DBGrid1.Columns With clm Select Case LCase(Operation) Case "save" lWidth = .Width SaveSetting App.Title, "Cols", CStr(.ColIndex), _ lWidth Case "load" lWidth = GetSetting(App.Title, "Cols", _ CStr(.ColIndex), lDefWidth) .Width = lWidth End Select End With Next clm End Sub As you can see, this procedure uses the SaveSetting and GetSetting functions to store the current width values in Visual Basic's portion of the registry. To use the procedure, call it from the parent form's Load and Unload events. Then, indicate which operation you want the procedure to perform, as in: Private Sub Form_Load() DBGridLayout "Load" End Sub Private Sub Form_Unload(Cancel As Integer) DBGridLayout "Save" End Sub For the week of January 17th Prevent drag and drop operations on Visual Basic treeview root nodes Often when you allow drag and drop operations in a treeview control, you won't want a root node dragged to another level. For instance, if you filled the control with departments and their employees, you wouldn't want a department node placed under an employee. To prevent this from happening, you can use the OLEStartDrag event to provide validation. For example, testing for the Parent property provides a quick way determine a root node. As you know, the Parent property returns an item's Parent node. So, if the property returns nothing, then the target node to be dragged is really a root node. Using our previous tip for assigning a treeview's current node, you can use code like the following: Option Explicit Public dragNode As Node, hilitNode As Node Private Sub TreeView1_MouseDown(Button As Integer, Shift As Integer, _ x As Single, y As Single) Set dragNode = TreeView1.HitTest(x, y) End Sub Private Sub TreeView1_OLEStartDrag(Data As MSComctlLib.DataObject, _ AllowedEffects As Long) If dragNode.Parent Is Nothing Then Set dragNode = Nothing End Sub Then in the OLEDragOver and OLEDragDrop events, create conditional statements to test if the dragNode is nothing, then perform the drag and drop operations accordingly. For example, the OLEDragOver event might look like this: Private Sub TreeView1_OLEDragOver(Data As MSComctlLib.DataObject, _ Effect As Long, Button As Integer, Shift As Integer, _ x As Single, y As Single, State As Integer) If Not dragNode Is Nothing Then TreeView1.DropHighlight = TreeView1.HitTest(x, y) End If End Sub For the week of January 10th Update the Visual Basic 6.0 DataGrid control with recordset changes The DataGrid control is a great way to display multiple data rows in a table-like format. Unfortunately, the control is also plagued with bugs. Some have been fixed by Service Patch 3, but some haven't. For instance, if you connect the DataGrid to a DataEnvironment, then make changes to the underlying recordset and refresh the DataGrid with the Refresh method, the control still doesn't reflect the changes. Unfortunately, the Refresh method doesn't work when the control's DataSource is a DataEnvironment. Instead, to show the updated recordset changes, first update the DataEnvironment's recordset, then rebind the DataGrid to the DataEnvironment. So, if you have a Refresh button, it's click event might look like this: DataEnvironment1.rsCommand1.Requery Set DataGrid1.DataSource = DataEnvironment1 Now, when you click the Refresh button, the code rebinds the DataEnvironment to the DataGrid and refills the control with the refreshed data. For the week of January 3rd Drag & Drop the real selected Visual Basic treeview node Enabling drag and drop in the treeview control can seem deceptively easy. The property sheet displays two properties for altering this capability. However, as you probably know, it takes a little code behind the scenes to actually make the drag and drop work. No doubt, you've seen the control's OLEStartDrag, OLEDragOver, and OLEDragDrop events, which you use to implement this technique. The basic idea behind a drag and drop code procedure is to set a public node variable equal to the currently selected node, highlight each node the mouse passes over during the OLEDragOver event, then add the dragged item to the node under the mouse pointer to complete the procedure. With this in mind, after first setting the control's OLEDragMode to 1-ccOLEDragAutomatic, (and filling it with items, of course) you might think to initiate the OLEStartDrag event like so: Option Explicit Public dragNode As Node, hilitNode As Node Private Sub TreeView1_OLEStartDrag(Data As MSComctlLib.DataObject, _ AllowedEffects As Long) Set dragNode = Treeview1.SelectedNode End Sub Here, the code sets the node to be dragged equal to the currently selected node. Unfortunately, this doesn't work. That's because, a node only becomes a selected node after the MouseUp event. And as you know, you initiate a drag by holding the mouse button down. As is, the above code actually selects the previously selected node instead. So, to indicate the correct node, use the HitTest method in the treeview's MouseDown event, like so: Private Sub TreeView1_MouseDown(Button As Integer, Shift As Integer, _ x As Single, y As Single) Set dragNode = TreeView1.HitTest(x, y) End Sub For the week of December 6th Replace lengthy path strings with Visual Basic object variables Many methods that manipulate a drive, folder, or file in the MS Scripting Runtime library require a filespec argument--the item's path, represented as a string. However, because the Path property is the default property for these three items (drive, folder, and file), you can also use object variables in place of the path strings. So for instance, to copy one folder to another using object variables, you could use fldr1.Copy fldr2, False or MyFSO.CopyFile file1, file2, False For the Week of November 22nd Use ADO's native OLEDB drivers instead of ODBC When you create a connection string, ADO gives you a choice between indicating a data source driver as either a Driver, as in Driver={SQL Server};DBQ=database_file or a Provider, such as Provider=Microsoft.Jet.OLEDB.4.0;Data Source=database_name However, when you use the first option, ADO uses older ODBC drivers to connect to the data source; whereas the second form uses OLEDB, which is ADO's native data access interface. For this reason, you should use the Provider option whenever possible. Such native OLEDB drivers exist for SQL Server, Index Server, Site Server Search, and Oracle, among others. When using the provider method and distributing the application, the correct version of MDAC must be distributed as well, as they are not compatible. The MDAC 2.0 series carries the OLEDB 3.51 provider, the MDAC 2.1 series carries the OLEDB 4.0 provider. For the Week of November 15th Let Visual Basic determine if the CD Rom drive contains media To quickly determine if the CD Rom drive contains media, use the Scripting Runtime library's IsReady property for the Drive object. For CD Rom drives, this property returns True only if the drive contains the appropriate media. To take advantage of this handy property, add a Reference to Microsoft Scripting Runtime library (scrrun.dll). Next, create a Drive variable based on the CD Rom drive, and test the IsReady property, as shown below: Dim FSO As FileSystemObject Dim aDrive As Drive Set FSO = New FileSystemObject For Each aDrive In FSO.Drives If aDrive.DriveType = CDRom And aDrive.IsReady = False Then MsgBox "Please enter a CD." Exit For ElseIf aDrive.DriveType = CDRom Then MsgBox aDrive.VolumeName Exit For End If Next Set FSO = Nothing For the Week of November 8th Retrieve subitem values from a Visual Basic ListView control When you use the ListView control in Report view, the control shows a table-like list. It's the same view that appears in the file dialog boxes (Open, Save, etc.) when you click the Details button. Each row displays additional subitem information about the main item. For example, in the file dialog boxes, the main item is the file itself, while the Size and Type columns are the subitems. When you use the ListView control in your own applications, Visual Basic makes it easy to determine which item you've selected. When you select an item in Report view, Visual Basic triggers the ItemClick event, which passes a ListItem object (the item you just clicked). From this object, you can determine its value. So, for example, the entire event procedure might look like this Private Sub lstvwFiles_ItemClick _ ByVal Item As MSComctlLib.ListItem) strFileSelected = Item End Sub This statement would pass the selected item's value (Value being the object's default property) into the strFileSelected variable. However, you may have wondered how to retrieve the Item's associated subitem values. Fortunately, Visual Basic provides the ListSubItems collection, which contains all the subitems of an individual item. So, continuing with the example above, the Size and Type items are both subitems of the main File item. To ascertain the values of these subitems, you simply loop through the collection, like so Private Sub lstvwFiles_ItemClick _ (ByVal Item As MSComctlLib.ListItem) Dim itm As ListSubItem Dim strSubs As String MsgBox "ItemClicked: " & Item For Each itm In Item.ListSubItems strSubs = strSubs & itm & ": " Next itm MsgBox "SubItems: " & strSubs End Sub For the Week of October 11th Add controls to a Visual Basic control array at run-time As you probably know, a control array lets you create controls that share the same name and events. They also use fewer resources than do the same number of controls not part of a control array. Often, you may want to add a control, such as a button, to a control array at runtime. To do so, you use the Load statement, which takes the following syntax. Load object(index) where object is the name of the control array, and index is the index number of the new control you want to add. In order to add controls to a control array at runtime, however, you must have at least one control already in the array, (with it's index property set-most likely to 0). Visual Basic only allows 32,767 controls in an array. For example, suppose you have a form with a button control array named cmdBtn. On the button's Click event, you want to add another button to the form. To illustrate, open a new project and add a command button to the default form. In the Properties Window, enter 0 for the control's Index. When you do, Visual Basic transforms the button into a control array. Now, add the following code to the form: Private Sub cmdBtn_Click(Index As Integer) Dim btn As CommandButton Dim iIndex As Integer iIndex = cmdBtn.Count If iIndex <= 32767 Then Load cmdBtn(iIndex) Set btn = cmdBtn(iIndex) With btn .Top = cmdBtn(iIndex - 1).Top + 620 .Caption = "Command" & iIndex + 1 .Visible = True End With Set btn = Nothing End If End Sub For the Week of October 4th Use the Visual Basic 6.0 Split function to count substrings As we mentioned in a previous tip, Visual Basic 6.0 introduced the Split function, which provides a new way to parse strings. With it, you indicate a delimiter within a string, and Visual Basic fills a one-dimensional array with the substrings. However, in addition to the functionality described above, you can also use the Split function as a quick way to determine the number of substrings within a larger string. For example, suppose you want to determine the number of times 'sea' appears in the string "She sells seashells by the seashore." To do so, simply perform the split, and use the UBound function to count the number of elements in the resultant array, as in strTungTied = "She sells seashells by the seashore." arySea = Split(strTungTied, "sea") MsgBox "'Sea' appears in '" & strTungTied _ & "' " & UBound(arySea) & " times." When you run this code snippet, Visual Basic informs you that 'sea' appears in the phrase twice. For the Week of September 20th Let DateDiff() determine if two dates are in the same month To determine if two dates are in the same month, your first instinct may be to simply use the Month() function on each date, then compare the two resulting numbers. However, under these circumstances, the comparison would equate 1/1/2000 with 1/1/1999. Instead, the DateDiff() function provides one quick way to make this determination, like so: DateDiff("m", Date1, Date2) In this expression, the DateDiff() function finds the difference in calendar months between the two dates. If the expression returns a zero, then the two dates are in the same calendar month. To include this feature in an conditional expression, you could use the following in a query: If DateDiff("m", Date1, Date2) Then 'Month's are different Else 'Month's are same End If For the Week of September 13th Force Visual Basic 6.0 to open maximized code windows While Visual Basic 5.0 was very good at remembering how you preferred the IDE windows-maximized or normal. Visual Basic 6.0 isn't. It always opens the Code and Object windows in normal view. Fortunately, you can modify this behavior with a minor tweak to the Windows Registry so that the IDE always opens these two windows maximized. Unfortunately, when you make these changes, Visual Basic 6.0 will ALWAYS open them maximized-it still won't remember your preferences between sessions. Before we begin, take note that altering the Registry is risky business and you should always make a back-up copy of the settings so that you can restore them if something untoward happens. That said, to force Visual Basic 6.0 to open a maximized Code or Object window, you add a new value called MDIMaximized to the following Registry key: HKEY_CURRENT_USER/Software/Microsoft/Visual Basic/6.0/MDIMaximized = "1" To do so, in Windows click the Start button and select Run. Enter RegEdit in the Run dialog box, then click OK. When Windows displays the system registry, navigate through the keys until you've found the Visual Basic 6.0 folder. Next, rightclick anywhere in the right-hand pane and select New/ String Value from the shortcut menu. Enter MDIMaximized as the name and press [Enter]. Now, right-click on MDIMaximized and select Modify from the shortcut menu. Finally, in the Edit String dialog box, enter 1 for the value and click OK. When you do, Windows assigns the value to MDIMaximized. That's all there is to it! Close the registry and open a Code or Object window in a Visual Basic 6.0 project. The IDE displays them maximized. For the Week of September 6th Add a new line to existing textbox text Often, you may want to append additional information to existing text within a multiline textbox. For instance, suppose you want to add the string "Updated: " followed by the current date. To do so, you can take advantage of the SelStart and SelText properties. As you probably know, the SelStart property returns or sets the beginning of a selection. The SelText returns or sets the actual selected text. If there isn't a selection, then both properties return the insertion point. So, to insert a new line of text in a multiline textbox, use code similar to: Dim strNewText As String With Text1 strNewText = "Updated: " & Date .SelStart = Len(.Text) .SelText = vbNewLine & strNewText End With This code snippet moves the insertion point to the end of any existing text in Text1, then inserts a new line followed by the additional information. For the Week of August 30th Enabling the horizontal scrollbar in a RichTextbox control By default, when you add a RichTextbox control to a form, Visual Basic sets the RightMargin property to 0. This means that the text you enter wraps in the control. To display the horizontal scroll bar, you must set the RightMargin property to a value greater than the control's width. Otherwise, even if you set the ScrollBars property to 1-rtfHorizontal, the RTB won't display the scrollbar. As an example, add a 3200 wide RTB to a form, then set the RightMargin to 3300 and the ScrollBars to 1-rtfHorizontal. Run the project and type text into the control until it extends beyond the RTB's boundaries. When you do, Visual Basic displays the horizontal scroll bar. For the Week of August 23rd Visible Cues from a Minimized Form Suppose you want a form to perform a task while minimized, then notify the user without a message box and while remaining minimized. You can send a message via a changing icon on the minimized form in the taskbar. Create a form containing a timer and an image list. Set the timer's Interval property to 2000, then use the ImageList control's Custom property to add three images. Finally, add this code to the Timer event: Private Sub Timer1_Timer() Static iImage As Integer iImage = iImage + 1 If iImage > 3 Then iImage = 1 Me.Icon = ImageList1.ListImages(iImage).Picture End Sub For the Week of August 9th Use the Timer control for longer than 1 minute As you know, the Timer control provides a great way to schedule events in a Visual Basic project. When you enable the control, it fires off its Timer event every n milliseconds, as determined by the TimeInterval property. However, the TimeInterval property only accepts numbers up to 65,535, or just over one minute. As a result, you may have wondered how to use this control for periods longer than that. To do so, use a form, or project level, variable to keep track of how many times the Timer event fires. Then, in the Timer event, re-enable the control if enough time hasn't passed. For example, consider the code below that we attached to a standard form. Option Explicit Dim iElapsedMin As Integer Const cMax_Min As Integer = 2 Private Sub Form_Load() Timer1.Enabled = True iElapsedMin = 1 End Sub Private Sub Timer1_Timer() lblText.Visible = (iElapsedMin = cMax_Min) Timer1.Enabled = (iElapsedMin < cMax_Min) iElapsedMin = iElapsedMin + 1 End Sub For a Week of August 2nd A new Format function Visual Basic 5 has the Format command that almost works the same as Print. The difference is that Format shortens the output string length if all the format characters are not used. To work around this I wrote a Public Function called FormatNum. Public Function FormatNum(MyNumber As Double, FormatStr As String) As String ' This Function returns number formatted as a string ' with the desired minimum number of characters ' MyNumber - Use CDbl(MyNumber) in the function ' call to prevent type mismatch error. ' FormatNum = Format$(MyNumber, FormatStr) If Len(FormatNum) < Len(FormatStr) Then FormatNum = Space$(Len(FormatStr) - Len(FormatNum)) & FormatNum End If End Function Use this function like this: Print #FileNumber, FormatNum(CDbl(MyVariable), " #### ") For the Week of June 7th Using Server.HTMLEncode() Function Although it is not recommended (and almost always a bad idea) many ASP applications contain and pass connection string information. If your ASP application was written with ActiveX Data Objects (ADO) 1.x and it passed the ADO ConnectionString property using HTTP POST or GET, it may fail when switched to ADO 2.0. ADO 2.0 returns the ConnectionString with the "Extended Properties" argument in double-quotes. If this value is passed to an HTML property, it will result in a truncated ConnectionString value at the first double-quotes encountered. An example of the ConnectionString property returned using ADO 2.0 is displayed below: Provider="MSDASQL.1;Data Source=Mytest;Connect Timeout=15; Extended Properties="DSN=MyTest;DBQ=C:\MyTest.mdb; DriverId=25;FIL=MS Access;MaxBufferSize=512;PageTimeout=5;"; Locale Identifier=1033 The problem may be remedied by displaying the Server.HTMLEncode() function as: <%@ LANGUAGE="VBScript"%> <% Set objConn = Server.CreateObject("ADODB.Connection") objConn.Open "TheConnectString.." %%> <form method=post action=test.asp> <input type=hidden name=txtConnString value="<%=Server.HTMLEncode(objConn.ConnectionString)%>"> <input type=submit value=" Submit! "> </form> For the Week of May 31st Using Response.IsClientConnected Property to Determine Browser Connection When a browser requests an ASP page from the Web Server, but does not wait for the entire page to be downloaded, the server continues to process the request, wasting valuable CPU cycles. If running Internet Information Server (IIS) 4.0, you can use the Response.IsClientConnected property to determine whether or not the browser is still connected to the server. If it is not connected to the server, processing can be stopped to conserve CPU cycles. To do this, request an ASP page that contains the script below and use PerfMon to monitor the CPU cycles on the Web server. Note if you click "Stop" in the browser, the number of CPU cycles will decrease sooner than if the loop had continued. <%@ LANGUAGE="VBSCRIPT" %> <% Function IsConnectedAfter(Seconds) Dim StartTime Dim PauseTime IsConnectedAfter = True StartTime = Now Do While DateDiff("s", StartTime, Now) < Seconds PauseTime = Now Do While DateDiff("s", PauseTime, Now) < 1 'Do Nothing Loop Response.Write "." If Response.IsClientConnected = False then IsConnectedAfter = False Exit Function End If Loop End Function %> For the Week of May 24th Enhancing Browser Page Load Performance If your ASP application is experiencing browser page load performance problems consider implementing one or both of the following techniques. One easy way to improve the speed performance of an ASP page is to set the @ENABLESESSIONSTATE to False if an ASP page does not use any session variables. To accomplish this, insert the following line of code at the top of the ASP page. <%@ENABLESESSIONSTATE = False%> Page performance also may be improved by copying the values of collection variables into local variables (as opposed to referencing the object each time the variable value is needed). For example, if you want to reference the Request.ServerVariable collection to determine the users logon id, you would use the following line of script: Request.ServerVariables("LOGON_USER") If you need to use this value in several places on your page, it is much more efficient to store this value to a local variable and then reference the local variable. <% Dim User User = Request.ServerVariables("LOGON_USER") Request.Write User %> For the Week of May 17th Switching to Design View after Modifying ASP Script in Visual InterDev When developing an Active Server site using Visual InterDev6 you may have encountered the annoying problem of not being able to switch the editor into Design view after you have modified the ASP script. The Visual InterDev editor can't switch into Design view because there is unquoted ASP script inside of an attribute value or inside of a <select> tag. The problem is that the Design Editor doesn't know how to handle ASP script within certain HTML tags. Inserting the code below into an ASP page from the Visual InterDev source Editor and then switching to Design view will demonstrate this problem: <SELECT> <OPTION value="<%=Value1%>">First Option</OPTION> </SELECT> To get around this problem, you need to use the Response.Write method to write out the HTML that contains the embedded ASP. You will then be able to switch to Design view. The HTML control however will not appear on the page. The code below demonstrates how to accomplish this. <SELECT> <% Response.Write "<OPTION value=" & Value1 & "> First Option</OPTION>" %> </SELECT> For the Week of May 10th Parsing Using SPLIT Function Parsing functions are one of the most commonly, over-written string manipulation functions. Visual Basic 6.0 answered this problem by adding a SPLIT function. The function is very easy to use and, with only one line of code, you can parse any string using a specific delimiter. The code looks like this: Dim strAnimals As String Dim iCounter As Integer Dim arrAnimals() As String strAnimals = "Cats,Dogs,Horses,Birds" '-- Parse String arrAnimals = Split(strAnimals, ",") '-- Loop through array For iCounter = LBound(arrAnimals) To UBound(arrAnimals) MsgBox arrAnimals(iCounter) Next For the Week of May 3rd Keep Your Background Processes Running In Visual Basic, if you make a call to the MSGBOX function all other background processes that you may have running (counters, timer events, etc) are stopped until the user acknowledges the Msgbox dialog box. This can be potentially devastating if you write an application that runs unattended. To overcome this problem, you must use the Windows API call for the MessageBox function. It looks and acts the same as the Visual Basic "msgbox" function, but does not stop the background processes from running. In a module, paste the following API declaration: Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long Next, on the default form add a timer control, 2 command buttons, and a label. Then type the following code into the form, which demonstrates the Visual Basic msgbox and API MessageBox functions. That's all there is to it. Private Sub Command1_Click() MsgBox "The Timer STOPS!" End Sub Private Sub Command2_Click() MessageBox Me.hwnd, "Notice the timer does not stop!", "API Call", _ vbOKOnly + vbExclamation End Sub Private Sub Timer1_Timer() Label1.Caption = Time End Sub For the Week of April 26th Creating Professional Documents with Code Have you ever wanted to create polished, professional documents, like those created in Microsoft Word, through the use of code? Follow these easy steps to make it happen: First add a reference to your project for "Microsoft Word 8.0 Object Library" (MSWORD8.OLB). Then add the following code to create an instance of Word and add text to a new document: Dim objWord As New Word.Application '-- Show Microsoft Word objWord.Visible = True '-- Add new Document objWord.Documents.Add '-- Add text to Document objWord.Selection.TypeText "Visual Basic!" '-- Select all Text objWord.Selection.WholeStory '-- Change Font Size objWord.Selection.Font.Size = 50 Set objWord = Nothing Check out the Object Browser for more properties and methods exposed by the Word Object. For the Week of April 19th Convert NULL Values to Empty Strings to Avoid Errors One way to avoid errors from occurring when retrieving NULL values from a recordset object is to inspect the field's value. If it is NULL, then convert it to an empty string or zero. For example: If isnull(rs("Field")) then tmp="" else tmp=rs("Field") form.textfield=tmp An even simpler way is to use the format function, which will convert a NULL value to an empty string automatically, avoiding any error messages. It will look like this: form.textfield=format(rs("Field")) Top of Page Friday, Feb. 26th: Writing to the Windows NT event log Windows applications typically write to the NT event log to provide the user with useful information. In VB5/6, the App object now provides methods to make writing to the event log in Windows NT a snap: '-- Start Event Logging Call App.StartLogging("", vbLogToNT) '-- Log Events to NT Call App.LogEvent("Info", vbLogEventTypeInformation) Call App.LogEvent("Error", vbLogEventTypeError) Call App.LogEvent("Warning", vbLogEventTypeWarning) Check out the Knowledge Base article App.LogEvent Only Logs in Compiled Applications for more information. Top of Page Friday, Feb. 12th: Keeping track of Visual Basic source code builds Keeping track of source code builds in Visual Basic can be easy, if you use the Version Numbering feature provided in EXE creations. Click on the options button when creating an EXE file. Then turn on the "Auto Increment" checkbox. Versioning supports a three-segment version number: Major, Minor and Revision. The Auto Increment feature, if selected, will automatically increase the Revision number by one each time you run the Make Project command for this project. Typically, build information will go on an About form. Just add a label called, "lblVersion" and add the following code to your form: lblVersion.Caption = "Version: " & App.Major & "." & App.Minor & "." & App.Revision If my Major version number is 2, the Minor number is 1 and the Revision is 12, the label will display: "Version: 2.1.12" Top of Page February 5, 1999 Implementing a "wait" function in Visual Basic Here's a simple way of implementing an accurate "wait" function in Visual Basic. 1. Add a timer called timer1 to a form. Set its Interval property to 0 and it's enabled property to FALSE. 2. Add two labels (label1 and label2) and a command button (command1) to the form. 3. Add the following subroutine and Timer1 event code: 4. Public Sub Wait(seconds) 5. 6. '-- Turn timer on 7. Timer1.Enabled = True 8. 9. '-- Set Timer Interval 10. Me.Timer1.Interval = 1000 * seconds 11. While Me.Timer1.Interval > 0 12. 13. DoEvents Wend 14. 15. '-- Turn timer off 16. Timer1.Enabled = False 17. End Sub 18. 19. Private Sub Timer1_Timer() 20. 21. Timer1.Interval = 0 End Sub That's it! Use the Wait function anywhere a delay is required, for example: Private Sub Command1_Click() Label1.Caption = Now Wait (5) Label2.Caption = Now End Sub Top of Page January 29, 1999 Create templates from existing forms Here's a way for you to create a template from an existing form so you can use it to design future forms. Once you've created the 'perfect' form, place it in the ..\Template\Forms\ subdirectory. Now, the next time you want to add a new form to your project, you'll see that the newly created form is in the list of available form templates! Just select it and you're on your way. This tip also applies to modules, classes, etc. Top of Page January 4, 1999 Nothing to declare? When you use the Windows API, you should never need to write your own Declare statement. The API Text Viewer utility loads in a text file (WIN31API.TXT for 16-bit, and WIN32API.TXT for 32-bit) and lets you copy declares, global constants, and user-defined types. You can also convert the text file to an Access database for faster searching. Handy, eh? Top of Page November 16, 1998 Figuring out the current screen resolution You can use the following small piece of code to detect the current screen resolution and then act on the information - for instance, by resizing form objects to suit the user's resolution. Dim x,y As Integer x = Screen.Width / 15 y = Screen.Height / 15 If x = 640 And y = 480 Then MsgBox ("640 * 480") If x = 800 And y = 600 Then MsgBox ("800 * 600") If x = 1024 And y = 768 Then MsgBox ("1024 * 768") Top of Page November 09, 1998 Finding the last day of the month In many industries (particularly in the insurance industry), it's important to know the last day of the month. To find the last day of a given month, add a text box and a command button to a form. Enter the following code in the command button: Dim TEMP2 As Date Dim nLastDay As Integer TEMP2 = InputBox$("Please Enter A Date", "LastDay") nLastDay = DatePart("d", DateAdd("M", 1, TEMP2 DatePart("d", TEMP2))) Text1.Text = nLastDay When you run the application and click the button, you'll be prompted for a date. Then, the program will display the last day of that month of any year. Top of Page August 31, 1998 GETting the contents of a file submitted by Pritesh TransCapacity LP; spritesh@hotmail.com To read a complete file in Visual Basic, the normal procedure is to read the contents of the file line by line and accumulate it into a string. Instead, you can use the GET function to read the file with a single call. Doing so simplifies and speeds up the process of reading a file. You can use the following function: Dim Handle As Integer Dim FileString As String Handle = FreeFile Open "C:\TEMP\TST.TXT" For Binary As #Handle FileString = Space(FileLen("C:\TEMP\TST.TXT")) Get #Handle, ,FileString Close #Handle This code involves a single call to return the contents of the file. Top of Page August 24, 1998 Help with Shell submitted by Brad Gile bgile@amfam.com Suppose you have a DOS program, Dosapp.exe. This program produces an output file that will subsequently be processed, and you want to do this N times. The code might look like this: for Trial = 1 to N x=shell("Dosapp.exe",vbHide) Process the output Next Trial The problem is, the code after the Shell may be executed before the Shelled Dosapp.exe has created the file. There are complicated ways of solving this, including API calls, but here's a simple solution: the FileLen function. If the file to be processed is x$, you can just insert a few lines of code: for Trial = 1 to N Open x$ for output as 1 Close 1 ' This sets file length equal to zero x=shell("Dosapp.exe",vbHide) Do While FileLen(x$) = 0 DoEvents ' Halt further execution until x$ is created and closed Loop ' Process the output to add to a new file Next Trial You may want to do other things such as using Timer to prevent an infinite Do Loop, but this is the main idea. FileLen() works because even if x$ is open and has data in it, FileLen(x$) = 0. Thus you're assured that the process code won't execute until x$ is fully created. Top of Page August 17, 1998 Watch your window state submitted by Rick Michelhaugh 3m8@ornl.gov My application saves the window location and size, so it will open the next time at the same screen location. This technique is great and pleases customers. But after several months, the program wouldn't start right -- it would die with an 'overflow error'. We tried every possible test, and could only determined that something in the INI file had become corrupted. Finally, we found that the window's Top, Left, Width, and Height values were incorrect. When the program started and read these wrong numbers, the 'overflow error' was the result. As it turned out, the numbers were being thrown off when the user minimized the window and exited Windows with it minimized. As the program closes, it saves the position values of the minimized state -- which messes up the values. To fix the problem, we added some code to check the windowstate. If it's vbMinimized, the program skips the code that saves the window position. Top of Page August 10, 1998 Writing to and reading from INI files submitted by David Dommisse Dommisse@hotmail.com Here's an example that will write to and read from a INI file. You can use a file named TEST.INI that looks like this: [MyApp] Key1= TestString Put the following code in the declaration section of a module: View Source Now, use the following code to read from the INI file: View Source The key's value will be in retStr = TestString. The following code will write to the INI file: View Source After you write to the INI file, its contents will look like this: [MyApp] Key1=TestString Key2=My Test Top of Page August 3, 1998 Tracking down Resume Next submitted by Bill Shadish Fundamental Objects, Inc; bills@fo.com This tip is extremely simple. But try it, and see if it helps you some time in the future. Maybe you've inherited a large chunk of code from that developer who's smiling on the way out the door. Or, perhaps you're getting ready to ship that version 1.0 application to 100 users, try this. In any case, take a minute to search all of the code for the string On Error Resume Next In fact, try this even if you don't want to ship your code to 100 users -- or especially if you have code that seems to fail "for no reason." You might be surprised at what that ol' Resume Next is hiding. Top of Page July 27, 1998 Labeling your forms submitted by Bill Shadish Fundamental Objects, Inc; bills@fo.com Do you have a ton of screens in your application? Do you also have plenty of users who want to "help you" by pointing out buttons that are one twip out of place? Sometimes it's hard to know what screen users are talking about when they're trying to communicate a problem -- particularly if they're in a different location than you. To reduce the pain of this process, I add a label (called lblHeader) to the top of each GUI window, nominally to hold start-up information for users when they first open the window. You can also use this label to hold the name of the window the user is looking at, by using the following code: Private Sub Form_Load() SetupScreen me End Sub Public SetupScreen (frm as Form) ' Do other set-up stuff here (fonts, colors). HookInFormName frm End Sub Public Sub HookInFormName(frm As Form) ' The Resume Next on Error allows forms that do not use a standard ' header label to get past this. On Error Resume Next frm.lblHeader.Caption = "(" & frm.Name & ") " & frm.lblHeader.Caption End Sub Note that if you don't want to use a label, that you can also use code like frm.print frm.name to print to the back of the window itself. Top of Page For the Week of July 6, 1998 Opening a browser to your homepage submitted by Dan Newsome D&D Information Professionals, newsomed@earthlink.net You can use code like the following to open a browser to your homepage. Modify filenames, paths, and URLs as necessary to match the values on your system. Dim FileName As String, Dummy As String Dim BrowserExec As String * 255 Dim RetVal As Long Dim FileNumber As Integer Const SW_SHOWNORMAL = 1 ' Restores Window if Minimized or Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _ (ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, _ ByVal lpParameters As String, ByVal lpDirectory As String, _ ByVal nShowCmd As Long) As Long Declare Function FindExecutable Lib "shell32.dll" Alias "FindExecutableA" _ (ByVal lpFile As String, ByVal lpDirectory As String, ByVal lpResult As _ String) As Long '<Code> --------- BrowserExec = Space(255) FileName = "C:\temphtm.HTM" FileNumber = FreeFile() ' Get unused file number Open FileName For Output As #FileNumber HTML file Write #FileNumber, " ' Create temp <\HTML>" ' Output text Close #FileNumber ' Close file ' Then find the application associated with it. RetVal = FindExecutable(FileName, Dummy, BrowserExec) BrowserExec = Trim$(BrowserExec) ' If an application is found, launch it! If RetVal <= 32 Or IsEmpty(BrowserExec) Then ' Error Msgbox "Could not find a browser" Else RetVal = ShellExecute(frmMain.hwnd, "open", BrowserExec, _ "www.myurl.com", Dummy, SW_SHOWNORMAL) If RetVal <= 32 Then ' Error Msgbox "Web Page not Opened" End If End If Kill FileName ' delete temp HTML file Top of Page For the Week of June 29, 1998 Creating a incrementing number box submitted by Bryan Shoemaker www.shadow.net/~fubar You can't increment a vertical scroll bar's value -- a fact that can become annoying. For example, start a new project and place a text box and a vertical scroll bar on the form. Place the vertical scroll bar to the right of the text box and assign their Height and Top properties the same values. Assign the vertical scroll bar a Min property value of 1 and a Max value of 10. Place the following code in the vertical scroll bar's Change event: Text1.Text = VScroll1.Value Now press [F5] to run the project. Notice that if you click on the bottom arrow of the vertical scroll bar, the value increases; if you click on the top arrow, the value decreases. From my perspective, it should be the other way around. To correct this, change the values of the Max and Min properties to negative values. For example, end the program and return to the design environment. Change the vertical scroll bar's Max value to -1 and its Min value to -10. In its Change event, replace the line you entered earlier with the following: Text1.Text = Abs(Vscroll1.Value) Now press [F5] to run the project. When you click on the top arrow of the vertical scroll bar, the value now increases. Adjust the Height properties of the text box and the scroll bar so you can't see the position indicator, and your number box is ready to go. Top of Page For the Week of June 22, 1998 Measuring a text extent submitted by Nenad Cus Babic Nenad@computer.org It's very simple to determine the extent of a string in Visual Basic. You can do so with WinAPI functions, but there's an easier way: Use the AutoSize property of a Label component. First, insert a label on a form (labMeasure) and set its AutoSize property to True and Visible property to False. Then write this simple routine: Private Function TextExtent(txt as String) as Integer labMeasure.Caption = txt TextExtent = labMeasure.Width End Function When you want to find out the extent of some text, simply call this function with the string as a parameter. In my case it turned out that the measure was too short. I just added some blanks to the string. For example: Private Function TextExtent(txt As String) As Integer labMeasure.Caption = " " & txt TextExtent = labMeasure.Width End Function Top of Page For the Week of June 15, 1998 Importing Registry settings submitted by Dan Newsome D&D Information Professionals, newsomed@earthlink.net You can use just a few lines of code to import Registry settings. If you have an application called myapp.exe and a Registry file called myapp.reg, the following code will put those settings into the Registry without bothering the user. Dim strFile As String strFile = App.Path & "\" & opts.AppExeName & ".reg" If Len(Dir$(strFile)) > 1 Then lngRet = Shell("Regedit.exe /s " & strFile, vbNormalFocus) End If Top of Page For the Week of June 01, 1998 Use FreeFile to Prevent File Open Conflicts Submitted by markus@bactive.com Both Access and Visual Basic let you hard code the file numbers when using the File Open statement. For example: Open "myfile.txt" for Append as #1 Print #1,"a line of text" Close #1 The problem with this method of coding is that you never know which file numbers may be in use somewhere else in your program. If you attempt to use a file number already occupied, you'll get a file error. To prevent this problem, you should always use the FreeFile function. This function will return the next available file number for your use. For example: IntFile=FreeFile() Open "myfile.txt" for Append as #intFile Print #intFile,"a line of text" Close #intFile Top of Page For the Week of May 25, 1998 Confirm Screen Resolution Submitted by Nicholas L. Otley, nicholaso@kalamzoo.co.uk; www.kalamazoo.co.uk Here's a great way to stop the user from running your application in the wrong screen resolution. First, create a function called CheckRez: Public Function CheckRez(pixelWidth As Long, pixelHeight As Long) As Boolean ' Dim lngTwipsX As Long Dim lngTwipsY As Long ' ' convert pixels to twips lngTwipsX = pixelWidth * 15 lngTwipsY = pixelHeight * 15 ' ' check against current settings If lngTwipsX <> Screen.Width Then CheckRez = False Else If lngTwipsY <> Screen.Height Then CheckRez = False Else CheckRez = True End If End If ' End Function Next, run the following code at the start of the program: If CheckRez(640, 480) = False Then MsgBox "Incorrect screen size!" Else MsgBox "Screen Resolution Matches!" End If Top of Page For the Week of May 18, 1998 Quick Text Select On GotFocus Submitted by scott@beacon-inc.com When working with data entry controls, the current value in the control often needs to be selected when the control received focus. This allows the user to immediately begin typing over any previous value. Here's a quick subroutine to do just that: Public Sub FocusMe(ctlName As Control) ' With ctlName .SelStart = 0 .SelLength = Len(ctlName) End With ' End Sub Now add a call to this subroutine in the GotFocus event of the input controls: Private Sub txtFocusMe_GotFocus() Call FocusMe(txtFocusMe) End Sub Top of Page For the Week of May 11, 1998 Take Advantage of Built-in OLE Drag and Drop Submitted by Michael C. Amundsen, mike_tips@amundsen.com, http://www.amundsen.com To add fully functional drag and drop to your forms, all you need to do is set the OLEDragMode property and OLEDropMode property of the input controls to "Automatic." This works across forms and even across applications on the same workstation. To test this, add two text boxes to a form. Use the Property Window to set the OLEDragMode and OLEDropMode properties to "Automatic." Now run the app and enter text in one text box and drag and drop it into the other text box. Hint: Some controls only support OLEDrag. A few others do not support OLEDrag or OLEDrop. To determine what a control supports, check for the OLEDragMode and OLEDropMode properties. If their missing the control does not support OLEDrag and/or OLEDrop. Top of Page For the Week of May 04, 1998 Use ParamArray to Accept an Arbitrary Number of Parameters Submitted by Michael C. Amundsen, mike_tips@amundsen.com, http://www.amundsen.com You can use the ParamArray keyword in the declaration line of a method to create a subroutine or function that accepts an arbitrary number of parameters at runtime. For example, you can create a method that will fill a list box with some number of items even if you do not know the number of items you will be sent. Add the method below to a form: Public Sub FillList(ListControl As ListBox, ParamArray Items()) ' Dim i As Variant ' With ListControl .Clear For Each i In Items .AddItem i Next End With ' End Sub Note that the ParamArray keyword comes BEFORE the parameter in the declaration line. Now add a list box to your form and a command button. Add the code below in the "Click" event of the command button. Private Sub Command1_Click() ' FillList List1, "TiffanyT", "MikeS", "RochesterNY" ' End Sub Top of Page For the Week of April 27, 1998 Use FileDSNs to ease ODBC Installs Submitted by Michael C. Amundsen, mike_tips@amundsen.com, http://www.amundsen.com If you're using an ODBC connection to your database, you can ease the process of installing the application on workstations by using the FileDSN (data source name) instead of the morecommon UserDSN. You define your ODBC connection as you normally would with UserDSNs. However, the resulting definition is not stored in the workstation registry. Instead it gets stored in a text file with the name of the DSN followed by ".dsn" (i.e. "MyFileDSN.dsn"). The default folder for all FileDSNs is "c:\program files\common files\Odbc\data sources". Now, when you want to install the Visual Basic application that uses the FileDSN, all you need to do is add the FileDSN to the Install package and run the install as usual. No more setting up DSNs manually! NOTE: FileDSNs are available with ODBC 3.0 and higher. Top of Page For the Week of April 20, 1998 Increase Your RAD-ness by Creating Your Own VB5 Templates Submitted by Michael C. Amundsen, mike_tips@amundsen.com, http://www.amundsen.com You can create your own Visual Basic Templates quickly and easily. If you find that you are adding the same routines to your forms, classes, BAS modules, etc. you can build generic versions and place them in the Templates folder tree of VB5. By placing the coding module (frm, bas.cls, etc.) in the proper subfolder of the Templates folder, you'll see the new item appear whenever you select the Add... dialog box. You can add as many controls, library references, and lines of code as you wish to the templates. CAUTION: If you uninstall VB5, you may loose your Templates folder and all its contents. Be sure to keep a secured copy of all your template files in a safe location. Top of Page For the Week of April 06, 1998 Add Dithered Backgrounds to your Visual Basic Forms By: Barron Anderson, Micron Electronics, Inc. Ever wonder how the SETUP.EXE screen gets its cool shaded background coloring? This color shading is called dithering, and you can easily incorporate it into your forms. Add the following routine to a form: View Source Now, add to the Form_Activate event the line Dither ME This version creates a fading blue background by adjusting the blue value in the RGB function. (RGB stands for Red-GreenBlue.) You can create a fading red background by changing the RGB call to RGB(255 - intLoop, 0, 0). Top of Page For the Week of March 30, 1998 Dragging items from a list to another one By Bassam Alkharashi, bkhrashi@kacst.edu.sa Here's a way that you can let users drag items from one list and drop them in another one. Create two lists (lstDraggedItems, lstDroppedItems) and a text box (txtItem) in a form (frmTip). Put the following code in the load event of your form. Private Sub Form_Load() ' Set the visible property of txtItem to false txtItem.Visible = False 'Add items to list1 (lstDraggedItems) lstDraggedItems.AddItem "Apple" lstDraggedItems.AddItem "Orange" lstDraggedItems.AddItem "Grape" lstDraggedItems.AddItem "Banana" lstDraggedItems.AddItem "Lemon" ' End Sub In the mouseDown event of the list lstDraggedItems put the following code: View Source In the dragDrop event of the list lstDroppedItems put the following code: View Source Now you can drag items from lstDraggedItems and drop them in LstDroppedItems. Note that you cannot drag from the second list to the first. Also, the dragged item remains in the first list. You'll have to address those limitations yourself. Top of Page For the Week of March 23, 1998 Creating a new context menu in editable controls By Antonio Almeida, future.systems@mail.telepac.pt This routine will permit you to replace the original context menu with your private context menu in an editable control. Add the following code to your form or to a BAS module: View Source Next, use the Visual Basic Menu Editor and the table below to create a simple menu. Caption Name Visible Context Menu mnuContext NO ...First Item mnuContext1 ...Second Item mnuContext2 Note that the last two items in the menu are indented (...) one level and that only the first item in the list ("Context Menu") has the Visible property set to NO. Now add a text box to your form and enter the code below in the MouseDown event of the text box. View Source Note: If you just want to kill the system context menu, just comment out the line: FormName.PopupMenu MenuName in the OpenContextMenu routine. Top of Page For the Week of March 16, 1998 Quick Custom Dialogs for DBGrid Cells By Mike Amundsen, mike@amundsen.com, http://www.amundsen.com It's easy to add custom input dialogs to all the cells in the Microsoft Data Bound Grid control. First, add a DBGrid control and Data control to your form. Next, set the DatabaseName and RecordSource properties of the data control to a valid database and table ("biblio.mdb" and "Publishers" for example). Then set the DataSource property of the DBGrid control to Data1 (the data control). Now add the following code to your form. View Source Now whenever you attempt to edit any cell in the DBGrid, you'll see the InputBox prompt you for input. You can replace the InputBox with any other custom dialog you wish to build. Top of Page For the Week of March 9, 1998 Using the Alias Option to Prevent API Crashes By Mike Amundsen mike@amundsen.com, http://www.amundsen.com A number of Windows APIs have parameters that can be more than one data type. For example, the WinHelp API call can accept the last parameter as a Long or String data type depending on the service requested. Visual Basic allows you to declare this data type as "Any" in the API call, but this can lead to type mismatch errors or even system crashes if the value is not the proper form. You can prevent the errors and improve the run-time type checking by declaring multiple versions of the same API function in your program. By adding a function declaration for each possible parameter type, you can continue to use strong data type checking. To illustrate this technique, add the following APIs and constants to a Visual Basic form. Notice that the two API declarations differ only in their initial name ("WinHelp" and "WinHelpSearch") and the type declaration of the last parameter ("dwData as Long" and "dwData as String"). View Source Now add two command buttons to your form (cmdHelpAbout and cmdHelpSearch) and place the following code behind the buttons. Be sure to edit the location of the help file to match your installation of Visual Basic. Private Sub cmdHelpAbout_Click() ' WinHelp Me.hwnd, HelpFile, HELP_HELPONHELP, &H0 ' End Sub Private Sub cmdHelpSearch_Click() ' WinHelpSearch Me.hwnd, HelpFile, HELP_PARTIALKEY, "option" ' End Sub When you press on the HelpAbout button, you'll see help about using the help system. When you press on the HelpSearch button, you'll see a list of help entries on the "option" topic. Top of Page For the Week of March 2, 1998 Increment and decrement dates with the [+] and [-] keys By Mike Coleman, Mike.Coleman@anixter.com If you've ever used Quicken, you've probably noticed a handy little feature in that program's date fields. You can press the [+] key to increment one day, [-] to decrement one day, [PgUp] to increment one month, and [PgDn] to decrement one month. In this tip, we'll show you how to emulate this behavior with Visual Basic. First, insert a text box on a form (txtDate). Set its text property to "" and its Locked property to TRUE. Now place the following code in the KeyDown event: View Source The one nasty thing about this is that if you have characters that are not the characters usually in a date (i.e., 1-9, Monday, Tuesday, or /) you get errors in the format command. To overcome this, I set the Locked property to True. This way, the user can't actually type a character in the field, but the KeyDown event still fires. Top of Page For the Week of Febuary 23, 1998 Creating Win32 region windows By AlMoataz B. Ahmed, AlMoataz_m@hotmail.com The Win32 API includes a really amazing feature called region windows. A window under Win32 no longer has to be rectangular! In fact, it can be any shape that may be constructed using Win32 region functions. Using the SetWindowRgn Win32 function from within Visual Basic is so simple, but the results are unbelievable. The following example shows a Visual Basic form that is NOT rectangular. Enjoy! ' This goes into the General Declarations section: Private Declare Function CreateEllipticRgn Lib "gdi32" _ (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, _ ByVal Y2 As Long) As Long Private Declare Function SetWindowRgn Lib "user32" _ (ByVal hWnd As Long, ByVal hRgn As Long, _ ByVal bRedraw As Boolean) As Long Private Sub Form_Load() Show 'The form! SetWindowRgn hWnd, _ CreateEllipticRgn(0, 0, 300, 200), _ True End Sub Top of Page For the Week of Febuary 16, 1998 Manipulate your controls from the keyboard By Narayana Vyas Kondreddi, vyas@aditi.com, http://members.tripod.com/~vyaskn/index.html If you're not comfortable using your mouse--or can't achieve the precise results you'd like--these tips will come in handy. First, you can resize controls at design time by using the [Shift] and arrow keys, as follows: SHIFT + RIGHT ARROW increases the width of the control SHIFT + LEFT ARROW decreases the width of the control SHIFT + DOWN ARROW increases the height of the control SHIFT + UP ARROW decreases the height of the control Note: The target control must have focus, so click on the control before manipulating it from the keyboard. Second, by using the [Control] key and the arrow keys, you can move your controls at design time, as follows: CONTROL + RIGHT ARROW to move the control to the right CONTROL + LEFT ARROW to move the control to the left CONTROL + DOWN ARROW to move the control downwards CONTROL + UP ARROW to move the control upwards If you select more than one control (by clicking on the first and shift-clicking on the others), the above procedures will affect all the selected controls. Top of Page For the Week of Febuary 9, 1998 Simple file checking from anywhere By Matthew Kent, mace@pacificcoast.net To keep my applications running smoothly, I often need to check that certain files exist. So, I've written a simple routine to make sure they do. Here it is: Public Sub VerifyFile(FileName As String) ' On Error Resume Next 'Open a specified existing file Open FileName For Input As #1 'Error handler generates error message with file and exits the routine If Err Then MsgBox ("The file " & FileName & " cannot be found.") Exit Sub End If Close #1 ' End Sub Now add a button to your form and place the code below behind the "Click" event. Private Sub cmdVerify_Click() ' Call VerifyFile("MyFile.txt") ' End Sub Top of Page For the Week of Febuary 2, 1998 Showing long ListBox entries as a ToolTip By Matt Vandenbush, matt_vandenbush@whbrady.com Sometimes the data you want to display in a list is too long for the size of ListBox you can use. When this happens, you can use some simple code to display the ListBox entries as ToolTips when the mouse passes over the ListBox. First, start a new Visual Basic project and add a ListBox to the default form. Then declare the SendMessage API call and the constant (LB_ITEMFROMPOINT) needed for the operation: Option Explicit 'Declare the API function call. Private Declare Function SendMessage _ Lib "user32" Alias "SendMessageA" _ (ByVal hwnd As Long, _ ByVal wMsg As Long, _ ByVal wParam As Long, _ lParam As Any) As Long ' Add API constant Private Const LB_ITEMFROMPOINT = &H1A9 Next, add some code to the form load event to fill the ListBox with data: Private Sub Form_Load() ' ' load some items in the list box With List1 .AddItem "Michael Clifford Amundsen" .AddItem "Walter P.K. Smithworthy, III" .AddItem "Alicia May Sue McPherson-Pennington" End With ' End Sub Finally, in the MouseMove event of the ListBox, put the following code: Private Sub List1_MouseMove(Button As Integer, Shift As Integer, _ X As Single, Y As Single) ' ' present related tip message ' Dim lXPoint As Long Dim lYPoint As Long Dim lIndex As Long ' If Button = 0 Then ' if no button was pressed lXPoint = CLng(X / Screen.TwipsPerPixelX) lYPoint = CLng(Y / Screen.TwipsPerPixelY) ' With List1 ' get selected item from list lIndex = SendMessage(.hwnd, _ LB_ITEMFROMPOINT, _ 0, _ ByVal ((lYPoint * 65536) + lXPoint)) ' show tip or clear last one If (lIndex >= 0) And (lIndex <= .ListCount) Then .ToolTipText = .List(lIndex) Else .ToolTipText = "" End If End With '(List1) End If '(button=0) ' End Sub Top of Page Last Updated: 9/18/00 © 2000 Microsoft Corporation. All rights reserved. Terms of Use.