I started to address this topic during this previous post, but it seemed like it was worth coming back to.
This time I'm looking at a different technique: to create our own partial CUI file programmatically using the Autodesk.AutoCAD.Customization functionality, save it to disk and then make sure it's loaded at the beginning of every subsequent AutoCAD session.
I'm not going to focus on adding menus etc. into AutoCAD's list - that's left for a future post - this is mainly about the logic needed to make sure a CUI is created and loaded.
Here's some C# code I put together (with the help of some pointers I took from Wayne Brill, a member of our DevTech Americas team). There's more than one way to skin a cat, as they say, but this seems a reliable way to make sure our partial menu is created and loaded. By the way, you'll need to add AcCui.dll as an assembly reference to your project for this code to build.
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Customization;
using System;
using System.Collections.Specialized;
namespace PartialCUI
{
public class Commands : IExtensionApplication
{
public void Initialize()
{
BuildMenuCUI();
}
public void Terminate()
{
}
[CommandMethod("bm")]
public void BuildMenuCUI()
{
const string myCuiFile = "c:\\kean.cui";
const string myCuiFileToSend = "c:/kean.cui";
const string myCuiSectionName = "Kean";
Editor ed =
Application.DocumentManager.MdiActiveDocument.Editor;
string mainCui =
Application.GetSystemVariable("MENUNAME") + ".cui";
CustomizationSection cs =
new CustomizationSection(mainCui);
PartialCuiFileCollection pcfc = cs.PartialCuiFiles;
if (pcfc.Contains(myCuiFile))
{
ed.WriteMessage(
"\nCustomization file \""
+ myCuiFile
+ "\" already loaded."
);
}
else{
if (System.IO.File.Exists(myCuiFile))
{
ed.WriteMessage(
"\nCustomization file \""
+ myCuiFile
+ "\" exists - loading it."
);
LoadMyCui(myCuiFileToSend);
}
else
{
ed.WriteMessage(
"\nCustomization file \""
+ myCuiFile
+ "\" does not exist - building it."
);
// Create a customization section for our partial menu
CustomizationSection pcs = new CustomizationSection();
pcs.MenuGroupName = myCuiSectionName;
// Let's add a menu group, with two commands
MacroGroup mg =
new MacroGroup(myCuiSectionName, pcs.MenuGroup);
MenuMacro mm1 =
new MenuMacro(mg, "Cmd 1", "^C^CCmd1", "ID_MyCmd1");
MenuMacro mm2 =
new MenuMacro(mg, "Cmd 2", "^C^CCmd2", "ID_MyCmd2");
// Now let's add a pull-down menu, with two items
StringCollection sc = new StringCollection();
sc.Add("POP15");
PopMenu pm =
new PopMenu(
myCuiSectionName,
sc,
"ID_MyPop1",
pcs.MenuGroup
);
PopMenuItem pmi1 =
new PopMenuItem(mm1, "Pop Cmd 1", pm, -1);
PopMenuItem pmi2 =
new PopMenuItem(mm2, "Pop Cmd 2", pm, -1);
// Finally we save the file and load it
pcs.SaveAs(myCuiFile);
LoadMyCui(myCuiFileToSend);
}
}
}
private void LoadMyCui(string cuiFile)
{
// This load technique sends a LISP string to the
// command line (which avoid us having to set FILEDIA
// to 0) after setting CMDECHO to 0, to minimize
// what's displayed.
// We make sure the LISP string resets the value of
// CMDECHO at the end (the string is executed
// asynchronously, so we don't have the chance to do
// it in our calling function).
Document doc =
Application.DocumentManager.MdiActiveDocument;
object oldCmdEcho = Application.GetSystemVariable("CMDECHO");
Application.SetSystemVariable("CMDECHO", 0);
doc.SendStringToExecute(
"(command \"_.CUILOAD\" \""
+ cuiFile
+ "\")(setvar \"CMDECHO\" "
+ oldCmdEcho
+ ")(princ) "
, false, false, false
);
}
}
}
Here's what happens when we first load our module:
Command: netload
Customization file "c:\kean.cui" does not exist - building it.
Command:
Customization file loaded successfully. Customization Group: KEAN
Command:
Here's what happens when we then run our manual command - "bm" - which calls the same code:
Command: bm
Customization file "c:\kean.cui" already loaded.
Command:
Once created and loaded, the partial menu should be loaded automatically on AutoCAD startup - as shown by the running the CUILOAD command:
If we then unload the file and launch AutoCAD again, loading our module, we see this:
Command: netload
Customization file "c:\kean.cui" exists - loading it.
Command:
Customization file loaded successfully. Customization Group: KEAN
Command:
Update:
The above implementation of the LoadMyCui function has some unfortunate behaviour: it doesn't actually cause the menu to be added to the menu bar. Calling CUILOAD from LISP, should be identical to calling the command directly, but in this case it doesn't appear to be. Thanks to Hongxian Qin, from DevTech China, for analysing the problem. The following implementation works as the code was designed to:
private void LoadMyCui(string cuiFile)
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
object oldCmdEcho =
Application.GetSystemVariable("CMDECHO");
object oldFileDia =
Application.GetSystemVariable("FILEDIA");
Application.SetSystemVariable("CMDECHO", 0);
Application.SetSystemVariable("FILEDIA", 0);
doc.SendStringToExecute(
"_.cuiload "
+ cuiFile
+ " ",
false, false, false
);
doc.SendStringToExecute(
"(setvar \"FILEDIA\" "
+ oldFileDia.ToString()
+ ")(princ) ",
false, false, false
);
doc.SendStringToExecute(
"(setvar \"CMDECHO\" "
+ oldCmdEcho.ToString()
+ ")(princ) ",
false, false, false
);
}