Export (0) Print
Expand All

Walkthrough: Dynamic Customization of ToolboxItem Configuration

[This topic is pre-release documentation and is subject to change in future releases. Blank topics are included as placeholders.]

This walkthrough illustrates how a managed VSPackage can supply dynamic configuration support for the ToolboxItem objects.

This walkthrough guides you through the following steps:

  1. Add and properly register all Toolbox controls in the VSPackage objects using ToolboxItemAttribute, ToolboxBitmapAttribute, and DisplayNameAttribute.

  2. Create the following two controls and add icons for each to the Toolbox:

    • One control added to the Toolbox using the default of the ToolboxItem class.

    • One control added using a custom class derived from the ToolboxItem class.

  3. Write an implementation of IConfigureToolboxItem, and register it by:

    1. Applying an instance of ProvideAssemblyFilterAttribute to the implementation of the IConfigureToolboxItem class to specify the ToolboxItem instances that this implementation will act upon.

    2. Applying an instance of ProvideToolboxItemConfigurationAttribute to the class implementing the Package class for the VSPackage.

  4. Register the VSPackage as providing ToolboxItem objects with the ProvideToolboxItemsAttribute.

  5. Use reflection to generate a list of all ToolboxItem objects the VSPackage provides on VSPackage load.

  6. Create a handler for the ToolboxInitialized and ToolboxUpgraded events, which ensure that the VSPackage's ToolboxItem objects are properly loaded.

  7. Implement a command on the VSPackage to force re-initialization of the toolbox.

Carry out the following procedures to create a managed VSPackage.

To create a Managed VSPackage to provide toolbox items

  1. On the File menu, point to New, and then click Project to open the New Project dialog box.

  2. From the list of Other Project Types, expand Extensibility and then click Visual Studio Extensibility.

  3. Run the Visual Studio Extensibility Package Wizard and carry out the following steps:

    1. Create a new project named ItemConfiguration.

    2. In the Select a Programming Language page, set the language to Visual C#.

    3. In the Select VSPackage Options page, select the Menu Command check box.

    4. On the Command Options page, enter Initialize ItemConfiguration for the Command Name.

    5. Accept all other defaults.

    The wizard generates a managed project named ItemConfiguration.

    All classes and objects in this Visual Studio solution are to be under the Company.ItemConfiguration namespace.

    The name of the assembly generated will be ItemConfiguration. By using ProvideAssemblyFilterAttribute, you ensure that the VSPackage restricts application of its dynamic configuration functionality to ToolboxItem objects within this package.

  4. The walkthrough will require functionality available under the System.Drawing.Design, and therefore, system.drawing.design.dll must be added to the ItemConfiguration project's References:

    1. Right-click the references item in the ItemConfiguration hierarchy in Solution Explorer.

    2. Choose Add Reference from the menu.

    3. On the .NET tab of the Add References dialog box, scroll down to the entry for system.drawing.design and double-click on it.

  5. Verify the correctness of the wizard-generated code:

    1. Build the solution and verify that it compiles without errors.

    2. Test the solution by choosing the Start command on the Debug menu of the IDE.

    When running the walkthrough, you should see a new command item, My Command Name, on the Tools menu, when you run a new instance of the IDE (running with the experimental hive).

    Selecting this command opens a Message Box containing the text Inside Company.ItemConfiguration.ItemConfiguration.MenuItemCallback().

In this section, you create and register a simple Toolbox control, Control1, derived from the UserControl class. For more information about authoring Windows Forms controls and ToolboxItem, see Developing Windows Forms Controls at Design Time.

