This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.

MSDN Magazine

Driving Visio 2000 from Visual Basic

Ken Spencer

Download the code for this article:Serving0102.exe (138KB)
Browse the code for this article at Code Center:Table Seating App

I

n the September 2000 issue of MSDN® Magazine I wrote about using Microsoft® Visio® to design Web applications. Judging by the mail I received, it generated lots of interest, so this month I want to revisit Visio from another perspective.
      Many applications require the ability to display things graphically. For instance, if you are in the business of hosting events, you may require seating charts from time to time. You can generate the charts by simply printing the information on the charts in list format, using a tool like Visio to draw them by hand, or using Visio to generate the graphic charts automatically.
      This last approach is my favorite because it allows me to automate much of the process of creating easily understandable charts. To demonstrate this approach, I am going to walk through the use of Visio from Visual Basic® to create a table seating chart. The sample application is one that I originally wrote in Visual Basic 4.0 in 1995 and included in the book Client/Server Programming with Microsoft Visual Basic (Microsoft Press, 1995). I thought it would be interesting to rewrite this application with Visual Basic 6.0 and Visio 2000.
      The application takes information from a Microsoft Access database (Visitor2000.mdb, downloadable from the link at the top of this article). The database contains two tables. The Visitmaster table contains information about activities such as picnics, conferences, and such. The VisitorDemographics table is linked to Visitmaster by the VisitName field and contains information about the people who are registered for a particular visit. VisitorDemographics also includes each individual's sex, department name, and whether they requested wine with their meal.
      Let's move on to the code. Part of the application deals with pulling the data from the Microsoft Access database. The original code was written using Remote Data Objects (RDO). So, to expedite things, I pulled the RDO code and replaced it with ActiveX® Data Objects (ADO) code. I am not going to go through the database code here because it is standard ADO and almost everyone knows how to use ADO these days.
      Figure 1 shows the interface for the application. The three command buttons in the top left of the form do all the work with Visio. All the user does is select an event to chart from the dropdown list, then clicks the Set the Table button. Visio is initialized and performs the charting. Once Visio completes its work, the user can save or print the graphic file by clicking the appropriate button.

Figure 1 Creating Table Settings

Figure 1 Creating Table Settings

      When the user clicks the Set the Table button, the SetTheTable function is executed to actually create the drawing in Visio. The name of the visitor selected is passed to the procedure from the cboVisitorList. Once the function completes, the EnableCmdButtons function is executed to turn on all three buttons in the control array.

Figure 2 Table Setting in Visio

Figure 2 Table Setting in Visio

      Figure 2 shows what the result should look like in Visio. The resulting Visio graphic depicts a table and arrangement of chairs. Each chair is labeled with the person's name, department, and whether or not they want wine. Also, each person is seated beside a person of the opposite sex, the value of which is indicated by the graphic in the chair.

Working with Visio

      The modTableSetting module contains the methods that do most of the work in this application. The declaration section is where I placed all the properties defined as objects in the application. Putting them in one place makes it easy to find them during the maintenance phase of the application.
      I'll skip many of the gory details here, but I do need to cover several of the definitions. Notice that all the objects used for the Visio-related objects are preceded by comment lines explaining their use.
      The VisitorType is a user-defined type that holds the information required for one user record. The following user-defined type definition is used to describe a visitor record. The application stores the demographics (Name and Department) and the indicator for wine:

Public Type VisitorType
Name As String
Department As String
Wine As String
End Type

      The MaleVisitors and FemaleVisitors arrays use this type to set up the tracking structure for each group. By using these arrays, the application splits up the males and females when each record is loaded from the database. This eliminates the need to do any fancy sorting and matching as the records are processed during the drawing process (in the SetTheTable function).
      The next two lines define the MaleVisitors and FemaleVisitors arrays. These arrays use the user-defined type VisitorType:
  Dim MaleVisitors(1 To 9) As VisitorType
Dim FemaleVisitors(1 To 9) As VisitorType

      The next line defines two counters that track the number of visitors. FakePre-c5b5af6a34944bee9a6c5f6ed7f2f11e-cb6e90de4b5241c19f13674bfc028605      SetTheTable is the major function for the application. Most of the important action takes place in this function or is driven from it. The first three statements shown here create variables that are used in the procedure: FakePre-88d6db8c266648b4b56364eb9d6894fe-60824d16081e405aae656c4c528f99bb      The following constants were set for use in calculating a SmartShapes location at either 0 or 45 degrees. The multiplication by 12 is used to convert the value to inches. FakePre-3f72668066974fa787dcf46caa7258c1-c96decce2d8746e2a4cee724538fce9b      The next two lines complete the definitions for this function by defining a variable to hold the event description and another variable to the application path: FakePre-7f95b532829d4fb28828ca2c6ea66ca4-aa30e8c5c743483cbce600fd5cb596ca      The next several lines of code set the positioning for various objects in the drawing.First, the following two lines establish the center of the table in the Y and X directions: FakePre-f7508409b8c945fca4ce6c1e721b2b38-a1304c93fabf4259b25ed21eae9eca14      Then, the XPos and YPos variables will dictate where each object is placed. Here, the XPos and YPos variables are set to their default values: FakePre-6384b3228c0e4185a4616c7e095a0c22-8f64d02b6ce54cac8acfa5ce4f9a205b      Notice the use of the frmSetTable.labStatus control. This is the status label control on the form where I track the actions of the ActiveX Server during the Set the Table process. FakePre-8114143ba61741b79ebdd1d6b1d9004f-9fca8140d6464492809595015fa5f9d7      You may notice a DoEvents statement here or there in the code. This is primarily to allow the application to process the updates to the label control as certain things execute in the application. If you don't use DoEvents after setting a control, you may not see the update take place until the process on which you are trying to provide feedback completes.
      Next, the LoadVisitorTables function is called and passed the VisitName.
LoadVisitorTables VisitName

This causes LoadVisitorTables to load the visitors from the database into the two arrays.
      Next, the GetDescription function is called to return the visit description from the database.
VisitDescription = GetDescription(VisitName)
frmSetTable.labStatus = "Starting Visio"

      At this point, I start Visio with the StartVisio method. Then I reset the error handler to none. FakePre-5691c4465d674d06a63456ec93242c42-ee9f28615f124376990a49c53e94adbc      The next step sets the sPath variable to the current path for the application and appends to it the name of the Visio template I'm using: FakePre-f72318b08e2247918632460c92e70cfe-74a79371b1944c349245ba9b6a5a7bcd      The next line adds a new drawing to Visio using the Add method. The drawing is based on the template TableSetting.vst. FakePre-11e46dc44d284b79850184f5a6b93a1e-5ac4f0a6c5f54c0d93e62a9b8b0cfdb4      The next line creates a reference to the stencil used in the drawing. FakePre-6406d183c8354f0086202d7430b0525a-491588cbae7244b1a61cd5dde85e220e      After the drawing is created, the code sets objDocument to the ActiveDocument property to point to the new drawing. FakePre-7a3ab3513ec7437ba0c967171c46a4a2-5d9ea7c0002a4085a3f126d4c12a2672The objDocument is not destroyed when the function completes because it may be used by other parts of the application.
      The following statement is commented out in my sample. In a production system, I would enable this statement in order to turn off the Visio interface while I place the objects. This will drastically improve the performance of the application as Visio will not do any visible drawing until I finish the routine.
