This sample creates a basic User Interface Extensibility Framework application consisting of one ShellUI module which adds buttons to the task pane and context menu that are enabled/disabled and shown/hidden depending upon the user’s actions.

The source code for this sample is available within our GitHub Samples and Libraries repository.

Overview

This sample does not show how to create a local development folder or to deploy the code to the M-Files server. It is assumed that a local development folder already exists, and that is the location in which the development is occurring.

Creating the application structure

Creating the application definition file

Into this folder we will create an application definition file. This file must be named appdef.xml. The application will use version 3 of the client schema (as we are only targeting newer M-Files versions), and the supported platform will be set as desktop only. The application will declare a single Shell UI module (with its code in main.js), and no dashboards.

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:noNamespaceSchemaLocation="http://www.m-files.com/schemas/appdef-client-v3.xsd">
	<guid>60c93a08-27c3-4c59-aa61-906aaff52b81</guid>	
	<name>Commands</name>
	<version>0.1</version>
	<description>A basic application showing how to work with commands.</description>
	<publisher>M-Files Corporation</publisher>
	<copyright>(c) M-Files Corporation 2017</copyright>
	<required-mfiles-version>11.3.0000.00</required-mfiles-version>
	<platforms>
		<platform>Desktop</platform>
	</platforms>
	<modules>
		<module environment="shellui">
			<file>main.js</file>
		</module>
	</modules>
</application>

Ensure that your application has a unique GUID by using a GUID generator, such as this one.

Creating the module

Next we will create a module file to contain our actual application logic. At this point we will just register to be notified of main lifecycle events:

  • We will declare a default entry point for the ShellUI module.
  • We will react to the NewShellFrame event and obtain a reference to the shell frame.
  • We will react to the shell frame’s Started event (as using the shell frame before this point will result in an exception).
// NOTE! This code is for demonstration purposes only and does not contain any kind of
// 		 error handling. MUST be revised before using in production.

"use strict";
 
function OnNewShellUI( shellUI )
{
	/// <summary>Executed by the UIX when a ShellUI module is started.</summary>
	/// <param name="shellUI" type="MFiles.ShellUI">The shell UI object which was created.</param>
 
	// This is the start point of a ShellUI module.
 
	// Register to be notified when a new shell frame (Event_NewShellFrame) is created.
	shellUI.Events.Register(
		Event_NewShellFrame,
		handleNewShellFrame );
}
 
function handleNewShellFrame(shellFrame)
{
	/// <summary>Handles the OnNewNormalShellFrame event for an IShellUI.</summary>
	/// <param name="shellFrame" type="MFiles.ShellFrame">The shell frame object which was created.</param>
 
	// The shell frame was created but it cannot be used yet.
	// The following line would throw an exception ("The object cannot be accessed, because it is not ready."):
	// shellFrame.ShowMessage("A shell frame was created");
 
	// Register to be notified when the shell frame is started.
	shellFrame.Events.Register(
		Event_Started,
		getNewShellListingHandler( shellFrame ) );
}
 
function getShellFrameStartedHandler(shellFrame)
{
	/// <summary>Gets a function to handle the OnStarted event for an IShellFrame.</summary>
 
	// The shell frame is now started and can be used.
	return function(){};
}

Creating a button in the task area

Adding a button into the task area involves two steps:

  1. Creating a new ICommand using CreateCustomCommand (line 47).
  2. Adding the command into the task area using AddCustomCommandToGroup (line 52).

The group that the button will be added to is either one of the built-in task pane groups or the ID of a group that has previously been created with CreateGroup.

// NOTE! This code is for demonstration purposes only and does not contain any kind of
// 		 error handling. MUST be revised before using in production.

"use strict";
 
function OnNewShellUI( shellUI )
{
	/// <summary>Executed by the UIX when a ShellUI module is started.</summary>
	/// <param name="shellUI" type="MFiles.ShellUI">The shell UI object which was created.</param>
 
	// This is the start point of a ShellUI module.
 
	// Register to be notified when a new shell frame (Event_NewShellFrame) is created.
	shellUI.Events.Register(
		Event_NewShellFrame,
		handleNewShellFrame );
}
 
function handleNewShellFrame(shellFrame)
{
	/// <summary>Handles the OnNewNormalShellFrame event for an IShellUI.</summary>
	/// <param name="shellFrame" type="MFiles.ShellFrame">The shell frame object which was created.</param>
 
	// The shell frame was created but it cannot be used yet.
	// The following line would throw an exception ("The object cannot be accessed, because it is not ready."):
	// shellFrame.ShowMessage("A shell frame was created");
 
	// Register to be notified when the shell frame is started.
	shellFrame.Events.Register(
		Event_Started,
		getShellFrameStartedHandler(shellFrame) );
}
 
