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 = nameof(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 HashSet<ObjectId> _refeditSS_Before = [];
// 利用命令反应器 Refedit 启动之后做差集,就是在位编辑的时候内部的图元
private static HashSet<ObjectId> _refeditSS_Interior = [];

两个反应器写法

/// <summary>
/// 反应器->命令否决触发命令前(不可锁文档)
/// </summary>       
public static void Dc_VetoCommand(object sender, DocumentLockModeChangedEventArgs e)
{
    //这里不可以声明database...不然关闭文档的时候,再打开会引发致命错误
    if (string.IsNullOrEmpty(e.GlobalCommandName) || e.GlobalCommandName == "#")
        return;

    Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
    switch (e.GlobalCommandName.ToUpper())
    {
        case "REFEDIT":
            {
                //在位编辑命令,使用前获取当前空间所有图元
                var prompt = ed.SelectAll(_filter);//全选 
                if (prompt.Status != PromptStatus.OK)
                    return;
               var _hatchIds = prompt.Value.GetObjectIds();
                for (int i = 0; i < _hatchIds.Count; i++)
                    _refeditSS_Before.Add(_hatchIds[i]);
            }
            break;
    }
 }

/// <summary>
/// 反应器->command命令完成后(内锁文档)
/// </summary>
public static void Md_CommandEnded(object sender, CommandEventArgs e)
{
     if (string.IsNullOrEmpty(e.GlobalCommandName) || e.GlobalCommandName == "#")
        return;

    Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
    switch (e.GlobalCommandName.ToUpper())
    {
        case "REFEDIT":
            {
                //在位编辑命令,使用后获取当前空间所有图元 
                var prompt = ed.SelectAll(_filter);//全选 
                if (prompt.Status != PromptStatus.OK)
                    return;
                
                var _hatchIds = prompt.Value.GetObjectIds();
                for (int i = 0; i < _hatchIds.Count; i++)
                    if (!_refeditSS_Before.Contains(_hatchIds[i]))//Except
                        _refeditSS_Interior.Add(_hatchIds[i]);

                var sb = new StringBuilder();
                foreach (var id in _refeditSS_Interior)
                    sb.AppendLine(id.ToString());
                Env.Printl("块内填充id:" + sb.ToString());
            }
            break;
        case "REFSET": //加减在位编辑图元
            {
                //完成后必然有上次选择集
                var psr = ed.SelectPrevious();
                if (psr.Status != PromptStatus.OK)
                    return;

               // 上次选择集无法利用过滤器,因此我们手动过滤.你也可以封装一个方法.
               // 在位编辑时候肯定是当前数据库,所以你不需要像我这样复杂的获取
               // 如果你不需要过滤填充,也不需要写这段
                var _hatchIds = new HashSet<ObjectId>();
                using (Transaction tr = ids[0].Database.TransactionManager.StartTransaction())
                {
                    foreach (var id in psr.Value.GetObjectIds())
                    {
                        if (id.ToEntity(tr) is Hatch ha)              
                            _hatchIds.Add(ha);
                    }
                }
                if (_hatchIds.Count == 0) return;
                string last = CadSystem.Getvar("lastprompt"); //再获取最后一行命令

                // 就是因为无法遍历到在位编辑的块内图元,只能进行布尔运算
                if (last.Contains("添加") || last.Contains("Added"))// 中英文cad
                {
                    for (int i = 0; i < _hatchIds.Count; i++)
                    {
                        _refeditSS_Before.Remove(_hatchIds[i]);
                        _refeditSS_Interior.Add(_hatchIds[i]);
                    }
                    return;
                }
                if (last.Contains("删除") || last.Contains("Removed"))// 中英文cad
                {
                    for (int i = 0; i < _hatchIds.Count; i++)
                    {
                       _refeditSS_Interior.Remove(_hatchIds[i]);
                        _refeditSS_Before.Add(_hatchIds[i]);
                    }
                    return;
                }
            }
            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);
        }
    }
}

(完)

posted @ 2021-03-05 18:00  惊惊  阅读(1291)  评论(0编辑  收藏  举报