'Visio.ScreenUpdating = False

      Next, the code can begin placing objects on the drawing. The first task is to place a table in the center of the drawing. The stTemp property is used to hold the definition of the name of the master shape for my table. The next statement creates an object reference to the master shape (in the Masters collection) using this variable: FakePre-23830736ba204ecfa57d18fb382f05b5-e41f05f290444a0da68b88057ae98d49      The Drop method actually places the object in the drawing: FakePre-35aa7a30d9d3405c8ad948b9b84b4f80-9c686c857cb84106b095b1c8b90710f0      The next bit of code adds the title for the drawing using the same process I used for the table. Notice that 7 feet (converted to inches) is added to the YCenter value to place the title at the top of the drawing. The Text property is set in the last statement to actually change the text of the title shape. FakePre-c862e39a11bd4ef490fdf85cbf3894ac-55755d3fa96d4559b1c84ca291f99d29      The next few lines of code initialize various counters. FakePre-233030badc7b460e93612119c2f1f937-286d51666c2e4f4383c4e43904738845      The While loop loops through each visitor for a table. FakePre-810dfeda30c8469ca73d46bde1594953-ebe72b2f67d74ef9ba981e42b1218466      The Select Case statement determines the X and Y position for an individual based upon the seating position around the table. This is where the Degree constants are used to locate the individuals (see Figure 3).
      The If statement checks the sCurrentSex variable to determine if the person is male or female, then sets the variables for that individual (see Figure 4).
      Next, I create a reference to the appropriate icon for each visitor based upon their sex. The stSex property has been previously set to either Men or Women. As you might expect, the two shapes are named Men and Women, respectively. The Drop and Text properties are used, as in previous statements, to place the shape and set its title.
If Len(sCurrentName) > 0 Then
Set shMaster = objTableSettingStencil.Masters(stSex)
Set Shape = objTableDiagram.Drop(shMaster, _
XPos, YPos)
stLabel = sCurrentName & " - " & sCurrentDepartment
stLabel = stLabel & " (" & sWine & ")"
Shape.Text = stLabel

      The next few lines of code set the text of the shape to bold, excluding the "Wine" string. FakePre-b102a930219f4e91873327b0f8240992-9c17f07faca74d50bc3446cb0add4f5d      The last few lines in the function perform cleanup activities: FakePre-4a7550df429e4bfc9bf8366c1452ef2e-f7374fae8e824c90a0a0c9b325cd6b46      As mentioned earlier, the StartVisio method is used to actually start Visio. Encapsulating a function (such as starting an ActiveX server) in a separate method is good programming practice. By separating this code, you can perform whatever error checking is required and never have to worry about cluttering up another method with code specific to your task. FakePre-fe27aff6c468433c89c06c5084c9e63d-cb36ec95a16147b6afc17784e283bd96      If Visio is already running, the next statement will create the Visio object reference that you need. If Visio is not running, then an error occurs and continues to the next statement. The If statement will pick up the error by checking the Err property, then use the CreateObject statement to start Visio and provide the object reference. FakePre-b8b4b707e33b4e11aaa7ade87974613a-990e9d638e0b48f5829ccaa8be07d3d0      The cmdSetTable_Click event in the form handles the processing of all three buttons that interact with Visio. I have already explored how the Set the Table button works, but let's take a quick look at saving and printing documents created with Visio. When a user clicks the Save the Table button, the code sets the path for the new document and then calls the SaveAs method of the ActiveDocument object: FakePre-0f4696cc179d41dcad25a42b4d0b8b24-24cdcd8b783947b793d1522e37ee8eb3      When the user clicks the Print the Table button, the code selects the page to print (the first page) and calls the Print method of the Page reference returned to the Pages collection: FakePre-be6f99b9d39e4e28900ea1b841c94576-5d602a96639347988e2b796c47e75554      So far I have covered the major points in the code. When a user runs this application, he can let it create the drawing and do all the work. He can also switch to Visio after the drawing has been created and modify it himself. This makes publishing items from a database easy and quite flexible.

Conclusion

      One of the interesting side notes to this application is the Visio object model. As I mentioned, the application was originally written in 1995 using a much earlier version of Visio. To get it to work with Visio 2000, I did not need to change a single line of code! This speaks highly of the stability of the Visio object model and its evolution through time. This code also works because it uses late binding to get to Visio. I know late binding is slow, but this is a client application, not a server application with large numbers of users, so speed is not as critical.
      I checked the object references for Visio that showed up in Visual Basic. I found 14 objects, which give you lots of options for solving graphical problems programmatically with Visio.
      Before you use the sample code, you must change the SetTheTable function to work with all the items (individuals) pulled from the database. As is, the code only works with one table and eight or fewer individuals.

Ken Spencer works for the 32X Tech Corporation (https://www.32X.com), which produces a line of high-quality developer courseware. Ken also spends much of his time consulting or teaching private courses.

From the February 2001 issue of MSDN Magazine