cad.net 操作cui和cuix含工具条

版本说明

本文测试为Acad2008(Cui)和Acad2021(Cuix)的案例,应该能够涉及所有高低版本的操作.
Cui是个xml,Cuix是个压缩包...知道好像没什么好处,还是应该调用管理器操作而不是序列化操作,除非你发现了其中的一些bug无法避免...

关于宏的说明,看这个桌子的链接

特别的bug

public static class CuiStatic
{
  #if NET35
    //为了让Acad2008配合高版本的API,所以写了个拓展方法给它
    public static bool RemovePartialMenu(this CustomizationSection cs, string fileName, string menuGroupName)
    {
        return cs.RemovePartialMenu(fileName);//卸载局部cui
    }
  #endif
}

然后 cs.RemovePartialMenu 这个万恶之源,令我在测试Acad2021移除未融入的cuix的时候是失效的,为什么失效呢?

在我百思不得其解的时候,执行了一次"_.CuiUnLoad"自带的卸载命令,它会将cuix某些错误修复了,

然后再执行 cs.RemovePartialMenu 是成功的了.

修改主要Cui和Cuix

using Autodesk.AutoCAD.Customization;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
//需要引用 AcCui.dll

namespace JoinBox.Menu
{
    // 修改mainCui文件,可以考虑更换为修改局部cui替代
    // 参考来自 https://adndevblog.typepad.com/autocad/2012/12/customizing-double-click-on-block-reference.html
    public partial class Cmd
    {
        [CommandMethod("ChangeMacroCUI")]
        public void ChangeMacroCUI()
        {
            var cs = Cui.CuiMain(out string expand);
            //遍历所有双击动作
            DoubleClickAction ad = null;
            foreach (DoubleClickAction dca in cs.MenuGroup.DoubleClickActions)
            {
                if (dca.Name == "块" || dca.Name == "Block")
                {
                    ad = dca;
                    var ma = ad.DoubleClickCmd.MenuMacroReference.macro;
#if true
                    ma.Command = "$M =$(if,$(and,$(>,$(getvar,blockeditlock),0)),^C^C_properties,^C^C_bedit)"; //原始的
#else
                    ma.Command = $"^C^C_{Cmd_MyBEdit}";//新创建的命令
#endif
                }
                if (dca.Name == "属性块")
                {
                    ad = dca;
                    var ma = ad.DoubleClickCmd.MenuMacroReference.macro;
#if true
                    ma.Command = "^C^C_eattedit"; //原始的
#else
                    ma.Command = $"^C^C_{Cmd_MyBEdit}";//新创建的命令
#endif
                }
            }
            if (cs.IsModified)
            {
                cs.SaveAs(cs.CUIFileName);//保存覆盖掉主cui文件
                CuiStatic.CuiAcedReloadMenus();
            }
        }

        const string Cmd_MyBEdit = "MyBEdit";
        [CommandMethod(Cmd_MyBEdit, CommandFlags.UsePickSet)]
        public void MyBeditCommand()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;
            var db = doc.Database;
            string regAppName = "MyApp";

            ed.WriteMessage("\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
            return;

            var result = ed.GetSelection();
            if (result.Status != PromptStatus.OK)
            {
                return;
            }
            var ss = result.Value;
            foreach (SelectedObject so in ss)
            {
                bool isMyBlockRef = false;
                db.Action(tr =>
                {
                    var ent = so.ObjectId.ToEntity(tr);
                    // 让我们检查XDATA以识别我们是否需要显示我们的对话框
                    if (ent.GetXDataForApplication(regAppName) != null)
                    {
                        isMyBlockRef = true;
                    }
                });

                if (isMyBlockRef)
                {
                    Application.ShowAlertDialog("定制行动,如显示我们的表格。");
                }
                else
                {
                    // 让Auto CAD进行块编辑。
                    ObjectId[] ids = ss.GetObjectIds();
                    ed.SetImpliedSelection(ids);
                    doc.SendStringToExecute("_BEDIT ", false, false, false);
                }
            }
        }
    }
}

修改局部Cui和Cuix

大多数时候不需要修改主Cui而是用局部Cui,因为局部Cui会替代主Cui的动作.

using System.IO;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Customization;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;

namespace JoinBox.Menu
{
    // 修改局部cui文件
    // 参考来自: https://through-the-interface.typepad.com/through_the_interface/2007/05/creating_a_part.html
    public partial class Cmd
    {
        /// <summary>
        /// 建立CUI菜单
        /// </summary>
        [CommandMethod("BuildMenuCUI")]
        public void BuildMenuCUI()
        {
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
            var pcfs = Cui.CuiMain(out string expand).PartialCuiFiles;//局部cui

            string myCuiName = "Kean";
            string myCuiFile = AutoGo.ConfigPath + myCuiName + expand;
            string msg = $"\n自定义CUI文件: \"{myCuiFile}\" ";

            if (pcfs.Contains(myCuiFile))
            {
                ed.WriteMessage(msg + "已经加载;");
                ed.WriteMessage("\n执行卸载");
                Cui.UnLoad(myCuiName);
                ed.WriteMessage("\n卸载成功");
                return;
            }

            if (File.Exists(myCuiFile))
            {
                ed.WriteMessage(msg + "存在,");
            }
            else
            {
                ed.WriteMessage(msg + "不存在建立它,");
                var pcs = CuiCreate(myCuiName, "Attblockref");//属性块
                pcs.SaveAs(myCuiFile);   //保存文件可能因为c盘权限而失败,更换到其他盘
            }

            ed.WriteMessage("执行加载\r");
            Cui.Load(myCuiFile);
            CuiStatic.CuiAcedReloadMenus();
        }

        /// <summary>
        /// 创建CUI菜单双击
        /// </summary>
        /// <param name="cuiName">cui菜单的名称</param>
        /// <param name="dxfName">限定双击对象的类型,例如"Circle" "Attblockref"</param>
        /// <returns></returns>
        private static CustomizationSection CuiCreate(string cuiName, string dxfName)
        {
            // 为我们的部分菜单创建自定义部分
            var pcs = new CustomizationSection
            {
                MenuGroupName = cuiName
            };

            string doubleClick = "DoubleClick";
            //cui双击事件名称...这里也是命令定义的名称,需要用这个名称去动态编译一个命令出来...
            string actionName = $"{doubleClick}ActionName_{cuiName}";

            //创建双击动作...局部的定义会覆盖掉主cui的定义,覆盖顺序未知
            var dca = new DoubleClickAction(pcs.MenuGroup, actionName, -1)
            {
                Description = "双击自定义",
                ElementID = "EID_" + actionName,
                DxfName = dxfName
            };

            var macGroup = new MacroGroup($"MacroGroup_{cuiName}", pcs.MenuGroup);
            //actionName是执行的命令,你把他当成命令的唯一身份证号码
            var createMenuMacro = macGroup.CreateMenuMacro(actionName, $"^C^C_{actionName}", $"ID_{dxfName}_{doubleClick}_{cuiName}");

            var cmd = new DoubleClickCmd(dca)
            {
                MacroID = createMenuMacro.ElementID
            };
            dca.DoubleClickCmd = cmd;
            return pcs;
        }

        //这里还没有完成动态编译.......就将就一下
        const string Cmd_double = "DoubleClickActionName_Kean";//CuiCreate提供的...最好修改成动态编译
        [CommandMethod(Cmd_double)]
        public void DoubleClickActionName_Kean()
        {
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
            ed.WriteMessage("\n双击属性块执行了我耶");
        }
    }
}

Cui类

using System;
using System.Text;
using System.IO;
using Autodesk.AutoCAD.Customization;
using Autodesk.AutoCAD.ApplicationServices;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;