function getShellFrameStartedHandler(shellFrame)
{
	/// <summary>Returns a function which handles the OnStarted event for an IShellFrame.</summary>
 
	// The shell frame is now started and can be used.

	return function()
	{

		// Create a command (button).  Note that it is not yet visible.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ICommands~CreateCustomCommand.html
		var commandOneId = shellFrame.Commands.CreateCustomCommand("My First Command");

		// Add the command to the task area.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ITaskPane~AddCustomCommandToGroup.html
		try {
			shellFrame.TaskPane.AddCustomCommandToGroup(commandOneId, TaskPaneGroup_Main, 1);
		}
		catch (e) {
			// This will except if the task pane is not available (e.g. in a History view).
		}

	}

}

Logging into the M-Files vault should now show a button in the task area with the text My First Command:

The command showing in the user interface

Reacting when the command is clicked

Reacting to a command being clicked involves three steps:

  1. Register to be notified of the CustomCommand event (line 59).
  2. Ensure that the command that was clicked was the one we want to handle (line 61).
  3. Execute the required code (line 68).
// NOTE! This code is for demonstration purposes only and does not contain any kind of
// 		 error handling. MUST be revised before using in production.

"use strict";

function OnNewShellUI( shellUI )
{
	/// <summary>Executed by the UIX when a ShellUI module is started.</summary>
	/// <param name="shellUI" type="MFiles.ShellUI">The shell UI object which was created.</param>
 
	// This is the start point of a ShellUI module.
 
	// Register to be notified when a new shell frame (Event_NewShellFrame) is created.
	shellUI.Events.Register(
		Event_NewShellFrame,
		handleNewShellFrame );
}
 
function handleNewShellFrame(shellFrame)
{
	/// <summary>Handles the OnNewNormalShellFrame event for an IShellUI.</summary>
	/// <param name="shellFrame" type="MFiles.ShellFrame">The shell frame object which was created.</param>
 
	// The shell frame was created but it cannot be used yet.
	// The following line would throw an exception ("The object cannot be accessed, because it is not ready."):
	// shellFrame.ShowMessage("A shell frame was created");
 
	// Register to be notified when the shell frame is started.
	shellFrame.Events.Register(
		Event_Started,
		getShellFrameStartedHandler(shellFrame) );
}
 
ffunction getShellFrameStartedHandler(shellFrame)
{
	/// <summary>Returns a function which handles the OnStarted event for an IShellFrame.</summary>
 
	// The shell frame is now started and can be used.

	return function() {

		// Create a command (button).  Note that it is not yet visible.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ICommands~CreateCustomCommand.html
		var commandOneId = shellFrame.Commands.CreateCustomCommand("My First Command");

		// Add the command to the task area.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ITaskPane~AddCustomCommandToGroup.html
		try {
			shellFrame.TaskPane.AddCustomCommandToGroup(commandOneId, TaskPaneGroup_Main, 1);
		}
		catch (e) {
			// This will except if the task pane is not available (e.g. in a History view).
		}

		// Register to be notified when a custom command is clicked.
		// Note: this will fire for ALL custom commands, so we need to filter out others.
		shellFrame.Commands.Events.Register(
			Event_CustomCommand,
			function(commandId)
			{
				// Branch depending on the Id of the command that was clicked.
				switch(commandId)
				{
					case commandOneId:
						// Our first command was clicked.
						shellFrame.ShowMessage("The first command was clicked.")
						break;
				}
			});

	};

}

A message shown when the command is clicked

Setting an icon for the command

To set an icon for the command:

  1. Ensure that you have an icon in the correct format. This should be a .ico file with at least a 16x16 image.
  2. Ensure that the icon file is included within the User Interface Extensibility Framework application.
  3. Call SetIconFromPath, passing both the command Id and the relative path to the icon (line 50).

Setting an icon only works if the command is shown in the task area. Icons will not be shown for items in the context menu.

To maximise browser compatibility, it is recommended that a 16x16 .png file is used as the icon within the M-Files Web Access. If your application is targetting the M-Files Web Access then please see this page for information on recommended changes.

// NOTE! This code is for demonstration purposes only and does not contain any kind of
// 		 error handling. MUST be revised before using in production.

"use strict";

function OnNewShellUI( shellUI )
{
	/// <summary>Executed by the UIX when a ShellUI module is started.</summary>
	/// <param name="shellUI" type="MFiles.ShellUI">The shell UI object which was created.</param>
 
	// This is the start point of a ShellUI module.
 
	// Register to be notified when a new shell frame (Event_NewShellFrame) is created.
	shellUI.Events.Register(
		Event_NewShellFrame,
		handleNewShellFrame );
}
 
