The IExternalCommand interface can be implemented in addins to provide external commands for Revit to call directly or as callbacks for Revit ribbon buttons to trigger. The single core method in the IExternalCommand interface is the Execute() and it has to be implemented if we really want the external command to do something.
Here is an example implementation in VB.NET:
#Region "Namespaces"
Imports System.Text
Imports System.Linq
Imports System.Xml
Imports System.Reflection
Imports System.ComponentModel
Imports System.Collections
Imports System.Collections.Generic
Imports System.Windows
Imports System.Windows.Media.Imaging
Imports System.Windows.Forms
Imports System.IO
Imports Autodesk.Revit.ApplicationServices
Imports Autodesk.Revit.Attributes
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.DB.Events
Imports Autodesk.Revit.DB.Architecture
Imports Autodesk.Revit.DB.Structure
Imports Autodesk.Revit.DB.Mechanical
Imports Autodesk.Revit.DB.Electrical
Imports Autodesk.Revit.DB.Plumbing
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.UI.Selection
Imports Autodesk.Revit.UI.Events
Imports Autodesk.Revit.Collections
Imports Autodesk.Revit.Exceptions
Imports Autodesk.Revit.Utility
Imports RvtApplication = Autodesk.Revit.ApplicationServices.Application
Imports RvtDocument = Autodesk.Revit.DB.Document
#End Region
<Transaction(TransactionMode.Manual)> _
<Regeneration(RegenerationOption.Manual)> _
Public Class ExtCmd1
Implements Autodesk.Revit.UI.IExternalCommand
#Region "Cached Variables"
Private Shared _cachedCmdData As ExternalCommandData
Public Shared ReadOnly Property CachedUiApp() As UIApplication
Get
Return _cachedCmdData.Application
End Get
End Property
Public Shared ReadOnly Property CachedApp() As RvtApplication
Get
Return CachedUiApp.Application
End Get
End Property
Public Shared ReadOnly Property CachedDoc() As RvtDocument
Get
Return CachedUiApp.ActiveUIDocument.Document
End Get
End Property
#End Region
#Region "IExternalCommand Members"
Public Function Execute(ByVal cmdData As ExternalCommandData, ByRef msg As String, ByVal elemSet As ElementSet) As Result Implements Autodesk.Revit.UI.IExternalCommand.Execute
_cachedCmdData = cmdData
Try
'TODO: add your code below.
TaskDialog.Show("External Command (IExternalCommand) in VB.NET", "Hi, here is the external command ExtCmd1.")
Return Result.Succeeded
Catch ex As Exception
msg = ex.ToString()
Return Result.Failed
End Try
End Function
#End Region
End Class
Som commonly used namespaces of Revit API have been imported at the top of the source. Due to a problem with the VB.NET itself though, both in its IDE and compiler, as we discussed before, many times we still have to append full namespace prefixes to Revit types.
Another looking smart but actually stupid thing regarding VB.NET is that it will append the ‘Root namespace’ as specified in the Project Properties to all types defined in the project no matter whether they have some other namespaces specified or not in declarations.
For example, if we specify the project name, Revit2011x64VBAddinByVS2008, as the namespace for the ExtCmd1 class, as we normally do in C#, when we register the external command directly in the manifest file or hook it up with a button, some extra cautions have to be exercised. In our case, the following PushButtonData definition is not good:
New PushButtonData("item", "desc", assembly, "Revit2011x64VBAddinByVS2008.ExtCmd1")
Instead, the IExternalCommand implementation type in the PushButtonData should be specified this way:
New PushButtonData("item", "desc", assembly, "Revit2011x64VBAddinByVS2008. Revit2011x64VBAddinByVS2008.ExtCmd1")
Out of expectations of many people, isn’t it?
So VB.NET does not really support different namespaces for those types defined in a single project. And those source level namespaces can be thought as partial or second level namespaces.
That may explain why VB.NET has a project level Imported Namespaces stuff and C# does not, but considering the fact that VB.NET projects can reference in assemblies created by other languages such as C# and C++/CLI, the Imported Namespaces feature in VB.NET looks not so attractive.
To avoid such confusion, as a common practise in our Revit API wizards, coders, and widgets, newly created VB.NET types such as classes, interfaces, and enums will not have any extra namespaces specified in their source besides the hidden ‘Root namespace’ one.
Here is an example PushButton creation and the hook up code for the external command:
Dim cnt1Panel_grp0_item2Data As PushButtonData = New PushButtonData("cnt1Panel_grp0_item2", "PushButton2", assemFullName, "Revit2011x64VBAddinByVS2008.ExtCmd1")
Dim cnt1Panel_grp0_item2 As PushButton = CType(panel.AddItem(cnt1Panel_grp0_item2Data), PushButton)
cnt1Panel_grp0_item2.ToolTip = "A PushButton with the default image."
cnt1Panel_grp0_item2.LargeImage = BmpImageSource("Revit2011x64VBAddinByVS2008.ExtCmd32x32.bmp")
The TransactionMode and RegenerationOption have to be specified for external commands. We use Manual for both in this case. Automatic mode/option can be used for convenience but is not recommended.
The Execute() method implementation is straightforward. Just one reminder, please do not forget to add the Implements Autodesk.Revit.UI.IExternalCommand.Execute statement to the method and the VB.NET will complain about it anyway if we do not do so.
Let’s spend a moment on the ‘Cached Variables’ properties and explain what the good additions to the implementation are about.
They are all static or shared public properties. The CachedUiApp property caches the UIApplication instance carried by the ExternalCommandData argument; the CachedApp property caches the corresponding Revit Application; the CachedDoc the active Revit Document at the moment that the command is executed.
All these VB.NET code can be created through a few clicks by the RevitAddinWizard automatically.
Recent Comments