| < Free Open Study > |
|
One of the most useful features that an add-in can provide is the capability to clone objects, including procedures. In this section, you will enhance your add-in once again, with probably the most useful tool yet! You will use the add-in that you have been building throughout the book thus far.
First, you need to add another menu item to the TreeView. Load up the MyAddinTest1 solution and open the menu form (Form1.vb). Right-click the TreeView control and select Properties from the pop-up menu. The property window should be displayed for the TreeView control. Click the Nodes property and then click the ellipsis to open the TreeNode Editor. Click the top-level node (Smart Desktop) of the TreeView. Finally, click the AddChild button and type Clone Procedure into the Label box. Click the OK button to close the designer. If you open the TreeView by clicking the plus sign (+) on the top-level node, you will see that you have added your new menu item.
Next, you need to add a handler for the new menu option. To do so, go to the code for Form1.vb and add the code in Listing 5-12 to the tvMenu_AfterSelect event handler. Add the two boldface lines just before the Case Else statement in the event handler.
Listing 5-12: Adding to the AfterSelect Event
Private Sub tvMenu_AfterSelect(ByVal sender As Object, ByVal e As _ System.Windows.Forms.TreeViewEventArgs) Handles tvMenu.AfterSelect Dim i As Integer Select Case UCase$(e.Node.Text) Case "SMART DESKTOP" 'ignore root clicks Case "BLOCK COMMENT" Call Connect.BlockComment() Case "UNCOMMENT" Call Connect.BlockUnComment() Case "BLOCK CHANGE" Call Connect.BlockChange() Case "BLOCK DELETE" Call Connect.BlockDelete() Case "PROC ERROR HANDLER" Call Connect.GenLocalErrorTrap() Case "CLONE PROCEDURE" Call Connect.CloneProcedure() Case Else MsgBox("Please click on a Child Node.", _ MsgBoxStyle.Information, "Unknown Request") End Select End Sub
You now need to add two new methods to the Connect class (Connect.vb). The first is named CloneProcedure, which is shown in Listing 5-13. When the add-in user selects a procedure to clone, the CloneProcedure method will retrieve the selected procedure from the active document. CloneProcedure will call GetCodeFromWindow to get the selected block from the active window. After performing a cursory test to ensure that the user has selected a procedure, it calls a public Display function of a new form that you have yet to build. This form will display the selected procedure, and allow the user to change the name of the procedure and elect to either paste the new procedure to the current window or copy the new procedure to the Clipboard.
Listing 5-13: CloneProcedure
Shared Sub CloneProcedure() Dim s As String Dim i As Integer Dim rs As String Try ' get selected proc from active window s = GetCodeFromWindow() If InStr(1, s, " Sub ", 0) = 0 And _ InStr(1, s, " Function ", CompareMethod.Binary) = 0 Then MsgBox("Please select a whole Procedure to be cloned.", _ MsgBoxStyle.Exclamation) Exit Sub End If Dim oFrm As New CloneProc() rs = oFrm.Display(s) oFrm.Dispose() If rs <> "" Then AddMethodToEndOfDocument(rs) End If Catch e As System.Exception MsgBox("Error: " & e.Message, MsgBoxStyle.Critical, "Clone Procedure") Exit Sub End Try End Sub
You can actually call a function in a form, pass it some data, have it manipulate the data, and have it return the resulting data to the calling code. What this amounts to is logically calling a form! This is accomplished by the code that appears in bold in Listing 5-13. If the Display Function of the CloneProc form returns an empty string, it means that the user elected to copy the new procedure to the Clipboard. If there is something in the string, the user has elected to paste the new procedure to the end of the current document. This is accomplished by acall to AddMethodToEndOfDocument, passing the new procedure that has been returned in the string rs.
Next, you add the AddMethodToEndOfDocument method to the Connect class. This method was developed earlier and shown in Listing 5-11. The purpose of this method is to paste the new procedure at the end of current document (module).
Finally, you add a new form to the add-in project. You create the form, create a scrolling TextBox, and place two command buttons on the TextBox. Listing 5-14 contains the complete code for the form.
Listing 5-14: CloneProc Form Code
Option Strict On Public Class CloneProc Inherits System.Windows.Forms.Form #Region " Windows Form Designer generated code " Public Sub New() MyBase.New() 'This call is required by the Windows Form Designer. InitializeComponent() 'Add any initialization after the InitializeComponent() call End Sub 'Form overrides dispose to clean up the component list. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub 'Required by the Windows Form Designer Private components As System.ComponentModel.IContainer 'NOTE: The following procedure is required by the Windows Form Designer 'It can be modified using the Windows Form Designer. 'Do not modify it using the code editor. Friend WithEvents txtProcToClone As System.Windows.Forms.TextBox Friend WithEvents btnPasteToModule As System.Windows.Forms.Button Friend WithEvents btnCopyToClipboard As System.Windows.Forms.Button <System.Diagnostics.DebuggerStepThrough()> _ Private Sub InitializeComponent() Me.txtProcToClone = New System.Windows.Forms.TextBox() Me.btnPasteToModule = New System.Windows.Forms.Button() Me.btnCopyToClipboard = New System.Windows.Forms.Button() Me.SuspendLayout() ' 'txtProcToClone ' Me.txtProcToClone.Multiline = True Me.txtProcToClone.Name = "txtProcToClone" Me.txtProcToClone.ScrollBars = System.Windows.Forms.ScrollBars.Both Me.txtProcToClone.Size = New System.Drawing.Size(512, 229) Me.txtProcToClone.TabIndex = 0 Me.txtProcToClone.Text = "" ' 'btnPasteToModule ' Me.btnPasteToModule.Location = New System.Drawing.Point(389, 240) Me.btnPasteToModule.Name = "btnPasteToModule" Me.btnPasteToModule.Size = New System.Drawing.Size(115, 32) Me.btnPasteToModule.TabIndex = 1 Me.btnPasteToModule.Text = "&Paste To Module" ' 'btnCopyToClipboard ' Me.btnCopyToClipboard.Location = New System.Drawing.Point(263, 240) Me.btnCopyToClipboard.Name = "btnCopyToClipboard" Me.btnCopyToClipboard.Size = New System.Drawing.Size(115, 32) Me.btnCopyToClipboard.TabIndex = 2 Me.btnCopyToClipboard.Text = "&Copy To Clipboard" 'Label1 ' Me.Label1.Location = New System.Drawing.Point(16, 240) Me.Label1.Name = "Label1" Me.Label1.Size = New System.Drawing.Size(192, 24) Me.Label1.TabIndex = 3 Me.Label1.Text = _ "Change the name of the Method and click the desired button." ' 'CloneProc ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) Me.ClientSize = New System.Drawing.Size(514, 277) Me.Controls.AddRange(New System.Windows.Forms.Control() _ {Me.btnCopyToClipboard, Me.btnPasteToModule, Me.txtProcToClone}) Me.Name = "CloneProc" Me.Text = "CloneProc" Me.TopMost = True Me.ResumeLayout(False) End Sub #End Region Dim sTextSave As String Dim sOrigType As String Dim sOrigName As String Dim bFormLoading As Boolean Dim mbWait As Boolean Public Function Display(ByVal sText As String) As String Dim sTemp As String Dim sWord As String Dim i As Integer sTextSave = sText ' get the "Sub Name(" sTemp = Microsoft.VisualBasic.Left(sText, InStr(sText, "(") - 1) If InStr(sTemp, "Sub") > 0 Then sOrigType = "Sub" Else sOrigType = "Function" End If ' loop to get proc orig name ' when loop terminates, sOrigName is the name Do While Len(Trim$(sTemp)) > 0 sWord = Connect.GetToken(sTemp, "_") If Trim$(sWord) <> "" Then sOrigName = sWord Else Exit Do End If Loop Me.Show() mbWait = True Do While mbWait System.Windows.Forms.Application.DoEvents() Loop sTemp = sTextSave Return sTemp Return sTextSave End Function Private Sub UpdateFunctionReturns() ' If the procedure is a function, get the ' name and propagate it through the function ' also propagate any changes from the sub to a ' function and vice versa through the proc. Dim sWord As String Dim NextWord As String Dim sTemp As String Dim nL As Integer Dim sTemp2 As String Dim i As Integer Dim sNewName As String Dim sNewType As String Dim sLine As String Dim sTempLine As String Dim bFoundProcType As Boolean sTemp = Me.txtProcToClone.Text sTemp2 = "" nL = Connect.MLCount(sTemp, 0) For i = 1 To nL sLine = Connect.MemoLine(sTemp, 0, i) ' if Proc Def Line get new name, assumming it was changed If Not bFoundProcType And _ (InStr(sLine, "Sub ") > 0 Or _ InStr(sLine, "Function ") > 0) _ Then ' loop to get proc new name and new type ' when loop terminates, sNewName is the name bFoundProcType = True sTempLine = sLine Do While Trim$(sTempLine) <> "" sWord = Connect.GetToken(sTempLine, "") If sWord = "Sub" Then sNewType = "Sub" Exit Do ElseIf sWord = "Function" Then sNewType = "Function" Exit Do End If Loop sNewName = Connect.GetToken(sTempLine, "_") ElseIf bFoundProcType Then ' if the type changed, we must substitute the new type ' for the old type and change any functions name returns ' if new type is a function sLine = Replace(sLine, sOrigType, sNewType) sLine = Replace(sLine, sOrigName, sNewName) End If GetNextLine: ' write the output string sTemp2 = sTemp2 & sLine & vbCrLf Next Me.txtProcToClone.Text = sTemp2 End Sub Private Sub CloneProc_Load(ByVal sender As System.Object, ByVal e As _ System.EventArgs) Handles MyBase.Load Me.txtProcToClone.Text = sTextSave End Sub Private Sub btnCopyToClipboard_Click(ByVal sender As System.Object, _ ByVal e As _ System.EventArgs) _ Handles btnCopyToClipboard.Click Dim datobj As New System.Windows.Forms.DataObject() UpdateFunctionReturns() datobj.SetData(System.Windows.Forms.DataFormats.Text, txtProcToClone.Text) mbWait = False End Sub Private Sub btnPasteToModule_Click(ByVal sender As System.Object, ByVal e As _ System.EventArgs) Handles btnPasteToModule.Click UpdateFunctionReturns() sTextSave = Me.txtProcToClone.Text mbWait = False End Sub Protected Overrides Sub Finalize() MyBase.Finalize() End Sub Private Sub CloneProc_Closed(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Closed sTextSave = "" mbWait = False End Sub End Class
Two major methods of interest are in the form in Listing 5-14. The first is the Display function. This function facilitates the "calling" of the form from the CloneProcedure method of the Connect class. This function puts the passed parameter, in this case a procedure to be cloned, into the module-level variable sTextSave. The contents of sTextSave then gets placed into the TextBox of the form in the CloneProc_Load event. It then sets a Boolean, mbWait, to True. Finally, the method shows the form and loops in a DoEvents loop, waiting on mbWait to be changed to False. This Boolean will be set to false by either of the button handler events.
The second major method of interest is UpdateFunctionReturns. Once the user has changed the name of the procedure in the TextBox and one of the two buttons is clicked, the Display function breaks out of the wait loop. At that point, it calls the UpdateFunctionReturns method. This method loops through each line of code in the cloned procedure, replacing any occurrence of the original name of the procedure with the new name. You can even change the procedure from a Sub to a Function or vice versa.
After you add the new form and all of the new code previously described to the project, you run the add-in, as I have illustrated in previous chapters. In the second, or client, version of Visual Studio, select the WindowsApplication1 project. Next, select the procedure named MNZ in total, as shown in Figure 5-6.
Figure 5-6: Selecting the procedure to be cloned
Now, click the Clone Procedure node of the TreeView in the add-in's menu form. This will cause the CloneProc form to be loaded with the selected procedure displayed in its text box, as shown in Figure 5-7.
Figure 5-7: CloneProc form
Then change the name of the procedure from MNZ to MakeNullZero. Finally, click the Paste to Module button. The new procedure will be added to the end of the module as shown in Figure 5-8.
Figure 5-8: Cloned procedure
The new functionality enhances your add-in with a very useful feature. Of course, you can use your imagination and add to the functionality of the UpdateFunctionReturns method so that it will make adjustments to the return value in case the user changes from a Function to Sub, for example.
| < Free Open Study > |
|