Visual Studio Style Tool Manager
A component for managing external programs in your application, modeled on the Visual Studio 'External Tools' feature.
Introduction
The ToolManager
is a reusable component for managing external programs in your application. It is modeled on the "External Tools" feature in Visual Studio 2005, but contains several additional features, such as being able to assign hotkeys and images to commands.
Installing the ToolManager
Add a reference to ToolManager.dll to your project, or alternatively, add the entire project to your solution and add a reference to the ToolManager
project. If you choose the latter method, you should also ensure that the ToolManager
is built before your own project, by modifying the project dependencies accordingly.
Using the code
Be sure to look at the demo application, as much of this document is taken directly from it. Also, it will help you to understand key ideas, such as macros, if you have played around with the ToolManager
beforehand.
Create an instance of the tool manager in your main Window class.
protected Miszou.ToolManager.Tools mTools;
In the constructor for the main Window, add a call to a new method, InitializeTools
. This step is not strictly necessary, but it keeps your code nicely organized, and separates the ToolManager
stuff from everything else.
public Form1()
{
InitializeComponent();
InitializeTools(); // Add this line
}
The InitializeTools()
function is where all the initialization of the ToolManager
takes place. The following code is taken from the demo application, and will be explained in detail.
protected void InitializeTools()
{
// List of Command Macros supported by your application
List<Macro> macroList = new List<Macro>();
macroList.Add(new Macro("$(SearchTerm)", "Search Term"));
macroList.Add(new Macro(string.Empty, string.Empty));
macroList.Add(new Macro("$(Date)", "System Date"));
macroList.Add(new Macro("$(Time)", "System Time"));
macroList.Add(new Macro(string.Empty, string.Empty));
macroList.Add(new Macro("$(ComputerName)", "Computer Name" ));
macroList.Add(new Macro("$(UserName)", "User Name"));
// Folder macros
List<Macro> folderList = new List<Macro>();
folderList.Add(new Macro("$(AppDir)", "Application Directory"));
folderList.Add(new Macro("$(ProgramFiles)", "Program Files"));
folderList.Add(new Macro("$(MyDocuments)", "My Documents"));
// Tool images
ImageList imageList = new ImageList();
imageList.Images.Add("Calculator",
global::ToolManagerDemo.Properties.Resources.IMG_CALC);
imageList.Images.Add("Console",
global::ToolManagerDemo.Properties.Resources.IMG_CONSOLE);
imageList.Images.Add("Pencil",
global::ToolManagerDemo.Properties.Resources.IMG_PENCIL);
// Ordinarily, we should load the tools from a folder
// that a non-privileged user can write to,
// such as Environment.SpecialFolder.LocalApplicationData.
// However, for the purposes of this Demo project and
// the lack of a proper installation package,
// the Tool data is stored in the application folder instead.
string strFile = Path.Combine( Application.StartupPath, "Tools.xml");
// Create the ToolManager
mTools = new Tools(strFile,
macroList, folderList, new Tools.MacroExpander(ExpandToolMacros),
imageList);
// Load the Fixed tools.
// This file can be stored in an area that non-privileged users
// cannot write to, such as the application installation folder.
string strFixedTools = Path.Combine( Application.StartupPath,
"FixedTools.xml" );
mTools.LoadFixedTools(strFixedTools);
// Add the tools to the menu and toolbar
mTools.BuildToolMenu(mnuTools, 0);
mTools.BuildToolBar(toolStrip1, 2, false,
ToolStripItemDisplayStyle.ImageAndText);
}
Now that may look like a lot of code, especially for something that claims to be able to manage your external tools for you, but it's actually a lot simpler than it looks!
Creating Macros
The first thing that happens is a list of Command
Macro
objects is created. These macro
objects are nothing more than two strings of text - the macro
that your code will translate to something else (more on this later), and a more friendly description of the macro
that will be displayed by the ToolManager
when the user clicks the arrow button to the right of the Arguments
or Initial Directory
fields. For example, the following Macro
definition will create a macro, $(Date)
that is replaced by the system date
before the command is executed.
List<Macro> macroList = new List<Macro>();
macroList.Add( new Macro( "$(Date)", "System Date" ) );
A macro
with an empty string for both the Name
and Description
will create a Separator
between the menu items on the popup macro list.
Next, a list of Folder
Macro
s are created. These items are functionally the same as Command
Macro
s, but will be shown next to the Initial Directory
textbox in the ToolManagerEditor
, instead of the Arguments
textbox (See the diagram at the top of the page).
Tool Images
Each Tool
in the ToolManager
list can have an image associated with it. In this demo application I have used the Silk Icons from here, as they look really good and are ideal for this purpose. Furthermore, they're free.
Each icon has been added to the resource file of the Demo Application, and is then added to an ImageList
, which will be passed to the Tools
constructor, along with the macro
definitions. The text for the image will be displayed, along with the image itself, in the ToolManagerEditor
allowing the user to pick a suitable icon for each tool.
// Create a list of images that can be assigned to the Tools
ImageList imageList = new ImageList();
imageList.Images.Add( "Calculator",
global::ToolManagerDemo.Properties.Resources.IMG_CALC );
Construction
Now that we've created ourMacro
definitions and image list, it's time to create the ToolManager
itself. The following code invokes the constructor and creates the ToolManager
instance.
// Create the ToolManager
mTools = new Tools(
strFile, // Filename that is used to load/save the Tools
macroList, // List of application-defined Command macros
folderList, // List of application-defined Folder macros
new Tools.MacroExpander(ExpandToolMacros),
// Delegate that will be used to translate the macros
imageList); // List of images that can be assigned to the Tools
The strFile
parameter is the name of a file that the ToolManager
will use to persist the list of Tool
objects. It should be located in a folder that the end-user will have permission to write to. If the file does not exist, it will be created when the first tool is added to the ToolManager
by the user.
macroList
, folderList
and imageList
are the lists of Macros
and images that we created earlier.
Macro Expansion
The fourth parameter to the constructor requires some additional explanation. It is a delegate function that the ToolManager
will call whenever it needs to expand a Macro
. Since the ToolManager
cannot possibly know the meaning of any of your macros, it is up to your application to translate them whenever necessary.
A simple Tools.MacroExpander
function is basically a string replace operation that replaces the macro with it's actual value. For example, $(Date)
might become 07/29/2007
. The method below is an example of a Tools.MacroExpander
delegate taken from the demo application.
protected String ExpandToolMacros(String str)
{
// Command Expansion
str = str.Replace("$(SearchTerm)",
listBox1.Items[ listBox1.SelectedIndex ].ToString() );
str = str.Replace("$(Date)", DateTime.Today.ToShortDateString());
str = str.Replace("$(Time)", DateTime.Now.ToShortTimeString());
str = str.Replace("$(ComputerName)", SystemInformation.ComputerName );
str = str.Replace("$(UserName)", Environment.UserName);
// Folder Expansion
str = str.Replace("$(AppDir)", Application.StartupPath);
str = str.Replace("$(ProgramFiles)",
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles));
str = str.Replace("$(MyDocuments)",
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
return str;
}
Note that it is not necessary to have your macros take the form of $(macro)
- you can use any string that you choose. I have only used the $(macro)
notation as that is the same format that Visual Studio uses. It is entirely up to you, and your application, what your macros look like.
Fixed Tools
As well as user-defined tools, you can also provide a set of tools that the user is not allowed to edit or remove. These tools are referred to as "Fixed Tools". This feature is intended for use in controlled corporate environments where your application will have access to a known set of utilities and external applications. You can "fix" the settings for these tools, so that users cannot break them by changing the parameters, or by deleting them.
I use a set of fixed tools in a corporate application that allows users to launch several other internal applications, the company website and even a context-sensitive Google search, using Macro
expansion to substitute the search string. (The demo application contains an example of this, using Macro
expansion to look up words here.
If you are not in a corporate environment, it is not recommended that you use the fixed tools feature, as you cannot necessarily guarantee that your users will have access to all of the required applications. Furthermore, if you wish to ship your application with a predefined toolset, you should probably just use the standard tools and ensure that any Tool
file that you provide is installed into a folder that the end-user can write to, such as Environment.SpecialFolder.LocalApplicationData
.
The easiest way to create a fixed toolset is to create a "normal" toolset, using the ToolManager
, and then rename or move the created file. You can then pass this filename to the LoadFixedTools
method.
// Load the Fixed tools.
// This file can be stored in an area that non-privileged users
// cannot write to, such
// as the application installation folder.
string strFixedTools = Path.Combine( Application.StartupPath,
"FixedTools.xml" );
mTools.LoadFixedTools(strFixedTools);
Adding the Tools to the Application Menu and Toolbar
The ToolManager
contains several functions that facilitate adding the tools to a menu or a toolbar. Simply call the BuildToolMenu
method, passing in the appropriate MenuStrip
and the offset in the menu at which to add the tools.
// Add the tools to the menu and toolbar
mTools.BuildToolMenu(
mnuTools, // The menu to which to add the tools.
0); // The offset in the menu where the tools should be added.
mTools.BuildToolBar(
toolStrip1, // The Toolstrip to which to add the tools
2, // The offset in the toolstrip where
// the tools should be added.
false, // Don't add all tools to the toolbar
ToolStripItemDisplayStyle.ImageAndText);
// The style of buttons for the tools
Similarly, to add the tools to a toolbar, call the BuildToolBar
method. However, there are a couple of extra parameters required when building a toolbar, the first of which is a boolean value that determines whether ALL the tools are added to the toolbar, or only those that have been explicitly added to the toolbar by the user.
The ToolEditor
(which will be explained in greater detail later) gives the user the option of adding tools to a toolbar. However, your application may not support a toolbar, or you may want to force all tools to appear on the toolbar, regardless of whether the user elects to display the tool on a toolbar. Simply pass true
as the 3rd parameter to BuildToolBar
, and all tools will be added, regardless of whether the user selected it or not. Note that the ToolEditor
can optionally hide the "Add tool to toolbar" setting if it is not required.
The ToolManager
will automatically bind the appropriate events to the Tool
, so that the actual execution of the Tool
is also managed by the ToolManager
. There is no need to handle events or launch the Tools yourself. However, if necessary, you can execute a Tool
by calling its Execute
method yourself.
Invoking the ToolEditor
To invoke the ToolEditor
to allow the user to modify their tool settings, simply create an event handler and add the following code. In this case, the event is fired from a menu item.
The Edit
method returns a DialogResult
indicating whether the ToolEditor
was cancelled or not. If the method returns DialogResult.OK
, then we simply remove the current toolset from the menu and toolbar, by calling RemoveToolMenu
and RemoveToolBar
respectively, and then add the new toolset back into the menu, by calling BuildToolMenu
and BuildToolBar
, as we saw above.
private void mnuTools_Click(object sender, EventArgs e)
{
// Invoke the ToolEditor
if ( mTools.Edit(
Tools.EditFlags.AllowLockedUIEdit |
Tools.EditFlags.PromptToSave |
Tools.EditFlags.ShowToolBarButton) == DialogResult.OK )
{
// Remove the old tools from the menu/toolbar
mTools.RemoveToolMenu(mnuTools);
mTools.RemoveToolBar(toolStrip1);
// Add the new tool definitions
mTools.BuildToolMenu(mnuTools, 0);
mTools.BuildToolBar(toolStrip1, 2, false,
ToolStripItemDisplayStyle.ImageAndText);
}
}
The ToolEditor
is invoked with a set of flags that determine its behavior.
AllowLockedUIEdit |
Determines whether the Tool Editor allows the user to change the Hotkeys and image associated with fixed tools. Fixed tools are normally not modifiable by the user, but they may want to assign their own image or hotkeys. |
PromptToSave |
If this flag is specified, the ToolEditor will prompt the user to save any changes if the dialog is cancelled. |
ShowToolBarButton |
This flag will cause the ToolEditor to display a check box that says "Add this tool to the toolbar". If you want to add all tools to the toolbar, or if your application does not have a toolbar, you can skip this flag to prevent the checkbox from appearing. |
Managing your own Tools
While the ToolManager
does an admirable job of maintaining lists of user-defined tools and you should rarely need to manage the tool list manually or create your own tools programmatically, there may be a time when you need to do such things. The main Tools
collection can be enumerated in a foreach
loop. You may need to do this if you want to add the tools to something other than a menu or toolbar, for example.
To create a new Tool
, call one of the overloaded CreateTool
methods. The new tool will automatically be added to the ToolManager
s internal list.
Summary of Public Classes, Methods and Properties
Tools
: The main ToolManager
class. Contains methods for constructing and managing a list of user-defined tools.
Properties |
|
Count |
Returns the number of Tool objects currently managed by this ToolManager |
Methods |
|
BuildToolBar |
Adds the current tool definitions to a user-supplied ToolStrip |
BuildToolMenu |
Adds the current tool definitions to a user-supplied ToolStripDropDownItem |
CreateTool |
Creates a new Tool and adds it to the list of managed tools. You should not normally need to call this method, as Tool creation is managed automatically. |
Edit |
Invoke the ToolEditor dialog. |
LoadFixedTools |
Loads a fixed toolset and merges it with the users personal Tool items. |
MacroExpander |
A delegate method that is implemented in your application. The ToolManager will call this method whenever it needs to expand a Macro . |
RemoveToolBar |
Removes the list of Tool items from the specified Menu . |
RemoveToolMenu |
Removes the list of Tool items from the specified Toolbar . |
Tool
: An instance of a Tool
. You should not normally interact with this class directly, as the ToolManager
can handle everything for you.
Properties |
|
Args |
A string representing the arguments to be passed to Command , before Macro expansion takes place. |
Command |
The command that will be executed. |
ImageIndex |
The index into the ImageList of the image to associate with this Tool . |
InitialDir |
The starting directory that Command will be executed in. |
Locked |
Read-only property that indicates whether a Tool is fixed. |
Prompt |
A boolean that determines whether the user will be prompted for Args prior to executing the Command . |
ShortcutDisplayString |
The text for any keyboard shortcut that has been defined for this Tool . |
ShowOnToolBar |
Boolean value indicating whether the Tool should be displayed on a Toolbar . |
Title |
The text that is displayed on the menu for the Tool . |
Command |
The command that will be executed. |
Methods |
|
Execute |
Executes the Command associated with this Tool . Prior to execution, the delegate function, Tools.MacroExpander is called to translate all Macro items. |
Macro
: A Macro
definition.
Properties |
|
Name |
The name of the Macro , for example $(Date) . |
Description |
The description of the Macro , for example System Date. |
Summary
The ToolManager
can be a useful addition to an application, giving it a more professional, "complete" feel. Hopefully, you will find it useful and easy to incorporate into your own projects.
Release History
- Version 1.0 (07/29/2007) - Initial Version