Lesson 3: Data Input [Visio 2003 SDK Documentation]
The Microsoft Visual Basic® .NET project files that accompany this lesson can be found at \Samples\Tutorial\VBNet\Lesson3.
Table of contents
In this lesson we extend the COM add-in from Lesson 2: Event Handling to invoke a wizard that gathers user preferences for creating a simple flowchart. Through the wizard, the user can determine the number of flowchart shapes to draw and modify the shape properties. We extend the DocumentCreator class from the previous lesson so that the COM add-in creates a DocumentCreator object using the wizard data.
We demonstrate how to extend the COM add-in to get user input from a wizard implemented with Microsoft® Windows® Forms, and create a flowchart.
This lesson shows you how to:
- Manage shape properties selected in the wizard through a custom ShapeProperties class and store them using the ArrayList class.
- Implement the TutorialAddin wizard using Windows Forms.
- Use the AlertResponse property to determine if the wizard and messages should be shown.
- Display shape properties using the DataGrid class and allow the user to edit shape properties.
- Add flowchart shapes to a page and and connect them.
- Apply shape properties entered in the wizard to the shapes in the flowchart drawing.
To see the implementation of the sample application for this lesson, open the solution using Microsoft Visual Studio® .NET. The solution is found in the Lesson3 folder and is named Lesson3.sln.
As shown in Lesson 2, the TutorialAddin sample responds to the user creating a new Visio document based on the Managed Tutorial.vst template. However, instead of simply drawing a single shape as we did in Lesson 2, we have extended the sample code to invoke the TutorialAddin wizard.
The wizard walks the user through the following sequence of steps:
- It asks the user whether to create a 4- or 8-shape (representing the steps) flowchart.
- It displays the default shape properties.
By selecting cells in the displayed grid and editing the values, the user can modify these properties.
- It displays a summary page when the user has finished selecting the shape properties.
When the user clicks the Finish button, the COM add-in draws the flowchart.
Checking Alert Response to Determine if the Wizard and COM Add-in Messages Boxes Should Appear
Visio includes a property, AlertResponse, that can be set to control whether Visio shows a modal user interface. The non-zero values returned from the AlterResponse property correspond to standard windows constants such as IDOK, IDCANCEL, etc. Solutions that display message boxes, dialog boxes, or wizards should check this property before displaying these items in the Visio user interface.
Before displaying the wizard, the COM add-in checks the AlertResponse property. If this property is 0, the wizard is shown. If the AlertResponse property returns IDOK (1) or IDYES (6), the drawing creation functionality continues to use the defaults from the wizard. If the property is set to any other value, no drawing creation occurs.
In this sample, most of the calls to the MsgBox function have been updated to check for the AlertResponse property before displaying the message. As part of that change, there is a new helper function in Shared.vb called DisplayException. This method is used to display errors in a message box if the AlertResponse is 0, or the Debug window if it is not equal to zero. This method is called in some of the sample code below.
Managing Shape Properties
We will need to manage two pieces of information about the flowchart that the COM add-in will draw: the number of shapes, and the properties for each shape.
We will store this information in private members of the Wizard class, as shown in the following declarations:
Private shapeCountValue As Integer = 0 Private shapeListArray As New ArrayList()
The values in these private members represent the form state for the wizard. In order to maintain this state information, the DocumentCreator class instance is given a reference to a Wizard object when it is created. The Wizard object displays the wizard and holds the form state information. The Wizard class exposes two public properties, ShapeCount and ShapeList, that allow access to the wizard data.
The shape properties are stored using the ArrayList class. ArrayList is a smart array, which means it dynamically grows and shrinks as elements are added or removed from it. If we want to add a new element to the ArrayList, we call the Add method. The ArrayList makes it easy to bind the shape properties to the DataGrid control for display to the user. Note that there is no need to declare the type of the elements stored in our ArrayList member because ArrayList can contain heterogeneous objects.
For the shape properties that the wizard allows the user to modify, we create a ShapeProperties class. Each element of the shapeList member will be a ShapeProperties object. The ShapeProperties class contains five private members, representing the shape master, fill color, line color, shape text, and hyperlink, as shown in the following declarations:
Private shapeTypeString As String Private fillColorString As String Private lineColorString As String Private textString As String Private hyperlinkString As String
The ShapeProperties class exposes public properties to get and set these values. It also has a constructor that allows you to initialize all the members. This allows us to initialize a default flowchart as follows:
Dim shapeIndex As Integer shapeCountValue = 4 ' Initialize shape properties. For shapeIndex = 1 To ShapeCount shapeListArray.Add(New ShapeProperties(PROCESS_MASTER, _ COLOR_WHITE, COLOR_BLACK, "Add shape text", _ "Add shape hyperlink")) Next
Using Windows Forms to Build the TutorialAddin Wizard
Now that we have a way to store the information for customizing the flowchart, we can build the TutorialAddin wizard. Each page of the wizard is a separate Form class, which is declared as follows:
Imports System.Windows.Forms Public Class Screen1 Inherits Form
Inheriting from the Windows Forms base class gives us access to a wide array of form controls for customizing our wizard pages. The first wizard page contains two RadioButton controls, which allow the user to specify a 4- or 8-step flowchart.
To display this wizard page, we use the following code:
Dim screen As Form Dim result As DialogResult result = screen.ShowDialog()
The ShowDialog method performs these tasks:
- Makes the wizard modal with respect to Visio.
- Allows the wizard to determine which button the user clicked (see the topic Navigating the wizard pages).
- Displays the form (Figure 1).
In order to prevent the wizard form from appearing in the task pane, the form property ShowInTaskbar is set to False.
Figure 1. First page of the TutorialAddin wizard allows the user to specify a 4- or 8-step flowchart
To affect the shapeCountValue member of the Wizard object, we need to pass a reference of the object to the form. This is done by adding a parameter to the Screen1 class's New operator as follows:
Public Sub New(ByVal wizard As Wizard)
To detect the user's selection, we listen for the Click event on the NextButton button. When the user makes a selection, we use the public properties on Wizard to modify its state.
The following code in the Click event handler examines the Checked property of the radio button and modifies the shape properties depending on whether the user has chosen 4 or 8 shapes:
If Shapes4RadioButton.Checked Then If wizardObject.ShapeList.Count > 4 Then wizardObject.ShapeList.RemoveRange(4, 4) End If wizardObject.ShapeCount = 4 Else Dim addShapes As Integer = 0 If wizardObject.ShapeList.Count < 8 Then For addShapes = 1 To 4 wizardObject.ShapeList.Add(New ShapeProperties( _ PROCESS_MASTER, COLOR_WHITE, COLOR_BLACK, _ "Add shape text", "Add shape hyperlink")) Next End If wizardObject.ShapeCount = 8 End If
Displaying Shape Properties
Now that we know the number of shapes, we can start modifying the shape properties. We can use the Windows Forms DataGrid control to display our five customizable shape properties to the user.
Choosing the ArrayList class to store our shape properties makes it easy to bind the shape properties data to the DataGrid control, as shown in the following code located in Screen2_LoadEventHandler:
Dim tableStyle As New DataGridTableStyle() Dim ShapeTypeColumn As New DataGridComboBoxColumn() Dim ShapeTextColumn As New DataGridTextBoxColumn() Dim FillColorColumn As New DataGridComboBoxColumn() Dim LineColorColumn As New DataGridComboBoxColumn() Dim HyperlinkColumn As New Windows.Forms.DataGridTextBoxColumn() ' Create a custom tableStyle for column display. DataGrid1.DataSource = wizardObject.ShapeList() tableStyle.MappingName = "ArrayList" ' Display shape type in a drop-down list ShapeTypeColumn.MappingName = "ShapeType" ShapeTypeColumn.HeaderText = "Type" ShapeTypeColumn.ColumnComboBox.Items.Clear() ShapeTypeColumn.ColumnComboBox.Items.Add(PROCESS_MASTER) ShapeTypeColumn.ColumnComboBox.Items.Add(DOCUMENT_MASTER) ShapeTypeColumn.ColumnComboBox.Items.Add(TERMINATOR_MASTER) ShapeTypeColumn.ColumnComboBox.DropDownStyle = _ ComboBoxStyle.DropDownList tableStyle.PreferredRowHeight = _ (ShapeTypeColumn.ColumnComboBox.Height + 3) tableStyle.GridColumnStyles.Add(ShapeTypeColumn) ' Display shape text in an edit box ShapeTextColumn.MappingName = "Text" ShapeTextColumn.HeaderText = "Text" ShapeTextColumn.TextBox.MaxLength = 30 tableStyle.GridColumnStyles.Add(ShapeTextColumn) ' Display fill color in a drop-down list FillColorColumn.MappingName = "FillColor" FillColorColumn.HeaderText = "Color" FillColorColumn.ColumnComboBox.Items.Clear() FillColorColumn.ColumnComboBox.Items.Add(COLOR_WHITE) FillColorColumn.ColumnComboBox.Items.Add(COLOR_BLUE) FillColorColumn.ColumnComboBox.Items.Add(COLOR_YELLOW) FillColorColumn.ColumnComboBox.Items.Add(COLOR_GREEN) FillColorColumn.ColumnComboBox.DropDownStyle = _ ComboBoxStyle.DropDownList tableStyle.GridColumnStyles.Add(FillColorColumn) ' Display line color in a drop-down list LineColorColumn.MappingName = "LineColor" LineColorColumn.HeaderText = "Line color" LineColorColumn.ColumnComboBox.Items.Clear() LineColorColumn.ColumnComboBox.Items.Add(COLOR_BLACK) LineColorColumn.ColumnComboBox.Items.Add(COLOR_BLUE) LineColorColumn.ColumnComboBox.Items.Add(COLOR_YELLOW) LineColorColumn.ColumnComboBox.Items.Add(COLOR_GREEN) LineColorColumn.ColumnComboBox.DropDownStyle = _ ComboBoxStyle.DropDownList tableStyle.GridColumnStyles.Add(LineColorColumn) ' Display hyperlink in an edit box HyperlinkColumn.MappingName = "Hyperlink" HyperlinkColumn.HeaderText = "Hyperlink" HyperlinkColumn.TextBox.MaxLength = 255 tableStyle.GridColumnStyles.Add(HyperlinkColumn) DataGrid1.TableStyles.Clear() DataGrid1.TableStyles.Add(tableStyle)
Each GridColumnStyles item that we add to the DataGrid control allows us to specify the header text and the order that the columns are displayed in.
Note how the MappingName property corresponds to each public property that we expose in the ShapeProperties class. The following figure shows the second page in the wizard.
Figure 2. Displaying shape properties
On the second wizard page, we need to detect more than the standard Cancel or OK return value, so we implement a WizardResult property for the form. The IWizardResult interface defines this property, which is implemented by each wizard form.
The WizardResult property returns the following additional return values, defined in the WizardResults enumeration:
Public Enum WizardResults buttonNone = 100 buttonNext1 = 101 buttonBack2 = 102 buttonNext2 = 103 buttonBack3 = 104 buttonFinish = 105 End Enum
In the form, we set the private member wizardResultValue, whose value is returned by the WizardResult property, in the buttons Click event handler:
Private Sub BackButton_ClickEventHandler( _ ByVal sender As System.Object, _ ByVal eventData As System.EventArgs) _ Handles BackButton.Click wizardResultValue = WizardResults.buttonBack2 End Sub
Because each wizard form implements the IWizardResult interface, we can define a screen variable of the generic Form type and use polymorphism. We set screen to the derived form type depending on which wizard screen is currently shown. Then we cast screen to the IWizardResult interface type and check the return value. The Wizard class's GetShapeData method drives the wizard navigation process as follows:
Dim screen As Form Dim result As DialogResult Dim wizardResult As WizardResults Dim wizardScreen1 As New Screen1(Me) Dim wizardScreen2 As New Screen2(Me) Dim wizardScreen3 As New Screen3(Me) Try ' Show wizard. screen = wizardScreen1 Do result = screen.ShowDialog() If result = System.Windows.Forms.DialogResult.Cancel Then Return False Else wizardResult = CType(screen, IWizardResult).WizardResult End If Select Case wizardResult Case Is = WizardResults.buttonNext1 screen = wizardScreen2 Case Is = WizardResults.buttonBack2 screen = wizardScreen1 Case Is = WizardResults.buttonNext2 screen = wizardScreen3 Case Is = WizardResults.buttonBack3 screen = wizardScreen2 End Select Loop Until wizardResult = WizardResults.buttonFinish Return True Catch err As COMException DisplayException( _ alertResponse, _ "Exception in GetShapeData: " & _ err.Message) End Try
Editing Shape Properties
The DataGrid control allows the user to select a shape. When the user clicks a cell in the grid, the cells control is enabled for editing. For example, a drop-down list is displayed (Figure 3) that allows the user to edit the properties for the selected shape. This is implemented by overriding the DataGridTextBoxColumn controls Edit method as follows:
Protected Overloads Overrides Sub Edit(ByVal source As CurrencyManager, _ ByVal rowNumber As Integer, ByVal boundsRect As Rectangle, _ ByVal readOnly1 As Boolean, ByVal instantText As String, _ ByVal cellIsVisible As Boolean) MyBase.Edit(source, rowNumber, boundsRect, readOnly1, _ instantText, cellIsVisible) gridRowNumber = rowNumber sourceCurrencyManager = source ColumnComboBox.Parent = Me.TextBox.Parent ColumnComboBox.Location = Me.TextBox.Location ColumnComboBox.Size = New Size(Me.TextBox.Size.Width, _ ColumnComboBox.Size.Height) ColumnComboBox.Text = Me.TextBox.Text Me.TextBox.Visible = False ColumnComboBox.Visible = True ColumnComboBox.BringToFront() ColumnComboBox.Focus() End Sub
This code replaces the text box for the shape color, line color, and shape type cell with a drop-down list box implemented in DataGridComboBoxColumn.vb. The text box control is hidden and the list box is made visible, displaying the list of possible shape types. When the user selects a shape type from the list, the flowchart shape properties are modified by overriding the DataGridTextBoxColumn control's Commit method:
Protected Overloads Overrides Function Commit( _ ByVal dataSource As CurrencyManager, _ ByVal rowNumber As Integer) As Boolean If isEditing Then isEditing = False SetColumnValueAtRow(dataSource, rowNumber, _ ColumnComboBox.Text) End If
Figure 3. Editing shape properties
Now that the shape properties are set, we can display a wizard summary page as shown in Figure 4.
Figure 4. Wizard summary page
Drawing the Flowchart Shapes
Once the user is finished using the TutorialAddin wizard, the add-in is ready to draw the flowchart. To add the shapes, the CreateDrawing method iterates over the shapeList array, reading the master and dropping a shape of that type on the page in a random location as follows:
pinX = CInt(random.NextDouble() * 10) pinY = CInt(random.NextDouble() * 10) shapeProp = CType(shapeList(stepID - 1), ShapeProperties) shape = DropMasterOnPage(currentPage, _ shapeProp.ShapeType, _ TUTORIAL_STENCIL, pinX, pinY)
Note how we have to perform an explicit cast to the ShapeProperties type. This is because the ArrayList class stores its elements as generic objects.
The CreateDrawing method passes the following parameters to the DropMasterOnPage method: the name of the flowchart stencil, the master as specified by the user, and coordinates for dropping the shape. We use random coordinates because we will lay out and center the flowchart after all shapes are drawn and connected.
Now we can apply the remaining shape properties. Reading from the ShapeProperties element for each shape, we can set each property for the current shape. For example, the following code sets the shape text:
currentshape.Characters.Text = shapeProp.Text
Once each shape is created, we connect the shapes that represent the flowchart steps. The CreateDrawing method calls the ConnectWithDynamicGlueAndConnector procedure:
' Connect previous step to the one just created. If stepID > 1 Then ' Connect the two shapes. ConnectWithDynamicGlueAndConnector( _ prevShape, shape) End If ' Keep reference to previous shape for connecting steps. prevShape = shape
The ConnectWithDynamicGlueAndConnector procedure accesses the flowchart stencil and its Dynamic Connector master. By holding a reference to the previous shape, the CreateDrawing method connects each new shape that it drops on the page to the previous shape.
Lesson3 provides a quick way of designing a flowchart. Data is gathered using the Wizard class and its associated forms and displayed on the drawing page using the DocumentCreator class.
The TutorialAddin sample developed in this lesson has a high degree of functionality and can be customized easily for your applications. We will make one last change to the sample, but not a functional one.
In Lesson 4: Performance, we will improve the performance of the drawing code.