namespace JoinBox.Menu
{
    public class Cui
    {
        /// <summary>
        /// 打开主CUI文件
        /// </summary>
        /// <param name="expand">拓展名</param>
        /// <returns></returns>
        public static CustomizationSection CuiMain(out string expand)
        {
            expand = "";
            //打开主CUI文件,可以考虑局部cui文件
            string old = (string)Acap.GetSystemVariable("MENUNAME");
            var mainCuiFile = old;
            if (mainCuiFile == null)
            {
                throw new ArgumentNullException(nameof(CuiMain));
            }
            if (File.Exists(mainCuiFile + ".cuix"))
            {
                mainCuiFile += ".cuix";//高版本
                expand = ".cuix";
            }
            else if (File.Exists(mainCuiFile + ".cui"))
            {
                mainCuiFile += ".cui";//低版本08
                expand = ".cui";
            }
            if (mainCuiFile == old)
            {
                throw new ArgumentNullException("没有找到对应的cui文件:" + nameof(CuiMain));
            }
            //反序列化这个文件打开
            return new CustomizationSection(mainCuiFile);
        }

        /// <summary>
        /// 加载CUI文件
        /// </summary>
        /// <param name="cuiFile">cui文件路径</param>
        public static void Load(string cuiFile)
        {
            SendCmd("_.CuiLoad", new string[] { cuiFile });
            //var cs = CuiMain(out string expand);
            //cs.AddPartialMenu()//加载局部cui,加载类的
        }

        /// <summary>
        /// 卸载CUI文件
        /// </summary>
        /// <param name="cuiMenuGroupName">组名</param>
        public static void UnLoad(string cuiMenuGroupName)
        {
            // AccordingCadLispSendCmd("_.CuiUnLoad", new string[] { cuiMenuGroupName });

            var cs = CuiMain(out string expand);
            UnLoad(cs, cuiMenuGroupName);
        }

        /// <summary>
        /// 卸载局部cui
        /// </summary>
        /// <param name="cs">MainCui</param>
        /// <param name="removeCuiFile">指定移除的文件</param>
        /// <param name="removeBadFile">移除坏掉的cui</param>
        static void UnLoad(CustomizationSection cs, string removeCuiFile, bool removeBadFile = true)
        {
            var pcfs = cs.PartialCuiFiles;//局部cui

            //移除坏掉的cui(删除路径的文件之后显示未融入的)
            if (removeBadFile)
            {
                for (int i = 0; i < pcfs.Count; i++)
                {
                    var fileName = pcfs[i];
                    if (!File.Exists(fileName))
                    {
                        RemovePartialMenu(cs, pcfs, fileName);
                        i--;
                    }
                }
            }

            //移除指定名称的局部cui文件
            for (int i = 0; i < pcfs.Count; i++)
            {
                var fileName = pcfs[i];
                if (removeCuiFile.ToUpper() == GetFileName(fileName).ToUpper())
                {
                    RemovePartialMenu(cs, pcfs, fileName);
                    i--;
                }
            }

            if (cs.IsModified)
            {
                cs.Save();
            }
        }

        /// <summary>
        /// 移除局部cui
        /// </summary>
        /// <param name="cs"></param>
        /// <param name="pcfs"></param>
        /// <param name="fileName"></param>
        static void RemovePartialMenu(CustomizationSection cs, PartialCuiFileCollection pcfs, string fileName)
        {
            //Acad2008执行RemovePartialMenu会移除了pcfs元素和MainCui的元素
            //Acad2021执行RemovePartialMenu是true,但是没有和Acad08一样
            var aa2 = cs.RemovePartialMenu(fileName, null);//移除局部cui
            if (aa2 && pcfs.Contains(fileName))
            {
                pcfs.Remove(fileName);

                // 如果Acad2021移除失败了,证明了是第一次移除,可能存在cuix的错误导致.
                // 利用lisp语句移除是正确的,此命令会导致成功修复了某些东西.
                // 毕竟修复了一次之后执行上面就是正确的了,不知道怎么复现.
                SendCmd("_.CuiUnLoad", new string[] { fileName });

                // https://adndevblog.typepad.com/autocad/2012/07/unload-partial-cuix-when-autocad-quits.html
                // 这句无法工作
                // var aa3 = Acap.UnloadPartialMenu(fileName);//卸载局部cui
            }
        }