function handleNewShellFrame(shellFrame)
{
	/// <summary>Handles the OnNewNormalShellFrame event for an IShellUI.</summary>
	/// <param name="shellFrame" type="MFiles.ShellFrame">The shell frame object which was created.</param>
 
	// The shell frame was created but it cannot be used yet.
	// The following line would throw an exception ("The object cannot be accessed, because it is not ready."):
	// shellFrame.ShowMessage("A shell frame was created");
 
	// Register to be notified when the shell frame is started.
	shellFrame.Events.Register(
		Event_Started,
		getShellFrameStartedHandler(shellFrame) );
}
 
function getShellFrameStartedHandler(shellFrame)
{
	/// <summary>Returns a function which handles the OnStarted event for an IShellFrame.</summary>
 
	// The shell frame is now started and can be used.

	return function() {

		// Create a command (button).  Note that it is not yet visible.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ICommands~CreateCustomCommand.html
		var commandOneId = shellFrame.Commands.CreateCustomCommand("My First Command");

		// Set the icon for the command.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ICommands~SetIconFromPath.html
		shellFrame.Commands.SetIconFromPath(commandOneId, "icons/uparrow.ico");

		// Add the command to the task area.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ITaskPane~AddCustomCommandToGroup.html
		try {
			shellFrame.TaskPane.AddCustomCommandToGroup(commandOneId, TaskPaneGroup_Main, 1);
		}
		catch (e) {
			// This will except if the task pane is not available (e.g. in a History view).
		}

		// Register to be notified when a custom command is clicked.
		// Note: this will fire for ALL custom commands, so we need to filter out others.
		shellFrame.Commands.Events.Register(
			Event_CustomCommand,
			function(commandId)
			{
				// Branch depending on the Id of the command that was clicked.
				switch(commandId)
				{
					case commandOneId:
						// Our first command was clicked.
						shellFrame.ShowMessage("The first command was clicked.")
						break;
				}
			});

	};

}

The command with a custom icon

Adding a command to both the task area and the context menu

To add a command created with CreateCustomCommand to the context menu, call AddCustomCommandToMenu (line 62). In the example below, it is the same command object added to both task pane and context menu, so our code to react when the button is clicked will be fired for the context menu item and the task pane.

The command can be added in one of a number of locations within the context menu. Some of these locations are only shown in some scenarios (e.g. some sections are not shown when files are right-clicked, but are when objects are right-clicked).

// NOTE! This code is for demonstration purposes only and does not contain any kind of
// 		 error handling. MUST be revised before using in production.

"use strict";

function OnNewShellUI( shellUI )
{
	/// <summary>Executed by the UIX when a ShellUI module is started.</summary>
	/// <param name="shellUI" type="MFiles.ShellUI">The shell UI object which was created.</param>
 
	// This is the start point of a ShellUI module.
 
	// Register to be notified when a new shell frame (Event_NewShellFrame) is created.
	shellUI.Events.Register(
		Event_NewShellFrame,
		handleNewShellFrame );
}
 
function handleNewShellFrame(shellFrame)
{
	/// <summary>Handles the OnNewNormalShellFrame event for an IShellUI.</summary>
	/// <param name="shellFrame" type="MFiles.ShellFrame">The shell frame object which was created.</param>
 
	// The shell frame was created but it cannot be used yet.
	// The following line would throw an exception ("The object cannot be accessed, because it is not ready."):
	// shellFrame.ShowMessage("A shell frame was created");
 
	// Register to be notified when the shell frame is started.
	shellFrame.Events.Register(
		Event_Started,
		getShellFrameStartedHandler(shellFrame) );
}
 
function getShellFrameStartedHandler(shellFrame)
{
	/// <summary>Returns a function which handles the OnStarted event for an IShellFrame.</summary>
 
	// The shell frame is now started and can be used.

	return function() {

		// Create a command (button).  Note that it is not yet visible.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ICommands~CreateCustomCommand.html
		var commandOneId = shellFrame.Commands.CreateCustomCommand("My First Command");

		// Set the icon for the command.
		shellFrame.Commands.SetIconFromPath(commandOneId, "icons/uparrow.ico");

		// Add the command to the task area.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ITaskPane~AddCustomCommandToGroup.html
		try {
			shellFrame.TaskPane.AddCustomCommandToGroup(commandOneId, TaskPaneGroup_Main, 1);
		}
		catch (e) {
			// This will except if the task pane is not available (e.g. in a History view).
		}

		// Add the command to the context menu.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ICommands~AddCustomCommandToMenu.html
		shellFrame.Commands.AddCustomCommandToMenu(commandOneId, MenuLocation_ContextMenu_Top, 1);

		// Register to be notified when a custom command is clicked.
		// Note: this will fire for ALL custom commands, so we need to filter out others.
		shellFrame.Commands.Events.Register(
			Event_CustomCommand,
			function(commandId)
			{
				// Branch depending on the Id of the command that was clicked.
				switch(commandId)
				{
					case commandOneId:
						// Our first command was clicked.
						shellFrame.ShowMessage("The first command was clicked.")
						break;
				}
			});

	};

}

