Users of my application prefer to use datasheet view for browsing data. I'd like to provide a smart tag that will enable them to open forms and reports. How can I create a custom smart tag that will allow users to open a form that shows all orders for a customer as well as open a report that shows total sales for a customer?
If you want to provide conditional processing for smart tag actions then you must create a smart tag DLL, using Visual Basic 6.0 or Visual Basic .NET. In this solution, you'll see how you can use Visual Basic 6.0 to accomplish this.
|
Follow these steps to create the DLL project using Visual Basic 6.0:
Launch Visual Basic 6.0 and create a new DLL project. The sample application is named AccessSmartTag, and it includes one class, stActions. The stActions class provides the Actions interface that defines the smart tag actions you want to take.
Add the references shown in Figure 16-10. The reference to the Microsoft Smart Tags 2.0 Type Library is required. This example also has a reference to the Microsoft Access 11.0 Object Library so that you can work with Access objects from your smart tag code and the Microsoft DAO 3.6 Object Library so that you can work with data objects.
Place the following statements in the Declarations of the stActions class. You do not need a Recognizer interface for a smart tag that is designed to work exclusively with Access:
Option Explicit Implements ISmartTagAction Implements ISmartTagAction2
The next step is to implement the smart tag action interface by creating properties and methods that describe the smart tag action DLL. Most of these properties are fairly straightforward and just return a requested string. The ISmartTagAction_ProgId( ) is the language-independent unique identifier that corresponds to the ProgID of the DLL class. In this example, the name of the project is AccessSmartTag, and the class name is stActions:
Private Property Get ISmartTagAction_ProgId( ) As String
ISmartTagAction_ProgId = "AccessSmartTag.stActions"
End Property
The ISmartTagAction_Name property is a short phrase that describes the DLL:
Private Property Get ISmartTagAction_Name(ByVal lcid As Long) As String
ISmartTagAction_Name = "Demo Smart Tag Actions"
End Property
The ISmartTagAction_Desc property is a longer description of the DLL:
Private Property Get ISmartTagAction_Desc(ByVal lcid As Long) As String
ISmartTagAction_Desc = _
"This is a Sample SmartTag used to open Forms and Reports."
End Property
The ISmartTagAction_SmartTagCount property reflects the number of smart tag types. This example contains one smart tag, so the count is 1:
Private Property Get ISmartTagAction_SmartTagCount( ) As Long
ISmartTagAction_SmartTagCount = 1
End Property
Each smart tag type is defined by a namespace to keep it unique, which is defined in the ISmartTagAction_SmartTagName property. SmartTag type names are always in the format of namespaceURI#tagname. In this example, the (ismarttag = 1) condition isn't strictly necessary since there is only one smart tag type defined, but this shows a pattern you could use for handling multiple types:
Private Property Get ISmartTagAction_SmartTagName(ByVal ismarttag As Long) As String If (ismarttag = 1) Then ISmartTagAction_SmartTagName = _ "schemas-microsoft-com/smarttag/northwind#openform" End If End Property
The ISmartTagAction_SmartTagCaption property allows you to specify the caption that will be used:
Private Property Get ISmartTagAction_SmartTagCaption( _
ByVal ismarttag As Long, ByVal lcid As Long) As String
ISmartTagAction_SmartTagCaption = "Access Smart Tag Demo"
End Property
The ISmartTagAction_VerbCount is where you specify the number of verbs in the smart tag. In this example, there are two actions that the smart tag can take: opening a form or opening a report:
Private Property Get ISmartTagAction_VerbCount(ByVal bstrName As String) As Long
If (bstrName = "schemas-microsoft-com/smarttag/northwind#openform") Then
ISmartTagAction_VerbCount = 2
End If
End Property
Smart tag action clients will first ask action DLLs for a unique ID integer for each of the verbs it wants to support, passing in the name and ordinal number for each one. Generating the unique ID is totally up to the action DLL, which gives the action DLL more flexibility. For example, a smart tag action DLL can specify the same VerbID value for the same action across smart tag types, or it can use the same VerbID for similar variants of an action. In this example, the ISmartTagAction_VerbID property returns iVerb (the same ordinal number passed in) back to the action client as the unique ID:
Private Property Get ISmartTagAction_VerbID( _
ByVal bstrName As String, ByVal iVerb As Long) As Long
ISmartTagAction_VerbID = iVerb
End Property
The ISmartTagAction_VerbNameFromID property is used internally to represent the verb ID:
Private Property Get ISmartTagAction_VerbNameFromID(ByVal idVerb As Long) _ As String Select Case idVerb Case 1 ISmartTagAction_VerbNameFromID = "openCustomers" Case 2 ISmartTagAction_VerbNameFromID = "openReport" Case Else ISmartTagAction_VerbNameFromID = "" End Select End Property
The code in the ISmartTagAction2_VerbCaptionFromID2 property checks the VerbID and then uses the "///" syntax to get cascading menus in the smart tag. Figure 16-11 shows the results when the smart tag is accessed in the client application:
Private Property Get ISmartTagAction2_VerbCaptionFromID2( _ ByVal VerbID As Long, ByVal ApplicationName As String, _ ByVal LocaleID As Long, ByVal Properties As SmartTagLib.ISmartTagProperties, _ ByVal Text As String, ByVal Xml As String, ByVal Target As Object) As String If (VerbID = 1) Then ISmartTagAction2_VerbCaptionFromID2 = _ "Smart Tag Actions///Open Customer Form" ElseIf (VerbID = 2) Then ISmartTagAction2_VerbCaptionFromID2 = _ "Smart Tag Actions///Open Customer Report" End If End Property
The ISmartTagAction2_InvokeVerb2 method provides code to perform the actions that the smart tag takes. The first section of the code sets a variable to point to the Target, which is the Access control object passed in. If the smart tag is defined on a Table object instead of a form control, then Access creates a control under the covers that gets passed to the smart tag DLL:
Private Sub ISmartTagAction2_InvokeVerb2( _ ByVal VerbID As Long, ByVal ApplicationName As String, _ ByVal Target As Object, ByVal Properties As SmartTagLib.ISmartTagProperties, _ ByVal Text As String, ByVal Xml As String, ByVal LocaleID As Long) On Error GoTo HandleErr: Dim cb As Access.Control Set cb = Target
The next block of code validates that the control source is CustomerID. If not, a MsgBox statement provides feedback to the user that the smart tag only works when attached to the CustomerID. If the smart tag is attached to CustomerID, the code gets a reference to the Access.Application object from the Target's Application property:
If cb.ControlSource <> "CustomerID" Then MsgBox "This action only works if you run it on the Customer ID field.", _ vbOKOnly, "Smart Tag Error" GoTo ExitHere Else Dim app As Access.Application Set app = cb.Application End If
The code then branches based on VerbID. If the VerbID is 1, then the code sets a reference to the Application object's CurrentDb property to gain access to DAO objects. This allows the code to execute a query to obtain the total number of orders for a given CustomerID. This is then retrieved into a DAO Recordset, and passed to the Customers form as an OpenArgs argument:
If VerbID = 1 Then Dim db As DAO.Database Dim rs As DAO.Recordset Set db = app.CurrentDb Dim strQry As String Dim strOrders As String strQry = "SELECT Count(*) AS NumOrders FROM Orders WHERE CustomerID='" _ & cb.Value & "';" Set rs = db.OpenRecordset(strQry) If Not rs.EOF Then strOrders = "Total number of orders: " & rs!NumOrders Else strOrders = "No orders for this customer" End If rs.Close app.DoCmd.OpenForm "Customers", _ WhereCondition:="[CustomerID] = '" & cb.Value & "'", _ OpenArgs:=strOrders
If the second action is chosen, then the code opens rptCustomers report, passing the CustomerID as the WhereCondition argument (without this WhereCondition, the report would open displaying all the customers):
ElseIf VerbID = 2 Then app.DoCmd.OpenReport "rptCustomers", _ View:=acViewPreview, _ WhereCondition:="[CustomerID] = '" & cb.Value & "'" End If
The error handling code is mainly useful for debugging. It displays any error information in a MsgBox statement:
ExitHere: Exit Sub HandleErr: MsgBox Err.Number & " " & Err.Description, _ vbCritical, "Error in AccessSmartTag.ISmartTagAction2_InvokeVerb2" Resume ExitHere End Sub
Once you've written the code, build the DLL project by choosing File Make AccessSmartTag DLL from the menu. This will create the correct registry entries. Launch regedit from the Windows Start Run menu. To obtain the CLSID for the action handler, navigate to the following node in the Registry:
HKEY_CLASSES_ROOT\AccessSmartTag.stActions\Clsid
Double-click the Clsid node to obtain the value, as shown in Figure 16-12. Copy it to the clipboard and close the regedit window without saving.
You can then edit the registry directly or create a reg file to update the registry entries. Use Notepad to create a new file and name it Reg_AccessSmartTag.reg. The file should contain the following text. However, you will need to replace the value shown in the curly braces with the value that you copied to the Clipboard from the Registry in the previous step:
Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\Software\Microsoft\Office\Common\Smart Tag\Actions\ {9F7503BB-4BBA-4A4A-B1A5-A0DF0A0187F5}]
In case you ever need to unregister the smart tag, create a second file named Unreg_AccessSmartTag.reg. The file should contain the following text. Again, replace the value shown here in the curly braces with the value copied to the Clipboard:
Windows Registry Editor Version 5.00 [-HKEY_CURRENT_USER\Software\Microsoft\Office\Common\Smart Tag\Actions\ {9F7503BB-4BBA-4A4A-B1A5-A0DF0A0187F5}]
Save both files and double-click Reg_AccessSmartTag.reg. This will create the entries in the registry so that Access can recognize the smart tag actions.
Open the 16-07.MDB sample database and open the Customers form in design view. View the code in the form's Open event, which displays anything passed in the OpenArgs event in the form's Caption property:
Private Sub Form_Open(Cancel As Integer) ' Display any OpenArgs in the Caption Dim str As String str = Me.OpenArgs & "" If Len(str) > 0 Then Me.Caption = str End If End Sub
Close the form and open the Customers table in design view. Assign the smart tag to the CustomerID field, as shown in Figure 16-13.
Save the table and view it in datasheet view. When you choose the first smart tag action, the Customers form will open with the total number of orders for the selected customer displayed in the form's Caption. If you choose the second smart tag action, then the rptCustomers report will open displaying sales data for the selected customer.
You can write a smart tag DLL in any language that supports writing COM add-ins. You can also write a smart tag DLL in managed (.NET) code.
There are two interfaces involved in implementing smart tag actions: the ISmartTagAction interface and the ISmartTagAction2 interface. These interfaces provide the client application with the information needed to support smart tag actions. The ISmartTagAction interface is compatible with Office XP, and the ISmartTagAction2 interface is specific to Office 2003, and allows you to tap into new functionality.
|
The role of an ISmartTagAction interface is to provide actions for individual smart tag types. Each smart tag type is defined by a namespace URI plus its tag name to keep it unique. A "#" character is appended to the namespace URI and is used to separate the namespace URI from its tag name, as shown in this example, where "schemas-microsoft-com/smarttag/northwind" is the namespace URI and "openform" is the tag name. The combination results in the fully qualified name of the smart tag type. The URI portion of the property name ensures that it is globally unique and unambiguous, so that two tags with the same tag name (openform) can be differentiated:
Private Property Get ISmartTagAction_SmartTagName( _
ByVal ismarttag As Long) As String
ISmartTagAction_SmartTagName = _
"schemas-microsoft-com/smarttag/northwind#openform"
End Property
The most interesting part of the sample smart tag DLL is that it shows you how you can work with the Access object model as well as DAO. The code in the ISmartTagAction2_InvokeVerb2 method has an input parameter, Target As Object, which Access uses to pass in the control that has the smart tag attached. The code then creates an Access.Control variable that references the Target:
Dim cb As Access.Control Set cb = Target
Once you have the Access Control object, you can then set a variable to point to the Access Application object, giving you full access to any part of your application:
Dim app As Access.Application Set app = cb.Application
From there, you can work with the data in your application by creating a DAO Database object using the Application object's CurrentDb property:
Dim db As DAO.Database Set db = app.CurrentDb
The code goes on to open a Recordset based on a query that counts the total number of orders for the selected customer, writing it to a String variable. It then opens the form to display the selected customer and passes that count value in the OpenArgs argument of the Application object's DoCmd.OpenForm method:
app.DoCmd.OpenForm "Customers", _
WhereCondition:="[CustomerID] = '" & cb.Value & "'", _
OpenArgs:=strOrders
When the Customers form opens, the code in the Open event evaluates whether any data has been passed in the OpenArgs argument, and then displays that information in the form's Caption property. If the form is opened normally without any OpenArgs data being passed to it, then the default caption is displayed:
Private Sub Form_Open(Cancel As Integer) Dim str As String str = Me.OpenArgs & "" If Len(str) > 0 Then Me.Caption = str End If End Sub
The code for opening a report uses similar techniques. Creating a smart tag DLL allows you full access to the entire Access object model, and allows you to create conditional logic for your smart tag. Smart tags can be a good way to provide extra functionality for users who prefer working in datasheet view.
See the Preface for more information on working with DAO to access data.