cad.net 動態編譯生成命令+獲取在位編輯塊內圖元


首先問你一個問題,如果你要用命令改圖元顏色,那么cad有256個顏色,你需要寫256個命令來達到目的嗎?
答案:不.
程序員都是喜歡偷懶的.那么如何實現呢?看本文就知道了.

Lisp和c#的例子有點相似,都是利用了解釋器進行動態編譯.
由於想要盡可能兼容所有cad版本,就不采用AddCommand函數,因為低版本沒有.
Arx的例子就有點不一樣了,任何版本都有AddCommand函數,觸發命令后在函數做對應的操作即可.
至於c#能不能在 [DllImport "acad.exe" Entpoint ="??"]調用內置arx的AddCommand函數,我就不知道了.
有知道的可以留言.

那它有什么可拓展的方向呢?
可以寫一個配置文件作為快速定義命令的文件,例如:

ls1,螺栓1,./標准/模塊.dwg,5,10
ls2,螺栓2,./標准/模塊.dwg,6,20
ls3,螺栓3,./標准/模塊.dwg,7,30...

命令,塊名,工具箱某個目錄下的dwg文件,插入時候的顏色,動態塊拉伸距離(m10螺栓直徑)
我們只需要修改這個配置文件就可以配置多個插入的圖塊.

Lisp的例子

;;;desc:修改對象顏色
(apply
  '(lambda ( / cishu feilingyanse) ;匿名函數( 傳遞變量 / 局部變量 )
     (setq cishu 0)              ;設置循環次數初始值
     (repeat 255                 ;循環次數代表顏色數量
       (setq cishu (1+ cishu)               ;循環計數
                 feilingyanse (itoa cishu) ;循環數轉化成字符串
       )
       (eval        ;求值運行程序
         (read      ;去掉雙引號,返回整個程序
           (strcat  ;合並多個字符串
             "(defun c:"
             feilingyanse   ;顏色
             " ()(BF-yansemokuai "
             feilingyanse
             " ))"
           );注意反義斜杠能去掉引號作用
         )
       )
     )
   )
  nil ;匿名函數,空參數
)