To create a Toolbox control to be used with the default ToolboxItem

  1. Using Solution Explorer, add a UserControl object named Control1 in a file named Control1.cs to the project ItemConfiguration:

    1. In Solution Explorer, right-click the ItemConfiguration project and point to Add.

    2. On the Add menu, click New Item.

    3. In the Add New Item dialog box, select User Control, supplying Control1.cs as the name.

      For more information on how to add new items to a project, see How to: Add New Project Items.

  2. In Solution Explorer, right-click Control1.cs, and then select View Designer.

  3. Select the Button control from the Common Controls category of the Toolbox and drag it to the designer.

    NoteNote

    If the Button control is not visible, make the Toolbox visible by selecting Toolbox from the IDE's View menu.

  4. Double-click the UserControl to reveal the code view and the button1_Click method. Add the following line to the button1_Click body:

    private void button1_Click(object sender, EventArgs e)
    {
       MessageBox.Show("Hello world from " + this.ToString());
    }
    
  5. Modify the constructor of the Control1 class to set the text appearing in the button1 control after the InitializeComponent method is called:

    {
        InitializeComponent();
        this.button1.Text = this.Name.ToString();
    }
    
  6. Add attributes to the file. These attributes allow the VSPackage to query the supplied ToolboxItem class.

    namespace Company.ItemConfiguration {
    
        [DisplayNameAttribute("ToolboxMember Case 1")] //We will use this as the ToolboxItem display name.
        [ToolboxItemAttribute(true)] 
        [ToolboxBitmap(typeof(ItemConfiguration), "Control1.bmp")] //Indicate the custom bitmap to use. 
                                                                        //Note Build Action for bitmap must be set to "embed".
        public partial class Control1 : UserControl {
    
  7. From the File menu, choose Save to save the file.

Next, you create and register a simple Toolbox control, Control2, derived from UserControl using a custom class derived from ToolboxItem.

To create a Toolbox control for using a custom ToolboxItem-derived class

  1. Create the UserControl Control2, following the directions provided in steps 1-5 of the section above.

  2. Double-click the UserControl just created to reveal the code view, and modify the control's constructor and the button1_Click method to appear as below:

        public Control2(){
                InitializeComponent();
                this.button1.Text = this.Name.ToString() + " Button";
            }
    
            private void button1_Click(object sender, EventArgs e){
                MessageBox.Show("Hello world from " + this.ToString());
            }
    
  3. Add System.Drawing.Design to the namespaces used in Control2.cs.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Text;
    using System.Windows.Forms;
    using System.Drawing.Design;
    
  4. Add attributes to the file. These provide support that allows the VSPackage to query for a ToolboxItem class.

    For more information and examples about writing custom ToolboxItem objects, see the discussion in the ToolboxItem reference page.

    namespace Company.ItemConfiguration {
    
        [DisplayNameAttribute("ToolboxMember Case 2")] //We will use this as the ToolboxItem display name.
        [ToolboxItemAttribute(typeof(Control2_ToolboxItem))] // We will use the ToolboxItem Class below with this control
        [ToolboxBitmap(typeof(ItemConfiguration), "Control2.bmp")] //Indicate the custom bitmap to use. 
                                                                        //Note Build Action for bitmap must be set to "embed".
        public partial class Control2 : UserControl {
    
  5. Create the class Control2_ToolboxItem and add it to the end of Control2.cs, just before the namespace closing bracket This ToolboxItem derived class has a constructor that takes an argument of type Control2 and adds it to the toolbox. The Control2_ToolboxItem class must have SerializableAttribute applied to it.

    [SerializableAttribute()]  //ToolboxItem implementations have this attribute.
    internal class Control2_ToolboxItem : ToolboxItem {
        public Control2_ToolboxItem(Type type)
            : base(type) {
        }
        public override void Initialize(Type type){ //Initialize is called by the base constructor.
            if(!type.Equals(typeof(Control2))) {
                throw new ArgumentException("Bad constructor argument " +
                    typeof(Control2_ToolboxItem).FullName + " takes only " + typeof(Control2).FullName +
                    "objects as an argument to its constructor");
            } else {
                base.Initialize(type);
            }
        }
    }
    
  6. From the File menu, choose Save to save the file.

The two instances of ToolboxBitmapAttribute used above indicate that the project represents the two controls with these icons:

  • Control1.bmp, found in the namespace containing the class Control1

  • Control2.bmp, found in the namespace containing the class Control2.

To embed bitmap icons for toolbox item

  1. Add a new bitmap to the project:

    1. Right-click the project ItemConfiguration.

    2. Point to Add, and then click New Item.

    3. In the Add New Item dialog box, select Bitmap File, supplying Control1.bmp as the name.

  2. Set the size of the icon to 16∝16.

    1. Click the View menu and choose Properties Window.

    2. In the Properties window, set Height and Width to 16.

  3. Use the bitmap editor found in Visual Studio to create an image.

  4. In Solution Explorer right-click Control1.bmp or Control2.bmp, and then choose Properties.

  5. Set the Build Action property to Embedded Resource.

  6. Repeat the above steps to create Control2.bmp.

  7. From the File menu, choose Save to save the file.

In this section, supply and register a class implementing IconfigureToolboxItem, which will be instantiated and used by the Visual Studio IDE to configure Toolbox controls.

NoteNote

Because the Visual Studio environment instantiates an instance of the implementation of IConfigureToolboxItem, do not implement the IConfigureToolboxItem interface on the class implementing Package for a VSPackage.

To create and register a toolbox control configuration object

  1. Using Solution Explorer, add a new class object named ToolboxConfig in a file named ToolboxConfig.cs to the project ItemConfiguration:

    1. In Solution Explorer, right-click the ItemConfiguration project and click Add.

    2. In the Add New Item dialog box, select Class, supplying ToolboxControl as the name.

      For more information on how to add new items to a project, see How to: Add New Project Items

  2. Include the needed namespaces:

    The implementation of this toolbox control configuration object requires the addition of following namespaces:

    using Microsoft.VisualStudio.Shell;
    using System.Runtime.InteropServices;
    using System.Reflection;
    using System.Collections;
    using System.ComponentModel;
    using System.Drawing.Design;
    
  3. Make the class ToolboxConfig public, derive it from IConfigureToolboxItem, and apply a GUID to the class:

    [GuidAttribute("YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY")]

    public class ToolboxConfig : IConfigureToolboxItem {

  4. Apply an instance of ProvideAssemblyFilterAttribute to the class ToolboxConfig, constructed with an assembly name of "ItemConfiguration, Version=*, Culture=*, PublicKeyToken=*"

    This will restrict ToolboxConfig to working on ToolboxItem objects provided by the assembly containing the current VSPackage:

        [ProvideAssemblyFilterAttribute("ItemConfiguration, Version=*, Culture=*, PublicKeyToken=*")]

    public class ToolboxConfig : IConfigureToolboxItem {

  5. Implement the ConfigureToolboxItem method of IConfigureToolboxItem interface so the method only acts on the two ToolboxItem objects, Control1 and Control2, provided by the VSPackage under the Vsip.ItemConfiguration namespace.

    The implementation of ConfigureToolboxItem applies instances of ToolboxItemFilterAttribute to the two ToolboxItem objects such that:

    1. The ToolboxItem implemented by Control1 is always and only available to the Toolbox for designers handling UserControl objects.

    2. The ToolboxItem implemented by Control2 is never available to the Toolbox for designers handling UserControl objects.

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Drawing.Design;
    
    using Microsoft.VisualStudio.Shell;
    using System.Runtime.InteropServices;
    using System.Reflection;
    using System.Collections;
    using System.ComponentModel;
    using System.Drawing.Design;
    
    namespace Vsip.ItemConfiguration {
        [ProvideAssemblyFilterAttribute("Vsip.ItemConfiguration.*, Version=*, Culture=*, PublicKeyToken=*")]
        public class ToolboxConfig : IConfigureToolboxItem {
            public ToolboxConfig() {
            }
            public void ConfigureToolboxItem(ToolboxItem item) {
                if (item == null){
                    return;
                }
                //hide from .NET Compact Framework on the device designer.
                ToolboxItemFilterAttribute newFilter = null;
    
                if (item.TypeName == typeof(Control1).ToString()) {
                    newFilter = new ToolboxItemFilterAttribute("System.Windows.Forms.UserControl",
                                                          ToolboxItemFilterType.Require);
                } 
                else if (item.TypeName == typeof(Control2).ToString()) {
    
                    newFilter = new ToolboxItemFilterAttribute("System.Windows.Forms.UserControl",
                                                          ToolboxItemFilterType.Prevent);
                }
                if (newFilter != null) {
                    ArrayList array = new ArrayList();
                    array.Add(newFilter);
                    item.Filter = (ToolboxItemFilterAttribute[])
                            array.ToArray(typeof(ToolboxItemFilterAttribute));
                }
            }
        }
    }
    
    

The default implementation of the VSPackage provided by the Visual Studio wizard must be modified to:

  • Register support for being a Toolbox item provider.

  • Register the class providing dynamic Toolbox control configuration for the VSPackage.

  • Use the ToolboxService to load all the ToolboxItem objects provided by the VSPackage's assembly.

  • Handle ToolboxInitialized and ToolboxUpgraded events.

To modify the Package implementation for a toolbox item provider on the VSPackage

  1. In Solution Explorer, right-click VsPkg.cs, and click View Code to edit the ItemConfiguration class.

  2. Modify the declaration of the ItemConfiguration class, the solution's implementation of the Package class.

    1. Add the following namespaces to VsPkg.cs.

    using System.Collections;
    using System.ComponentModel;
    using System.Drawing.Design;
    using System.Reflection;
    
    1. Register the VSPackage as a ToolboxItem by adding an instance of ProvideToolboxItemsAttribute.

          [ProvideToolboxItemsAttribute(1)]

      See the listing below for placement.

      NoteNote

      The sole argument of ProvideToolboxItemsAttribute is the version of ToolboxItem provided by the VSPackage. Changing this value forces the IDE to load the VSPackage even if it has an earlier cached version of ToolboxItem.

    2. Register the ToolboxConfig class a providing Toolbox control dynamic configuration by applying an instance of ProvideToolboxItemConfigurationAttribute constructed using System.Type of ToolboxConfig as an argument:

          [ProvideToolboxItemConfigurationAttribute(typeof(ToolboxConfig))]

    3. Creating two new private fields in the ItemConfiguration class:

      A ArrayList member called ToolboxItemList to hold a list of the ToolboxItem objects the ItemConfiguration class manages.

      private ArrayList ToolboxItemList = null;
      

      A String named CategoryTab containing the Toolbox category or tab which is used to hold the ToolboxItem objects managed by the ItemConfiguration class.

      private String CategoryTab = "ItemConfiguration Walkthrough";
      

    The result of this modification is:

    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.InteropServices;
    using System.ComponentModel.Design;
    using Microsoft.Win32;
    using Microsoft.VisualStudio.Shell.Interop;
    using Microsoft.VisualStudio.OLE.Interop;
    using Microsoft.VisualStudio.Shell;
    using MSVSIP = Microsoft.VisualStudio.Shell;
    
    using System.Collections;
    using System.ComponentModel;
    using System.Drawing.Design;
    using System.Reflection;
    
    namespace Vsip.ItemConfiguration
    {
        [ProvideToolboxItemsAttribute(1)]
        [ProvideToolboxItemConfigurationAttribute(typeof(ToolboxConfig))]
        [MSVSIP.DefaultRegistryRoot("Software\\Microsoft\\VisualStudio\\8.0")]
        [MSVSIP.InstalledProductRegistration(false, "#100", "#102", "1.0", IconResourceID = 400)]
        [MSVSIP.ProvideLoadKey("Standard", "1.0", "Package Name", "Company", 1)]
        [MSVSIP.ProvideMenuResource(1000, 1)]
    
        [GuidAttribute("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]   //GUID in this format
        public class ItemConfiguration : MSVSIP.Package
        {
            private ICollection ToolboxItemList = null;
            private String CategoryTab = " ItemConfiguration Walkthrough";
            public ItemConfiguration(){
                Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));
            }
    
  3. Modify the Initialize method in ItemConfiguration to:

    1. Subscribe to the ToolboxInitialized and ToolboxUpgraded events.

    2. Call the GetToolboxItems method of the System.Drawing.Design.ToolboxService on the currently executing assembly to fill the ICollection object ToolboxItemList. The ToolboxItemList will contain a list of all the ToolboxItems that ItemConfiguration manages and will be used by the OnToolboxInitialized and OnToolboxUpgraded event handlers to load the Toolbox.

          protected override void Initialize()
          {
              Trace.WriteLine (string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));
              base.Initialize();
      
              // Add our command handlers for menu (commands must exist in the .ctc file)
              OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
              if ( null != mcs )
              {
                  // Create the command for the menu item.
                  CommandID menuCommandID = new CommandID(GuidList.guidItemConfigurationCmdSet, 
                      (int)PkgCmdIDList.cmdidMyCommand);
                  MenuCommand menuItem = new MenuCommand( new EventHandler(MenuItemCallback), menuCommandID );
                  mcs.AddCommand( menuItem );
              }
              ToolboxInitialized += new EventHandler(OnToolboxInitialized);
              ToolboxUpgraded += new EventHandler(OnToolboxUpgraded);
              //Use the ToolboxService to get a list of all the toolbox items in our assembly
              ToolboxItemList = ToolboxService.GetToolboxItems(Assembly.GetExecutingAssembly(), "");
              //CreateItemList(Assembly.GetExecutingAssembly());
              if (ToolboxItemList == null)
              {
                  throw new ApplicationException("Unable to generate a toolbox Items listing for "
                                              + GetType().FullName);
              }
          } 
      }
      
  4. Implement the method OnToolBoxInitialized, the handler for the ToolboxInitialized event.

    The OnToolboxInitialized method uses the list of ToolboxItem objects contained in the ToolboxItemList member of the ItemConfiguration class and:

    1. Determines if any ToolboxItem objects managed by the ItemConfiguration VSPackage are already present in the Toolbox category defined by the variable CategoryTab, which is set to the string "ItemConfiguration Walkthrough".

      Any instances of the ItemConfiguration class's ToolboxItem objects found are removed from the Toolbox.

    2. Adds new instances of all ToolboxItem objects listed in ToolboxItemList to the category "Walkthrough Tab".

    3. Sets the category "ItemConfiguration Walkthrough" as the Toolbox's active tab.

      private void OnToolboxInitialized(object sender, EventArgs e) {
                  //Add new instances of all ToolboxItems contained in ToolboxItemList, 
                  //if a duplicate or near-duplicate exists, remove it.
      
                  IToolboxService toolboxService = GetService(typeof(IToolboxService)) as IToolboxService;
                  IVsToolbox toolbox = GetService(typeof(IVsToolbox)) as IVsToolbox;
      
                  toolboxService.Refresh();
                  //Remove target tab and all controls under it.
                  foreach (ToolboxItem oldItem in toolboxService.GetToolboxItems(CategoryTab)) {
                      toolboxService.RemoveToolboxItem(oldItem);
                  }
                  toolbox.RemoveTab(CategoryTab);
                  
                  foreach (ToolboxItem itemFromList in ToolboxItemList) {
      
                      //Make sure to clean up old item
                      foreach (ToolboxItem itemOnTab in toolboxService.GetToolboxItems(CategoryTab)) {
                          if (itemOnTab.Equals(itemFromList)) {
                              toolboxService.RemoveToolboxItem(itemOnTab);
                          }
                      }
                      toolboxService.AddToolboxItem(itemFromList, CategoryTab);
                      Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Adding item {0} using control {1} to tab {2} on the Toolbox",
                          itemFromList.ToString(), itemFromList.TypeName, CategoryTab));
                      //Set the current Tab and item as selected.
                      toolboxService.SelectedCategory = CategoryTab;
                  }
                  toolboxService.Refresh();
              }
      
  5. Implement the method OnToolboxUpgrade, the handler for the ToolboxUpgraded event. The OnToolboxUpgraded method:

    1. Removes any existing instance of a ToolboxItem managed by the ItemConfiguration VSPackage currently present in the Toolbox category "Walkthrough Tab".

    2. Inserts any ToolboxItem listed in the ToolboxItemList that is not present in the "ItemConfiguration Walkthrough" category.

      private void OnToolboxUpgraded(object sender, EventArgs e) {
                  IToolboxService toolboxService = GetService(typeof(IToolboxService)) as IToolboxService;
                  //Remove all current items not in upgrade item list
                  foreach (ToolboxItem currentItem in toolboxService.GetToolboxItems(CategoryTab)) {
                          toolboxService.RemoveToolboxItem(currentItem);
                  }
                      //Add Upgraded items.
                  foreach (ToolboxItem newItem in ToolboxItemList){
                          toolboxService.AddToolboxItem(newItem);
                      }
                      toolboxService.Refresh();
              }
      
      NoteNote

      As an exercise, one could consider developing a mechanism for testing the version of the VSPackage or the items and only update if the VSPackage's version has changed, or if the version of the ToolboxItem has changed.

To implement a command to initialize the Toolbox

  • Change the menu item's command handler method MenuItemCallBack.

    1. Right-click VsPackage.cs and choose View Code. This opens the file in the text editor.

    2. Replace the existing implementation of MenuItemCallBack with:

          private void MenuItemCallback(object sender, EventArgs e) {
      
              IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
              IVsPackage pkg = GetService(typeof(Package)) as IVsPackage;
              pkg.ResetDefaults((uint)__VSPKGRESETFLAGS.PKGRF_TOOLBOXITEMS);
              
          }
      

To build the solution

  1. Choose the Rebuild Solution command on the Visual Studio IDE's Build menu.

  2. Start a second instance of the Visual Studio IDE running using the experimental registry hive. This can be done one of two ways:

    • The Visual Studio IDE automatically starts another instance of the Visual Studio IDE running if you choose either the Start or Start with Debugging command from the Debug menu.

    • By manually starting a second copy of the Visual Studio IDE from the command prompt. At the command prompt, type:

      devenv /rootsuffix exp

      For more information on using the experimental registry hive, see Experimental Build.

  3. A menu item labeled Initialize ItemConfiguration should appear at the top of the Visual Studio Tools menu beside an icon with the number 1 on it.

Once a new instance of the Visual Studio IDE starts using the experimental registry hive, the walkthrough can be verified by creating a Visual Studio Solution using both the Form and UserControl designers.

To exercise this walkthrough

  1. Create a new Windows application using Visual C# or Visual Basic:

    • On the Visual Studio File menu, point to New, and then click Project to open the New Project dialog box.

    • On the New Project dialog box, select Windows Application under either the Visual Basic or Visual C# list.

      A Form-based designer should appear as part of the Startup Project of the newly created Solution for a class name Form1 contained in the file Form1.cs or Form1.vb.

  2. To provide a UserControl designer, add a UserControl to the solution.

    • Right-click the Startup Project.

    • In Solution Explorer, right-click the Startup Project in the newly created Solution and choose Add.

    • On the Add menu, click New Item.

    • In the Add New Item dialog box, select User Control.

      A UserControl-based designer should appear as part of the Startup Project of the newly created solution for a class named UserControl1 contained in the file UserControl1.cs or UserControl1.vb.

  3. Make sure the Toolbox is visible. If the Toolbox is not visible, make it visible by clicking on the IDE's View menu and choosing Toolbox.

  4. If the ItemConfiguration Walkthrough category does not appear when the Toolbox is visible, re-initialize the Toolbox by clicking the Initialize ItemConfiguration command on the Tools menu.

  5. In Solution Explorer, right-click Form1.cs (or Form1.vb) and choose View Designer.

    The Control2 ToolboxItem should appear under the ItemConfiguration Walkthrough category, but not Control1.

  6. In Solution Explorer, right-click UserControl1.cs (or UserContol1.vb) and choose View Designer.

    The Control1 ToolboxItem should appear under the ItemConfiguration Walkthrough category, but not Control2.

Because the ProvideAssemblyFilterAttribute applied to ToolboxConfig was constructed with the string Vsip.ItemConfiguration *, Version=*, Culture=*, PublicKeyToken=*", only ToolboxItem objects in the walkthrough's assembly will be acted on by the ToolboxConfg class's ConfigureToolboxItem method.

Therefore, whenever the ItemConfiguration Walkthrough category is active on the Toolbox, the ToolboxConfg class's ConfigureToolboxItem method is called and ToolboxItemFilterAttribute instances are applied on the ToolboxItem objects implemented by Control1 and Control2.

Community Additions

ADD
Show:
© 2014 Microsoft