User Interface Development Index: BSWUserInterfaceDevelopment
The Command is one of the most basic building blocks in PowerPlatform. Without Commands, you couldn't open a file, save a file, start the Element Selection tool, etc. Commands are identified by two pieces of information: 1) an ID number (64-bits) and 2) the ID/Name of the Task owning that Command. When used in context, there is an additional command parameter string, or "unparsed" string, passed to the command's function. So in effect, these 3 pieces of information together form a unique command.
For the PowerPlatform core and MDL applications, Commands are defined in a CommandTable resource in a .r file that is compiled using the Bentley Resource Compiler. For .NET AddIns, Commands are defined in XML files.
This is an example command definition from cmdtable.r in the PowerPlatform core:
In the CommandTable definition above, notice the string "DIALOG". This is used as the first part of the generated C #define identifier for the CommandNumber. 119 is in the first column and it will be used in the actual numeric CommandNumber. CT_DIALOG ID is in second column on that same line. That ID is used for a subsequent CommandTable definition, which contains an entry with the string "OPENFILE", a 68 and CT_NONE. 68 will be used in the CommandNumber, and the CT_NONE indicates there are no more CommandTables to use when generating the CommandNumber. The following example shows how the hexidecimal represtations of the previous numbers are concatenated together to form the numeric CommandNumber.
Example CommandNumbers from the generated cmdlist.r.h command number header file:
Example command function setup:
To be thorough and not short change .NET AddIns, here is an example setup for AddIn commands:
IconCmds and Menus are two types of "entry points" that activate commands using CommandNumbers. IconCmds are actually made up of 3 separate resources. They contain the following:
An example of an IconCmd using the CMD_DIALOG_OPENFILE CommandNumber:
Menu items contain information similar to IconCmds, but with a few additions:
A reference to an IconCmd to get the icon information
In addition, a Feature Aspect ID check can surround the menu item definition to include or exclude the menu item for Power Products.
An example of a menu item using the CMD_DIALOG_OPENFILE CommandNumber:
In CONNECT edition, the Ribbon interface has been introduced. We're using the Telerik WPF RadRibbonView as the implementation of the ribbon control. To activate commands from the ribbon, we needed a new .NET-friendly way to get to PowerPlatform command information. IconCmds and Menu items already had duplicate information, and Ribbon Buttons would also need that same information. Instead of gathering the information from those existing resources, we decided to create a new resource that consolidates the duplicate information. The identifier for the new common resource is a name string. This new resource is called an XCommand because it's a common way of defining Cross-UI-Platform command information. The information includes:
The "Name / Id" is used to uniquely identify the XCommand and is used throughout PowerPlatform. Multiple XCommands are listed in a XCommandListRsc, which can be in core or in an application. The XCommands are quickly loaded during PowerPlatform 's start up and are cached.
The XCommand structures in dialogbox.r.h:
An example XCommand Name is "Mstn.File.Open". "Mstn.File.Open" is given the CommandNumber of CMD_DIALOG_OPENFILE from the "Ustn" task (MTASKID), which opens the file open dialog. The following is an example of an XCommand in a .r file:
The XCmdButton item is similar to an IconCmd or IconCmdX, which adds an item hook capability, but the XCmdButton refers to an XCommand. In a ToolBox or DialogItemList, use the XCmdButton item type instead of IconCmd.
The XCmdButton structures in dlogbox.r.h:
An example of an XCmdButton in a .r file:
Any application that contains XCommand resources should add itself to the MS_XCOMMAND_APPS configuration variable, which goes in the applicationload.cfg configuration file. This is accomplished by adding an entry into the <Part> for the application in its PartFile. The specific entry goes in this element hierarchy: <Part>, <Bindings>, <Files>, <LoadConfig>, ConfigVars ="...,MS_XCOMMAND_APPS" attribute.
An example of including MX_XCOMMAND_APPS in the ConfigVars attribute of the <LoadConfig> element:
The native API for XCommands is in the following interface and classes:
The following is an example of using the native XCommand API to activate/execute a command:
The PowerPlatform Ribbon uses the RadRibbonView from Telerik and is WPF based. Any discussion of WPF commands starts with an introduction to Microsoft's ICommand interface.
ICommand is in the System.Windows.Input namespace. It is in PresentationCore.dll in .NET 4 and earlier and in System.dll starting with .NET 4.5. ICommand contains the CanExecute and Execute methods and the CanExecuteChanged event. The CanExecute method controls whether a WPF button or menu item is enabled or disabled. The Execute method fires off the method associated with the command. The CanExecuteChanged event is fired when changes occur that affect whether or not the command can execute.ICommand Interface.
The RelayCommand class in Bentley.UI.Mvvm is an implementation of the ICommand interface. The RelayCommand 's sole purpose is to relay its functionality to another object by invoking delegates. Our RelayCommand implementation came from Josh Smith's library of MVVM classes.
The IMstnCommand interface is a managed interface for PowerPlatform commands. IMstnCommand is UI technology neutral. IMstnCommand, and several of its implementations, are in the MstnPlatformNET.Commands namespace in MicroStation.dll. IMstnCommand contains these properties:
The CanExecute property is all that overlaps with something in the ICommand interface. Everything else is unique to PowerPlatform. The MstnCommand class is an abstract base class that adds Named Expressions for Show, Enable, Toggle and Menu Mark. The NamedMstnCommand class is a subclass of MstnCommand and is the managed wrapper around the native XCommand.
The MstnRelayCommand class is a subclass of RelayCommand. It is WPF specific and is in the Bentley.MicroStation.WPF.dll assembly. It forms the connection between the Microsoft ICommand interface and the Bentley IMstnCommand interface introduced above. MstnRelayCommand contains:
To include anything in the PowerPlatform ribbon, it needs to be defined in a "Ribbon Definition". Ribbon Definitions are specified in an XML file conforming to the RibbonDefinitions.xsd file. There are definitions for numerous objects making up the ribbon, including Buttons, DropDownButtons, SplitButtons, Groups, Tabs, etc. Buttons, DropDownButtons, SplitButtons definitions may contain "Command Data" that refers to a "Named Command", which is really a reference to a NamedMstnCommand instance.
An example of a <Button> element in an XML file:
An example of a <ButtonRef> element referring to a Named <Button> in an XML file:
An example of exposing an XCommand to C# or XAML via the NamedMstnCommand and MstnRelayCommand classes:
An example of referencing an MstnRelayCommand in XAML for the Backstage:
We have implemented a way for managed applications to supply XCommandRsc s using a Bentley resource manager resource (.r) file. Here are the steps for using this capability.
Create resource file containing an XCommandRscList with the same name as your managed assembly.For example, if your assembly is Bentley.ECUISupport.dll, then your resource file should be Bentley.ECUISupport.r. See Bentley.ECUISupport.r for an example XCommand resource file. Here is a section of the Bentley.ECUISupport.r file:
Note that the XCommandListRsc s in MicroStation s source files use defined constants such as TXT_RibbonLabel_FileProperties because those text strings are also used in other places. It is not necessary to do that (you can just put the English strings directly into the resource) because a .rsc.mui.xliff file is generated from this file for localization purposes.
The field following the CMD_xxx entry must be the Add-ins TaskId. You can find out what that is by looking in the application dropdown in MicroStation s keyin browser. For XCommandRsc s added through the mechanism described here, it must be filled in.
The "#include <TransKitGenSrc\bentley.ecuisupportcmd.h>" and CMD_ELEMENT_INFO_FILEPROPERTIES sections are related to step 4 below.
Modify your transkit.xml file to specify that the XCommands resource file specified above is included in your translatable resources. For example, the <BentleyRsc> element was added to the ECUISupport transkit.xml file:
Modify the part in your xxx.PartFile.xml file to tell BentleyBuild that you want to deliver the compiled XCommand resource file alongside your assembly. You also need to add your .rsc file to the MS_XCOMMAND_RSRCS configuration file. If you are working with a MicroStation part, that can be done in the part file. The "Delivery\Bentley.ECUISupport.rsc" and "MS_XCOMMAND_RSCS" additions were made to the Bentley.ECUISupport part in PowerPlatform.Partfile.xml:
In your bmake file, make sure you are generating the .h file that specifies the CMD_xxx constants from your command table.If you are interested in making XCommand resources, that means you have specified the command structure of your application in an xml file, usually in a file named something like <appname>.commands.xml. You need to generate an include file that defines constants that you can use in the XCommands resource. In ECUISupport.mke, this was added:
If you look back at Step 1, you will see where the generated header file is #included, and where the constants are used in the XCommand specification.
Modify your bmake file to set up processing of the BentleyRsc section of the transkit.xml file. The following is in a new section that was added near the end of ECUISupport.mke.
Since this new section uses only bmake variables that are usually defined in every bmake file (e.g., appName, ContextDeliveryDir, baseDir), you can pretty much copy that section. If you are not working in the MicroStation source, you probably need to change the definition of MuiAdditionalIncludes, often to just . In the example, Transkit/SharedInc/ is there so that ribbontext.h is found to resolve the constants like TXT_RibbonLable_FileProperties.
If you follow the steps above, you should find the <appName>.rsc file delivered in the same directory as your <appName>.dll file. In the en subdirectory, there should be an <appName>.rsc.mui file, and an <appName>.rsc.mui.xliff file is created to be added to the transkit.