        /// <summary>
        /// 路径或(文件名.后缀)获取文件名,因为不存在cui路径时候会未融入
        /// </summary>
        /// <param name="fileOrPath"></param>
        /// <returns></returns>
        static string GetFileName(string fileOrPath)
        {
            var a = fileOrPath.LastIndexOf('\\') + 1;
            var b = fileOrPath.LastIndexOf('.');
            var groupName = fileOrPath.Substring(a, b - a);
            return groupName;
        }

        /// <summary>
        /// 以lisp的方式发送命令
        /// </summary>
        /// <param name="cuiFile"></param>
        static void SendCmd(string cmd, string[] args)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;

            object oldCmdEcho = Application.GetSystemVariable("CMDECHO");
            object oldFileDia = Application.GetSystemVariable("FILEDIA");

            Application.SetSystemVariable("CMDECHO", 0);
            Application.SetSystemVariable("FILEDIA", 0);

            //界面还没准备好导致这里会出错
            var arg = new StringBuilder();
            foreach (var item in args)
            {
                arg.Append(item);
            }
            doc.SendStringToExecute(cmd + " " + arg + " ", false, false, false);
            doc.SendStringToExecute("(setvar \"FILEDIA\" " + oldFileDia.ToString() + ")(princ) ", false, false, false);
            doc.SendStringToExecute("(setvar \"CMDECHO\" " + oldCmdEcho.ToString() + ")(princ) ", false, false, false);
        }
    }
}

CuiStatic类

using System.Runtime.InteropServices;
using Autodesk.AutoCAD.Customization;

namespace JoinBox.Menu
{
    public static class CuiStatic
    {
#if NET35
        //为了让Acad2008配合高版本的API,所以写了个拓展方法给它
        public static bool RemovePartialMenu(this CustomizationSection cs, string fileName, string menuGroupName)
        {
            return cs.RemovePartialMenu(fileName);//卸载局部cui
        }
#endif

#if NET35
        /// <summary>
        /// 重新载入CUI文件,刷新当前工作区2008
        /// </summary>
        /// <returns></returns>
        [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedReloadMenus")]
        public static extern int CuiAcedReloadMenus();
#else
    
       /* 这里未能详尽所有的版本 .*/
    
        /// <summary>
        /// 重新载入CUI文件,刷新当前工作区2021
        /// </summary>
        /// <returns></returns>
        [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedReloadMenus@@YAX_N@Z")]
        public static extern int CuiAcedReloadMenus();
#endif
    }
}

20210528更新,详尽所有的版本

上面的 /* 这里未能详尽所有的版本 .*/ 标记的入口点不一样,但是有规律,所以可以参考此处代码:

JoinBoxCurrency.PeInfo 参考 测试篇 c#读PE32PE32+

namespace JoinBox.Menu
{
    public static class CuiStatic
    {
/// <summary>
/// 重新载入CUI文件,刷新当前工作区
/// </summary>
/// <returns></returns>
public static int CuiAcedReloadMenus()
{
    string funcName = "acedReloadMenus";
    // 路径通过程序集就能拿到
    var acadexeName = Process.GetCurrentProcess().MainModule.FileName;

    bool getYes = false;
    //输出所有的函数名
    var pe = new JoinBoxCurrency.PeInfo(acadexeName);
    var sb = new StringBuilder();
    foreach (var name in pe.ExportDirectory.NameList)
    {
        sb.Append(Environment.NewLine);
        var str = Encoding.Default.GetString(name as byte[]);
        if (str.Contains(funcName))
        {
            funcName = str;
            getYes = true;
            break;
        }
        sb.Append(str);
    }
    // Debug.WriteLine(sb.ToString());
    if (!getYes)
    {
        throw new Exception("没有找到对应的函数");
    }

    IntPtr hModule = Win32API.WinApi.GetModuleHandle(acadexeName); // 执行前必须加载了先...
    if (hModule == IntPtr.Zero)
        throw new Exception("找不到模块:" + acadexeName + "当前程序没有加载这个东西?");

    //函数指针
    IntPtr funcAdress = Win32API.WinApi.GetProcAddress(hModule, funcName);
    if (funcAdress == IntPtr.Zero)
        throw new Exception("找不到函数入口点:" + funcAdress);

    //利用委托调用函数指针,从而实现对方法的调用.
    var deFunc = Marshal.GetDelegateForFunctionPointer(funcAdress, typeof(DelegateAcedReloadMenus)) as DelegateAcedReloadMenus;
    return deFunc.Invoke();//调用方法(刷新cad的cui)
}

//委托,调用函数指针
delegate int DelegateAcedReloadMenus();
}}

ToolBar工具条

using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.Customization;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;

namespace JoinBox.Menu
{
    public class ToolBar
    {
        [CommandMethod("TestToolBarClose")]
        public void TestToolBarClose()
        {
            var cuiMain = Cui.CuiMain(out string expand);
            ToolBarGetExistsPrint(cuiMain);

            //关闭工具条
            ToolBarClose(cuiMain, "YQARCH");
            //ToolBarClose(cuiMain, "MINI");
            //ToolBarClose(cuiMain, "ACAD");
        }

        /// <summary>
        /// 打印当前工作空间显示的工具条id
        /// </summary>
        public static void ToolBarGetExistsPrint(CustomizationSection cs)
        {
            List<string> lst = new();
            //当前使用的工作空间
            string wscurrent = (string)Application.GetSystemVariable("wscurrent");
            foreach (Workspace pace in cs.Workspaces)//工作空间 "二维草图与注释"
            {
                var cs2 = pace.CustomizationSection;//空间下的cui
                if (wscurrent != pace.Name)//当前空间
                {
                    continue;
                }

                Debug.WriteLine("**当前使用的空间名字:" + pace.Name);
                Debug.WriteLine("**此空间的Cui文件:" + cs2.CUIFileName);
                foreach (Toolbar tb in cs2.MenuGroup.Toolbars)
                {
                    if (tb.ToolbarVisible == ToolbarVisible.hide)
                    {
                        lst.Add(tb.ElementID);
                    }
                }
                break;
                //foreach (WorkspaceToolbar tb in pace.WorkspaceToolbars)//工具条
                //{
                //    if (tb.Display == 0)//会遍历到其他空间可展示的东西
                //    {
                //        lst.Add(tb.ElementID);
                //    }
                //}
            }
            //按照字母排序,打印目前已经存在的工具条
            lst = lst.OrderBy(elementID => elementID).ToList();
            foreach (var item in lst)
            {
                Debug.WriteLine(item);
            }
        }

        /// <summary>
        /// 关闭工具条
        /// </summary>
        public static void ToolBarClose(CustomizationSection cs, string cuiMenuGroupName)
        {
            //当前使用的工作空间
            string wscurrent = (string)Application.GetSystemVariable("wscurrent");
            foreach (Workspace pace in cs.Workspaces)//工作空间 "二维草图与注释"
            {
                if (wscurrent != pace.Name)//当前空间
                {
                    continue;
                }
                foreach (WorkspaceToolbar tb in pace.WorkspaceToolbars)//工具条
                {
                    if (tb.MenuGroup == cuiMenuGroupName)
                    {
                        pace.WorkspaceToolbars.Remove(tb);//卸载工具条
                    }
                }
                break;
            }
        }
    }
}

相关阅读

cad.net 更改高版本填充交互方式为低版本样子

南胜博客,自动生成cuix

Ribbon

官方博客工具条了

[尼克劳斯] https://www.cnblogs.com/bomb12138/p/3607990.html

(完)

posted @ 2021-04-19 12:31  惊惊  阅读(2381)  评论(0编辑  收藏  举报