Click here to Skip to main content
Click here to Skip to main content
Go to top

Visual Studio Style Tool Manager

, 9 Aug 2007
Rate this:
Please Sign up or sign in to vote.
A component for managing external programs in your application, modeled on the Visual Studio 'External Tools' feature.

Screenshot - ToolManagerEditor.png

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 Macros are created. These items are functionally the same as Command Macros, 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 our Macro 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 ToolManagers 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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Miszou
Software Developer (Senior)
United States United States
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionNice! PinmemberZac Greve5-Nov-12 4:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 9 Aug 2007
Article Copyright 2007 by Miszou
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid