首先問你一個問題,如果你要用命令改圖元顏色,那么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);
}
}
}
(完)