The command added to the context menu

Showing and hiding buttons

The visibility of commands can be controlled by calling SetCommandState. To show this, we will create a second command (line 50) and initially set it to hidden (line 54). Clicking the first command will show and enable the second command (line 83), and clicking the second command will disable itself (line 89).

Setting a command state to CommandState_Active will make it both active and visible.

// NOTE! This code is for demonstration purposes only and does not contain any kind of
// 		 error handling. MUST be revised before using in production.

"use strict";

function OnNewShellUI( shellUI )
{
	/// <summary>Executed by the UIX when a ShellUI module is started.</summary>
	/// <param name="shellUI" type="MFiles.ShellUI">The shell UI object which was created.</param>
 
	// This is the start point of a ShellUI module.
 
	// Register to be notified when a new shell frame (Event_NewShellFrame) is created.
	shellUI.Events.Register(
		Event_NewShellFrame,
		handleNewShellFrame );
}
 
function handleNewShellFrame(shellFrame)
{
	/// <summary>Handles the OnNewNormalShellFrame event for an IShellUI.</summary>
	/// <param name="shellFrame" type="MFiles.ShellFrame">The shell frame object which was created.</param>
 
	// The shell frame was created but it cannot be used yet.
	// The following line would throw an exception ("The object cannot be accessed, because it is not ready."):
	// shellFrame.ShowMessage("A shell frame was created");
 
	// Register to be notified when the shell frame is started.
	shellFrame.Events.Register(
		Event_Started,
		getShellFrameStartedHandler(shellFrame));
}
 
function getShellFrameStartedHandler(shellFrame)
{
	/// <summary>Returns a function which handles the OnStarted event for an IShellFrame.</summary>
 
	// The shell frame is now started and can be used.

	return function() {

		// Create a command (button).  Note that it is not yet visible.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ICommands~CreateCustomCommand.html
		var commandOneId = shellFrame.Commands.CreateCustomCommand( "My First Command" );

		// Create a command (button).  Note that it is not yet visible.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ICommands~CreateCustomCommand.html
		var commandTwoId = shellFrame.Commands.CreateCustomCommand( "My Second Command" );

		// Hide the second command.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ICommands~SetCommandState.html
		shellFrame.Commands.SetCommandState( commandTwoId, CommandLocation_All, CommandState_Hidden );

		// Set the icon for the command.
		shellFrame.Commands.SetIconFromPath( commandOneId, "icons/uparrow.ico" );

		// Add the first and second commands to the task area.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ITaskPane~AddCustomCommandToGroup.html
		try {
			shellFrame.TaskPane.AddCustomCommandToGroup( commandOneId, TaskPaneGroup_Main, 1 );
			shellFrame.TaskPane.AddCustomCommandToGroup( commandTwoId, TaskPaneGroup_Main, 1 );
		}
		catch (e) {
			// This will except if the task pane is not available (e.g. in a History view).
		}

		// Add the first and second commands to the context menu.
		// ref: https://www.m-files.com/UI_Extensibility_Framework/index.html#MFClientScript~ICommands~AddCustomCommandToMenu.html
		shellFrame.Commands.AddCustomCommandToMenu( commandOneId, MenuLocation_ContextMenu_Top, 1 );
		shellFrame.Commands.AddCustomCommandToMenu( commandTwoId, MenuLocation_ContextMenu_Top, 1 );

		// Register to be notified when a custom command is clicked.
		// Note: this will fire for ALL custom commands, so we need to filter out others.
		shellFrame.Commands.Events.Register(
			Event_CustomCommand,
			function(commandId) {
				// Branch depending on the Id of the command that was clicked.
				switch (commandId) {
					case commandOneId:
						// Our first command was clicked.
						shellFrame.Commands.SetCommandState(commandTwoId,
							CommandLocation_All,
							CommandState_Active);
						break;
					case commandTwoId:
						// Our second command was clicked.
						shellFrame.Commands.SetCommandState(commandTwoId,
							CommandLocation_All,
							CommandState_Inactive);
						break;
				}
			} );

	}

}

Testing the application

Altering visibility and active state of commands