;;;name:BF-yansemokuai
;;;desc:修改顏色的模板
;;;arg:
;;;return:
;;;example:(BF-yansemokuai 5)
(defun BF-yansemokuai(tuyuanyanse / ss)
  ;(BF-GO (list 'cmdecho 0) nil 1);出錯編組
  (if (setq ss (ssget))
      (progn
        (command "change" ss "" "p" "c" tuyuanyanse "")
        ;;;現在使用命令模式,因vla-put-Color在在位編輯的時候會修改塊外.
        ;;;(setq ss (BF-ss->vlass ss));轉為vla對象
        ;;;(vlax-for ^se ss ;循環體
        ;;;  (vla-put-Color ^se tuyuanyanse) ;修改顏色
        ;;;);這里會修改到塊外面的東西
      )
  )
  ;(BF-End);調用恢復錯誤處理
)

c#的例子

中間段的命令代碼,及命令的實現

#if !HC2020
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.EditorInput;
using GrxCAD.Geometry;
using GrxCAD.ApplicationServices;
using Acap = GrxCAD.ApplicationServices.Application;
#endif
using System;
using System.Text;
using JoinBoxCurrency;


//要注意.本cs文件的命名空間,類名是和動態編譯字符串關聯的,要修改的時候記得一起改.
//AutoGo.JoinBoxEnName = "JoinBox"
namespace JoinBox
{
    public class DynamicEntityColor : IAutoGo
    { 
        const string DynamicStr = "DynamicEntityColor";
        public Sequence SequenceId()
        {
            return Sequence.Last;
        }
        public void Terminate()
        {
            try
            {
                AppDomain.CurrentDomain.AssemblyResolve -= RunTimeCurrentDomain.DefaultAssemblyResolve;
            }
            catch
            {
                System.Windows.Forms.MessageBox.Show($"動態編譯{DynamicStr}卸載出錯", "驚驚連盒");
            }
        }


        // 動態編譯命令 
        public void Initialize()
        {
            try
            {
                AppDomain.CurrentDomain.AssemblyResolve += RunTimeCurrentDomain.DefaultAssemblyResolve;

                var code = new StringBuilder();
                for (int i = 0; i < 256; i++)
                {
                    AddDynamicCommand(i.ToString(), code);
                }
                DynamicAssembly.Go(code, DynamicStr);
            }
            catch (Exception e)
            {
                Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor;//命令欄交互 
                ed.WriteMessage(Environment.NewLine + $"動態編譯{DynamicStr}出錯::" + e.Message);
            }
        }

        /// <summary>
        /// 生成編譯的命令部分的字符串
        /// </summary> 
        /// <param name="command">命令</param> 
        private void AddDynamicCommand(string command, StringBuilder code)
        {
            code.Append("[CommandMethod(\"");
            code.Append(command);
            code.Append("\", CommandFlags.Modal | CommandFlags.UsePickSet | CommandFlags.Redraw | CommandFlags.DocExclusiveLock)]");
            code.Append("public static void ");
            code.Append($"{DynamicStr}FuncName" + command);
            code.Append("(){");
            code.Append(AutoGo.JoinBoxEnName);
            code.Append($".{DynamicStr}.CommandChangeEntityColor(\"{command}\");");
            code.Append("}");
        }
         
        /// <summary>
        /// 選擇圖元修改顏色
        /// </summary>
        /// <param name="dwgPath"></param>
        public static void CommandChangeEntityColor(string colorIndex)
        {
            Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor;//命令欄交互
            Database db = HostApplicationServices.WorkingDatabase;//當前的數據庫 
            ed.WriteMessage("\r\n這是動態編譯的修改圖元顏色喲,顏色是::" + colorIndex);
            var psr = ed.GetSelection();//手選
            if (psr.Status != PromptStatus.OK)
            {
                return;
            }
            db.Action(tr =>
            {
                foreach (var item in psr.Value.GetObjectIds())
                {
                    var ent = tr.GetObject(item, OpenMode.ForWrite) as Entity;
                    if (ent != null)
                    {
                        ent.ColorIndex = int.Parse(colorIndex); 
                    }
                }
            });
        }
    }
}

執行動態編譯

#if !HC2020
using Autodesk.AutoCAD.EditorInput;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using GrxCAD.EditorInput;
using Acap = GrxCAD.ApplicationServices.Application;
#endif
using System;
using System.CodeDom.Compiler;
using System.IO;
using System.Text;
using Microsoft.CSharp;

namespace JoinBox
{
    public class DynamicAssembly
    {
        /// <summary>
        /// 開始進行編譯
        /// </summary>
        /// <param name="midCode">中間的代碼段</param>
        /// <param name="dynamicStr">命名空間的字符串</param>
        public static void Go(StringBuilder midCode, string dynamicStr)
        {
            //生成駐留內存的動態程序集
            var pars = new CompilerParameters
            {
                CompilerOptions = "/target:library /optimize", //編譯器選項  
                GenerateExecutable = false, //生成可執行文件
                GenerateInMemory = true     //在內存中生成
            };
            var acpath = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory); //獲取當前cad運行路徑 "C:\\Program Files (x86)\\AutoCAD 2008\\"
            var asslst = pars.ReferencedAssemblies;  //添加引用

            //獲取程序集的位置
            string configPathAll = Assembly.GetExecutingAssembly().CodeBase;
            configPathAll = codeBase.Substring(8, codeBase.Length - 8);    // 8是file:// 的長度   
            asslst.Add(configPathAll); //當前的dll的路徑 "G:/K01.驚驚連盒/net35/JoinBox.dll"

            string st = acpath.ToString();
#if !HC2020
            asslst.Add(st + @"\acdbmgd.dll");
            asslst.Add(st + @"\acmgd.dll");
#else
            asslst.Add(st + @"\gmap.dll");
            asslst.Add(st + @"\gmdb.dll");
#endif
#if !HC2020 && !AC2008 && !AC2009 && !AC2010 && !AC2011 && !AC2012
            asslst.Add(st + @"\accoremgd.dll");
#endif

            var code = new StringBuilder();
            code.Append("using System;");
            code.Append("using System.Reflection;");
            code.Append("using System.Collections.Generic;");
            code.Append("using System.IO;");
            code.Append("using System.Text;");
#if !HC2020
            code.Append("using Autodesk.AutoCAD.Runtime;");
            code.Append("using Autodesk.AutoCAD.ApplicationServices;");
            code.Append("using Autodesk.AutoCAD.DatabaseServices;");
            code.Append("using Autodesk.AutoCAD.EditorInput;");
            code.Append("using Autodesk.AutoCAD.Geometry;");
#else
            code.Append("using GrxCAD.Runtime;");
            code.Append("using GrxCAD.ApplicationServices;");
            code.Append("using GrxCAD.DatabaseServices;");
            code.Append("using GrxCAD.EditorInput;");
            code.Append("using GrxCAD.Geometry;");
#endif
            code.Append($"using {AutoGo.JoinBoxEnName};");
            code.Append($"namespace {AutoGo.JoinBoxEnName + dynamicStr}");//不給插入到相同命名空間內
            code.Append("{");
            code.Append("public partial class " + dynamicStr + "{");
            code.Append(midCode);
            code.Append("}}");

            //編譯
            using (var comp = new CSharpCodeProvider())
            {
                var cr = comp.CompileAssemblyFromSource(pars, code.ToString());
                if (cr.Errors.HasErrors)
                {
                    Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor;
                    ed.WriteMessage(Environment.NewLine + $"動態編譯{dynamicStr}錯誤:");
                    foreach (CompilerError err in cr.Errors)
                    {
                        ed.WriteMessage(Environment.NewLine + err.ErrorText);
                    }
                }
            }
        }
    }
}

子函數

RunTimeCurrentDomain.DefaultAssemblyResolve 在:運行域事件
db.Action 在:委托的學習
IAutoGo接口 在:cad.net IExtensionApplication接口的妙用

處理在位編輯塊的方法

在位編輯塊時候,塊外的圖元(褪色的)也可以改到,或許可以通過以下來處理:

var dm = Application.DocumentManager;
var md = dm.MdiActiveDocument;

// 命令反應器命令前觸發(全局)
dm.DocumentLockModeChanged += Dc_VetoCommand; 
// 命令反應器命令后觸發
md.CommandEnded += HatchEvent.Md_CommandEnded;

// 利用命令反應器 Refedit 啟動之前創建選擇集
private static List<ObjectId> _refeditSS_Before = new List<ObjectId>() { };
// 利用命令反應器 Refedit 啟動之后做差集,就是在位編輯的時候內部的圖元
private static List<ObjectId> _refeditSS_Interior = new List<ObjectId>() { };

兩個反應器寫法

/// <summary>
/// 反應器->命令否決觸發命令前(不可鎖文檔)
/// </summary>       
public static void Dc_VetoCommand(object sender, DocumentLockModeChangedEventArgs e)
{
    Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
    //這里不可以聲明database...不然關閉文檔的時候,再打開會引發致命錯誤
    if (string.IsNullOrEmpty(e.GlobalCommandName) || e.GlobalCommandName == "#")
    {
        return;
    }
    switch (e.GlobalCommandName.ToUpper())
    {
        case "REFEDIT":
            {
                //在位編輯命令,使用前獲取當前空間所有圖元
                PromptSelectionResult prompt = ed.SelectAll(_filter);//全選 
                if (prompt.Status == PromptStatus.OK)
                {
                    _refeditSS_Before = prompt.Value.GetObjectIds().ToList();
                }
            }
            break;
    }
 }

/// <summary>
/// 反應器->command命令完成后(內鎖文檔)
/// </summary>
public static void Md_CommandEnded(object sender, CommandEventArgs e)
{
    Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
    if (string.IsNullOrEmpty(e.GlobalCommandName) || e.GlobalCommandName == "#")
    {
        return;
    }
    switch (e.GlobalCommandName.ToUpper())
    {
        case "REFEDIT":
            {
                //在位編輯命令,使用后獲取當前空間所有圖元 
                PromptSelectionResult prompt = ed.SelectAll(_filter);//全選 
                if (prompt.Status != PromptStatus.OK)
                {
                    return;
                }
                _refeditSS_Interior = prompt.Value.GetObjectIds().Except(_refeditSS_Before).ToList();
                foreach (var item in _refeditSS_Interior)
                {
                    ed.WriteMessage(item.ToString());
                }
            }
            break;
        case "REFSET": //加減在位編輯圖元
            {
                //完成后必然有上次選擇集
                PromptSelectionResult psr = ed.SelectPrevious();//上次選擇集
                if (psr.Status != PromptStatus.OK)
                {
                    return;
                }
                var ids = psr.Value.GetObjectIds();
                Database db = ids[0].Database;
                var haIds = new List<ObjectId>();
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    foreach (var item in ids)
                    {
                        Entity ent = item.ToEntity(tr);
                        if (ent is Hatch ha)
                        {
                            haIds.Add(item);
                        }
                    }
                }
                if (haIds.Count > 0)
                {
                    //獲取命令歷史,最后一行看是加還是減
                    string last = CadSystem.Getvar("lastprompt"); //再獲取最后一行命令

                    if (last.Contains("添加") || last.Contains("Added"))
                    {
                        _refeditSS_Before = _refeditSS_Before.Except(haIds).ToList();//差集
                        _refeditSS_Interior = _refeditSS_Interior.Union(haIds).ToList();//消重+合並
                    }
                    else if (last.Contains("刪除") || last.Contains("Removed"))
                    {
                        _refeditSS_Interior = _refeditSS_Interior.Except(haIds).ToList();//差集
                        _refeditSS_Before = _refeditSS_Before.Union(haIds).ToList();//消重+合並 
                    }
                }
            }
            break;
        case "REFCLOSE"://保存塊,清空集合
            {
                _refeditSS_Interior.Clear(); 
            }
            break;
    }
}

Arx的例子

節選自edata聊天記錄,我發現他博客沒寫過就拿過來了...

//256個顏色命令
namespace JJBox命令 {
    //獲取變量
    void GetVar(CString command, CString* sCmd)
    {
        struct resbuf var;
        acedGetVar(command, &var);
        //通過指針的方式存入內容
        (*sCmd).Append(var.resval.rstring);
    }

    //顏色命令最后都會調用到這個組
    void sk_ChColor()
    {
        CString sCmd;
        GetVar(_T("CMDNAMES"), &sCmd);//獲取是什么數字觸發的.

        if (sCmd == "")
        {
            return;
        }
        int nColor = _ttoi(sCmd);
        if (nColor < 0 || nColor > 256)
        {
            return;
        }
        ads_name ss;
        int nRet = acedSSGet(NULL, NULL, NULL, NULL, ss);
        if (RTCAN == nRet)//取消
        {
            return;
        }
        if (RTNORM != nRet)//回車
        {
            //設置默認顏色
            AcCmColor col;
            col.setColorIndex(nColor);
            acdbHostApplicationServices()->workingDatabase()->setCecolor(col);
        }

        Adesk::Int32 nSSLenght = 0;
        acedSSLength(ss, &nSSLenght);
        for (int i = 0; i < nSSLenght; i++)
        {
            ads_name ent;
            AcDbObjectId objId;
            acedSSName(ss, i, ent);
            acdbGetObjectId(objId, ent);
            AcDbEntityPointer pEnt(objId, AcDb::kForWrite);
            if (Acad::eOk != pEnt.openStatus())
            {
                continue;
            }
            pEnt->setColorIndex(nColor);
        }
    }

    //由於是順序編譯,所以這里的命令定義只能夠放最下面
    //定義顏色命令
    void jjarx_AddCommands()
    {
        for (int i = 0; i <= 256; i++)
        {
            CString strCmdName;
            strCmdName.Format(_T("%d"), i);
            acedRegCmds->addCommand(_T("sk_ChColor"), strCmdName, strCmdName, ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET, sk_ChColor);
        }
    }
}

(完)


免責聲明!

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



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