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


版本說明

本文測試為Acad2008(Cui)和Acad2021(Cuix)的案例,應該能夠涉及所有高低版本的操作.
Cui是個xml,Cuix是個壓縮包...知道好像沒什么好處,還是應該調用管理器操作而不是序列化操作.
關於宏的說明,看這個桌子的鏈接

特別的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

(完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM