I've been tasked with writing a custom install-action, to add a few user controls to the VS.NET toolbox. "No problem", I said.
Word? Schmurd. This VS.NET ToolBox can o' worms has turned out to be a new low in application-integration migraine severity...
There exists an innocent little API function, ToolBoxItems.Add(), in the EnvDTE assembly. Of course, its documentation is a masterpiece of misinformation...
I Google'd long and hard, and all I could scrape up was anecdotal evidence from various folks that it was possible -- somehow -- I just had to work around a few quirks. What those quirks were, exactly, and how to workaround them...? Not much consistent info to go on.
All I knew for sure was that calling ToolBoxItems.Add, with every reasonable combination of typename and assemblyname for parameters, didn't work.
Some folks said I have to index the tabs by number (what?), some folks said I have to load a Solution file first (yikes!), and some folks said I have to display the Properties window first... Unlikely as it sounds, that latter suggestion (plus or minus a few other non-intuitive stumbling blocks) turned out to be the case!
Think: EnvDTE.DTE.ExecuteCommand("View.PropertiesWindow");
There is clearly a very deep-running bug in VS.NET, regarding this Properties window. I've noticed it flashing briefly into existance, quite often, even when I have it hidden -- I somehow suspect that this "workaround" is well-known within the halls of Building 40. Grrr... Hopefully this is fixed in Everett, but of course I can't afford to care about that.
So, without further ado, here is the magic sequence to add InkPicture and InkEdit, from the Microsoft Tablet PC SDK, onto the VS.NET Toolbox. This code can be run from any standalone app -- it does not need to be run from within an addin. (In fact, all instances of VS should be closed.) Hope this helps somebody...
private static void AddToolBoxTab(EnvDTE.DTE env)
{
string fullpathToMicrosoftInk = LookupMicrosoftInkAssemblyFilename();
EnvDTE.Window toolboxWindow = env.Windows.Item(EnvDTE.Constants.vsWindowKindToolbox);
EnvDTE.ToolBox toolbox = (EnvDTE.ToolBox)toolboxWindow.Object;
EnvDTE.ToolBoxTabs toolboxTabs = toolbox.ToolBoxTabs;
foreach (EnvDTE.ToolBoxTab tab in toolboxTabs)
{
if (tab.Name == TabName)
return;
}
EnvDTE.ToolBoxTab newtab = toolboxTabs.Add(TabName);
env.ExecuteCommand("View.PropertiesWindow", "");
newtab.Activate();
newtab.ToolBoxItems.Item(1).Select();
newtab.ToolBoxItems.Add(
@"Unused?",
fullpathToMicrosoftInk,
EnvDTE.vsToolBoxItemFormat.vsToolBoxItemFormatDotNETComponent
);
}
private static string LookupMicrosoftInkAssemblyFilename()
{
Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.LocalMachine;
rk = rk.OpenSubKey(KeyName, false);
if (rk == null)
throw new ArgumentException(String.Format("Could not find registry key:\n[HKLM\\{0}]",KeyName));
string fullpathToAssembly = (string)rk.GetValue(null);
fullpathToAssembly += @"\Microsoft.Ink.dll";
if (!System.IO.File.Exists(fullpathToAssembly))
throw new System.IO.FileNotFoundException("File not found", fullpathToAssembly);
return fullpathToAssembly;
}