Autodesk Community Tips- ADNオープン
Autodesk Community Tipsではちょっとしたコツ、やり方、ショートカット、アドバイスやヒントを共有しています。
ソート順:
Issue 任意のタイミングで、開いている図面に共通画層があるかどうかチェックして、画層がなかった際に追加したいと考えています。    ... private DocumentCollection docs = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager; public void Initialize() { docs.DocumentActivated += new DocumentCollectionEventHandler(OnDocumentActivated); } public void Terminate() { docs.DocumentActivated -= OnDocumentActivated; } public void OnDocumentActivated(object sender, DocumentCollectionEventArgs e) { if (e.Document != null) { Database db = e.Document.Database; Document doc = e.Document; doc.LockDocument(); using (Transaction tr = db.TransactionManager.StartTransaction()) { LayerTable? tbl = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable; string name = "Main"; if (!tbl.Has(name)) { LayerTableRecord rec = new LayerTableRecord(); rec.Color = Autodesk.AutoCAD.Colors.Color.FromColorIndex(ColorMethod.ByAci, 3); rec.Name = name; tr.GetObject(db.LayerTableId, OpenMode.ForWrite); tbl.Add(rec); tr.AddNewlyCreatedDBObject(rec, true); } db.Clayer = tbl[name]; tr.Commit(); } } } ...   この際、ユーザーには上記の図面編集を意識させたくないため、図面に編集を加えなかったときには図面保存ダイアログの表示はさせたくありません。(イベントハンドラーで画層追加すると、図面タブで表示図面を切り替えたりすると、図面保存のダイアログが表示されるようです。)     新規図面が対象ではないので、図面テンプレート(.dwt)が使用出来ません。 何かいい方法はありますか?   Solution 図面の編集有無はシステム変数 DBMOD の値が 0 以外になっているか否かで判定されています。DBMOD は読み取り専用のシステム変数ですが、Document.PushDbmod メソッド と Document.PopDbmod メソッドを利用すると、両メソッド呼び出し間の処理を無効化して DBMOD の値を 0 に復元することが出来ます。   下記は、その使用例を示す C# コードです。   public void OnDocumentActivated(object sender, DocumentCollectionEventArgs e) { if (e.Document != null) { Database db = e.Document.Database; Document doc = e.Document; doc.LockDocument(); doc.PushDbmod(); using (Transaction tr = db.TransactionManager.StartTransaction()) { LayerTable? tbl = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable; string name = "Main"; if (!tbl.Has(name)) { LayerTableRecord rec = new LayerTableRecord(); rec.Color = Autodesk.AutoCAD.Colors.Color.FromColorIndex(ColorMethod.ByAci, 3); rec.Name = name; tr.GetObject(db.LayerTableId, OpenMode.ForWrite); tbl.Add(rec); tr.AddNewlyCreatedDBObject(rec, true); } db.Clayer = tbl[name]; tr.Commit(); } doc.PopDbmod(); } }  
記事全体を表示
Issue 比較的時間のかかるカスタム コマンドがあります。内部でループ制御による反復処理をしていますが、ESC キー押下で処理をキャンセルさせる実装にすることは出来ますか?   Solution AutoCAD 2011 以降、HostApplicationServices.UserBreak メソッドで、物理的な ESC キーの押下を検出することが出来るようになっています。ESC キーが押下されると同メソッドが true を返すので、例外を発生させるなどして処理を中断することが可能です。   [CommandMethod("MyCommand", CommandFlags.Modal)] public static void MyCommand() { ProgressMeter pm = new ProgressMeter(); pm.Start("Long process"); pm.SetLimit(100); try { for (int i = 0; i < 1000; i++) { if (HostApplicationServices.Current.UserBreak()) throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.UserBreak, "ESCAPE pressed"); pm.MeterProgress(); System.Threading.Thread.Sleep(100); } } catch (Autodesk.AutoCAD.Runtime.Exception ex) { Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.Message); } finally { pm.Stop(); } }  
記事全体を表示
Issue Editor.GetNestedEntity(PromptNestedEntityOptions) メソッドを利用して作図空間のブロック参照内の要素オブジェクトを直接選択、取得することが出来ますが、取得した要素オブジェクトからブロック名を得ることは出来ますか?   Solution 選択した要素オブジェクトの DBObject.OwnerId プロパティで、同オブジェクトのコンテナ オブジェクト、この場合、ブロック定義(BlockTableRecord)オブジェクトを取得ことが出来ます。その後、SymbolTableRecord.Name プロパティからブロック名を得ることが出来ます。      Database db = HostApplicationServices.WorkingDatabase; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; PromptNestedEntityOptions peo = new PromptNestedEntityOptions("\nブロック参照を選択:"); PromptEntityResult pent = ed.GetNestedEntity(peo); if (pent.Status != PromptStatus.OK) { return; } using (Transaction tr = db.TransactionManager.StartTransaction()) { Entity ent = (Entity)tr.GetObject(pent.ObjectId, OpenMode.ForRead); ed.WriteMessage("\nObject Type : " + ent.GetType().ToString()); BlockTableRecord blkDef = (BlockTableRecord)tr.GetObject(ent.OwnerId, OpenMode.ForRead); ed.WriteMessage("\nBlock Name : " + blkDef.Name); tr.Commit(); }
記事全体を表示
Issue REVCLOUD コマンド Object オプションを AutoCAD 上に入力して作成した雲マークには、「円弧の長さ」プロパティが表示されますが、同じ内容を AutoCAD .NET API から呼び出して作成した雲マークには、「円弧の長さ」プロパティが表示されません。     コマンドプロンプトで指定したコマンドは次の内容です。     一方、AutoCAD .NET API から呼び出しは、次のようになります。   Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; PromptEntityResult pent = ed.GetEntity("\nオブジェクトを選択:"); if (pent.Status == PromptStatus.OK) { ed.Command("REVCLOUD", "O", pent.ObjectId, "N"); }   この違いはなぜ生まれるのでしょうか? AutoCAD .NET API から作成した雲マークに同じように「円弧の長さ」プロパティを表示する方法はありますか?   Solution REVCLOUD コマンドは過去に数回機能強化されていて、 AutoCAD 2021で「円弧の長さ」プロパティが追加されています。AutoCAD は、アドインを含む古いスクリプトからの REVCLOUD コマンド呼び出しの互換性を維持する必要するため、コマンドの呼び出し元の環境に応じて、内部でコマンド バージョンを切り替える処理をしています。 今回の場合、ユーザーインターフェース(UI)から呼び出された REVCLOUD コマンドは内部コマンドバージョン 3 で実行されています。一方、アドインから同じコマンドを呼び出すと、内部コマンドバージョン 1 を使用して雲マークを作成します。この違いが、「円弧の長さ」プロパティの表示有無の結果を生んでいます。 アドインから REVCLOUD コマンドを呼び出す場合、次のようにコマンド呼び出しの直前に Editor.InitCommandVersion メソッドを用いて内部コマンドバージョンを切り替えることが出来ます。この処理で「円弧の長さ」プロパティを持つ内部コマンドバージョン 3 を実行することが出来ます。   Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; PromptEntityResult pent = ed.GetEntity("\nオブジェクトを選択:"); if (pent.Status == PromptStatus.OK) { Application.DocumentManager.MdiActiveDocument.Editor.InitCommandVersion(3); ed.Command("REVCLOUD", "O", pent.ObjectId, "N"); }
記事全体を表示
Issue AutoCAD のユーザー インターフェース上に開いている現在のアクティブな図面(MDI 子ウィンドウにフォーカスが当たっている表示中の図面)を Database.SaveAs(string, DwgVersion)  メソッドで保存しようとしても、図面ウィンドウ上の図面タイトルが指定した名前に変化しません。 SAVEAS コマンドと同等の挙動を実現したいのですが、どうすればいいでしょうか?     Solution Database.SaveAs メソッドには、次の3つのオーバロード メソッドが用意されています。 Database.SaveAs(string, Autodesk.AutoCAD.DatabaseServices.SecurityParameters) Database.SaveAs(string, DwgVersion) Method | Autodesk Database.SaveAs(string, [MarshalAs(UnmanagedType.U1)] bool, DwgVersion, Autodesk.AutoCAD.DatabaseServices.SecurityParameters) あいにく、いずれのオーバロード メソッドも SAVEAS コマンドに対応した動作を完全に実現するものではなく、同コマンドの実装の一部分を担う機能しか実装されていない状態です。   図面ウィンドウ上の図面タイトルが指定した名前を反映させる方法として Database.SaveAs(string, [MarshalAs(UnmanagedType.U1)] bool, DwgVersion, Autodesk.AutoCAD.DatabaseServices.SecurityParameters) のオーバーロード バージョンの第2パラメーターを true を指定する方法があります。ただし、この方法も SAVEAS コマンドと同等の動作をするものではありません。   完全に SAVEAS コマンドと同じ挙動を得るには、SAVEAS コマンドをそのまま呼び出す必要があります。   次の例は、Editor.Command メソッドで SAVEAS コマンドを呼び出すものです。   [CommandMethod("MyCommand1", CommandFlags.Modal)] public void MyCommand1() { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; if (doc != null) { string path = @"C:\temp\Test.dwg"; int filedia = System.Convert.ToInt32(Application.GetSystemVariable("FILEDIA")); Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("FILEDIA", 0); if (System.IO.File.Exists(path)) { ed.WriteMessage("\n指定したフォルダに同じ名前の図面ファイルが既に存在しています"); string check_path = Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("DWGPREFIX").ToString(); check_path = check_path + Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("DWGNAME").ToString(); if (path == check_path) { ed.WriteMessage("\n開いている同じ名前の図面ファイルで上書きします..."); ed.Command("SAVEAS", "2018", path); } else { ed.WriteMessage("\n開いている図面で上書きします..."); ed.Command("SAVEAS", "2018", path, "Y"); } } else { ed.WriteMessage("\n指定したフォルダに初めて指定した名前で図面ファイルを保存します..."); ed.Command("SAVEAS", "2018", path); } Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("FILEDIA", filedia); } }   通常の SAVEAS コマンドでは、保存先のパスとファイル名を指定する際にファイル ダイアログが表示されてしまうので、FILEDIA システム変数でその表示を抑止しています。また、保存先のパスに同じファイル名の図面が存在した場合、アクティブな現在の図面の状態によって上書き保存の可否を指定するプロンプト オプションが異なるため注意が必要です。
記事全体を表示
Issue モデル空間やペーパー空間(レイアウト)の作図領域の背景色は、[オプション] ダイアログの [表示] タブからアクセス出来る [作図ウィンドウの色] ダイアログから変更出来ますが、AutoCAD .NET API で同等の処理を実装することは出来ますか?     Solution あいにく、AutoCAD .NET API ネイティブにモデル空間やペーパー空間(レイアウト)の作図領域の背景色を変更する API が用意されていません。   代替として、COM Interop を使って PreferencesDisplay オブジェクト(ActiveX) の  GraphicsWinModelBackgrndColor プロパティ(モデル空間背景色)、 GraphicsWinLayoutBackgrndColor プロパティ(ペーパー空間背景色)を使用、背景色を取得、設定することが出来ます。   この場合、 GraphicsWinModelBackgrndColor プロパティ、 GraphicsWinLayoutBackgrndColor プロパティには AcCmColor オブジェクト が適用出来ないため、インデックスカラーやカラーブックの指定は出来ず、RGB 指定になってしまう点にご注意ください。   次の C# コードは、  GraphicsWinModelBackgrndColor プロパティ を使ってモデル空間の作図領域の背景色を変更するものです。   using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application; ... ... Editor ed = AcadApp.DocumentManager.MdiActiveDocument.Editor; // ↓ if you face an error at 'var acadApp' below, try 'dynamic acadApp' var acadApp = (AcadApplication)AcadApp.AcadApplication; var modelBk = acadApp.Preferences.Display.GraphicsWinModelBackgrndColor; var color = ColorTranslator.FromOle((int)modelBk); ed.WriteMessage("\nCurrent Color = {0}", color.ToString()); acadApp.Preferences.Display.GraphicsWinModelBackgrndColor = (uint)ColorTranslator.ToOle(System.Drawing.Color.FromArgb(11, 22, 33)); modelBk = acadApp.Preferences.Display.GraphicsWinModelBackgrndColor; color = ColorTranslator.FromOle((int)modelBk); ed.WriteMessage("\nNew Color = {0}", color.ToString()); ...   なお、ObjectARX では、 acedGetCurrentColors() グローバル関数と acedSetCurrentColors() グローバル関数を使って、AcColorSettings 構造体の dwGfxModelBkColor 値、dwGfxLayoutBkColor 値で背景色の取得/設定をおこなうことが出来ます。
記事全体を表示
Issue VPORTS[ビューポート管理] コマンドで作成したモデル空間ビューポート内の表示をすべて再作図したいのですが、.NET API で REGENALL[全再作図] コマンドと同じような実装をすることは可能でしょうか?     Solution AutoCAD .NET API の Editor.Regen メソッドには、表示中のすべてのモデル空間ビューポートの再作図を指定するパラメーターがありません。このため、すべてのモデル空間ビューポートを走査して、ビューポートを識別する ViewportTableRecord.Number プロパティの値を CVPORT システム変数に指定、アクティブなビューポートをを切り替えて Editor.Regen メソッドを呼び出す必要があります。   [CommandMethod("MyCommand", CommandFlags.Modal)] public void MyCommand() { Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; var curVPort = Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("CVPORT"); using (Transaction tr = db.TransactionManager.StartTransaction()) { ViewportTable vpTable = (ViewportTable)tr.GetObject(db.ViewportTableId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForRead); if (vpTable != null) { ed.WriteMessage(Constants.vbLf + "Model Space Viewports : "); foreach (ObjectId vpId in vpTable) { ViewportTableRecord vptr = (ViewportTableRecord)tr.GetObject(vpId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite); Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("CVPORT", vptr.Number); ed.WriteMessage(vptr.Number.ToString()); ed.Regen(); } } tr.Commit(); } Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("CVPORT", curVPort); }    もちろん、プロジェクトの参照設定に Autodesk.AutoCAD.Interop.Common.dll と Autodesk.AutoCAD.Interop.dll の 2つのアセンブリを追加して、COM Interop(COM 相互運用機能)で ActiveX オートメーション(COM API)を利用、 acAllViewports パラメーターを指定して Regen メソッドを呼び出すことも可能です。   [CommandMethod("MyCommand", CommandFlags.Modal)] public void MyCommand() // This method can have any name { Autodesk.AutoCAD.Interop.AcadApplication COM_App = (Autodesk.AutoCAD.Interop.AcadApplication)Autodesk.AutoCAD.ApplicationServices.Application.AcadApplication; COM_App.ActiveDocument.Regen(Autodesk.AutoCAD.Interop.Common.AcRegenType.acAllViewports); }   
記事全体を表示
Issue グループやブロック化していない状態の特定オブジェクトを移動や回転する際に、 イベント処理を使用せずに、アプリ内で関係のあるオブジェクトを同じように移動や回転する方法はありますか?   Solution 特定のオブジェクト タイプ(クラス)の移動や回転、縮尺変更など編集は、TransformOverrule オーバールール(Overrule)プロトコルを使用して、既定の振る舞いを変更することが可能です。ただし、イベント ハンドラーでのオブジェクト編集で同一イベントが発生してしまい、無限ループに陥らないような配慮が必要です。   次の C# コードは、MOVE コマンドや ROTATE コマンド、SCALE コマンドで円弧オブジェクト(Arc)を選択すると、青色(インデックスカラー色:5)の線分オブジェクト(Line)に同じマトリックス変換を実施するものです。   public class TransformByOverrule : TransformOverrule { public override void TransformBy(Entity ent, Matrix3d mat) { Database db = HostApplicationServices.WorkingDatabase; Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor; ed.WriteMessage("\nTransformBy.TransformBy Overrule - matrix {0}", mat.ToString()); base.TransformBy(ent, mat); TypedValue[] typeval = new TypedValue[2]; typeval.SetValue(new TypedValue(62, 5), 0); typeval.SetValue(new TypedValue(0, "LINE"), 1); SelectionFilter filter = new SelectionFilter(typeval); PromptSelectionResult psr = ed.SelectAll(filter); ed.WriteMessage("\nGetSelection = {0}", psr.Status.ToString()); if (psr.Status == PromptStatus.OK) { using (Transaction tr = db.TransactionManager.StartTransaction()) { SelectionSet sset = psr.Value; foreach (ObjectId objId in sset.GetObjectIds()) { ed.WriteMessage("\nObjectTd:" + objId.ToString()); Entity slave = (Entity)tr.GetObject(objId, OpenMode.ForWrite); slave.TransformBy(mat); } tr.Commit(); } } } } static TransformByOverrule _toverrule = null; [CommandMethod("MyCommand")] static public void MyCommand() { Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor; if (_toverrule == null) { _toverrule = new TransformByOverrule(); ObjectOverrule.AddOverrule(RXObject.GetClass(typeof(Arc)), _toverrule, true); ObjectOverrule.Overruling = true; ed.WriteMessage("\nBegin modifying ARC overrule"); } else { ObjectOverrule.RemoveOverrule(RXObject.GetClass(typeof(Arc)), _toverrule); _toverrule.Dispose(); _toverrule = null; ed.WriteMessage("\nEnd modifying ARC overrule"); } }        
記事全体を表示
Issue 選択したオブジェクトが自動調整配列複写の要素かどうか判定して、自動調整配列複写であった場合に分解する実装するには、どのようなコードを記述すればいいでしょうか?     Solution 自動調整配列複写の対象となっているオブジェクト一式は、匿名ブロック(Anonymous Block)として自動調整フレームワークで管理、維持されています。   自動調整配列複写の判定には、AssocArray.IsAssociativeArray メソッドを利用することが出来ます。 同様に、分解には AssocArray.Explode メソッドを利用することが出来ます。   次のコードは、選択したオブジェクトが自動調整配列複写の構成要素だった場合に、トップレベルの調整配列複写を分解する C# 例です。   Database db = HostApplicationServices.WorkingDatabase; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; PromptEntityResult pent = ed.GetEntity("\nオブジェクトを選択:"); if (pent.Status != PromptStatus.OK) { return; } using (Transaction tr = db.TransactionManager.StartTransaction()) { if (AssocArray.IsAssociativeArray(pent.ObjectId)) { ed.WriteMessage("\n自動調整配列です"); AssocArray ary = AssocArray.GetAssociativeArray(pent.ObjectId); var param = ary.GetParameters(); if (param as AssocArrayPathParameters != null) ed.WriteMessage("\n - パス"); if (param as AssocArrayPolarParameters != null) ed.WriteMessage("\n - 円形状"); if (param as AssocArrayRectangularParameters != null) ed.WriteMessage("\n - 矩形状"); AssocArray.Explode(pent.ObjectId); } else ed.WriteMessage("\n自動調整配列ではありません"); tr.Commit(); }   EXPLODE コマンドで自動調整配列複写を分解する場合には、次のようなコードで AssocArray.Explode メソッドを代替することが出来ます。いずれの場合も、自動調整フレームワークが構成要素オブジェクトを保持しているため、すべてのオブジェクトを指定する必要はありません。   var sset = SelectionSet.FromObjectIds(new ObjectId[] { pent.ObjectId }); ed.Command("EXPLODE", sset);    
記事全体を表示
AutoCAD .NET APIリファレンスに記載の様に、PromptSelectionOptions.SingleOnlyは、AutoCAD ObjectARXでのacedSSGet()での”:S”モードの設定に相当します。また、 PromptSelectionOptions.SinglePickInSpaceは、":A"モードに相当します。   ObjectARXでのacedSSGetでは、”:S"モードは、単一のエンティティの選択が許容される状態となりますが、窓選択については有効な状態のままとなります。   また、”:A"モードについては、aecdSSGetのリファレンスでは「This mode option causes acedSSGet() to perform single pick selection in space on entities that implement a subselection filter.」との説明がありますが、これは例えばテーブルの様な、エンティティ内にサブエンティティがあるようなエンティティにおいて、テーブルのセル内の文字のクリックを、テーブル自身の選択とするオプション設定となります。
記事全体を表示
Issue AutoCAD .NET API でフォルダ内の DWG ファイルを順に開いて処理していますが、サイズの大きい図面があると相対的に処理時間が長くなってしまいます。高速化するアイデアはありますか?   Solution   AutoCAD 上に図面を開いて表示する際、描画に多く時間がかかってしまいます。 もし、ユーザインターフェース上に図面を表示する必要がないのであれば、Database.ReadDwgFile メソッドを使って、メモリ上に図面を展開する方法を採用することで、描画にかかかる時間を省いて処理全体を高速化させることが出来ます。   次のコードは、フォルダを指定後にフォルダ内の .dwg ファイルを順に Database.ReadDwgFile メソッドでメモリ展開し、モデル空間のオブジェクト タイプを表示するものです。 [CommandMethod("MyCommand", CommandFlags.Modal)] public void MyCommand() { Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; using (FolderBrowserDialog fd = new FolderBrowserDialog()) { if (fd.ShowDialog() == DialogResult.OK) { string folder = fd.SelectedPath; System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(folder); IEnumerable<System.IO.FileInfo> files = di.EnumerateFiles("*.dwg", System.IO.SearchOption.AllDirectories); if (files == null) return; ed.WriteMessage("\n\t{0}", folder); foreach (System.IO.FileInfo f in files) { string file = f.FullName; ed.WriteMessage("\n - {0}", file); try { using (Database db = new Database(false, true)) { db.ReadDwgFile(file, FileOpenMode.OpenForReadAndAllShare, false, null); using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTableRecord btr = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead); foreach (ObjectId id in btr) { Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead); ed.WriteMessage("\n - {0}", ent.GetType().ToString()); } tr.Commit(); } } } catch (System.Exception ex) { ed.WriteMessage(ex.ToString()); } } } } }   なお、Database.ReadDwgFile メソッドの使用時には、次の点に注意が必要です。   Database.ReadDwgFile メソッドで読み込む Database インスタンスを new ステートメントで用意する必要があります。この際、最低限必要なシンボルテーブルとオーナーシップを持つ Database 構造は、読み込む DWG ファイルに定義されているため、コンストラクタには Database.Database([MarshalAs(UnmanagedType.U1)] bool, [MarshalAs(UnmanagedType.U1)] bool)  オーバーロードを使用して、第1パラメーターの値を false にして、読み込む DWG ファイルの Database 構造との競合を抑止する必要があります。 AutoCAD のユーザインターフェースに表示された Database インスタンスは、同図面を表示している MDI 子ウィンドウを閉じると、AutoCAD が Database 領域をメモリから削除します。ただし、Database.ReadDwgFile メソッドでメモリ上に展開した Database インスタンスの領域は、展開したアドイン アプリがメモリーから削除しなければなりません。削除には上、記コードのように using ステートメントを利用してスコープ内の処理後に削除させるか、using ステートメントでスコープ指定せずに Dispose メソッドを明示的に呼び出すことも出来ます。 Database.ReadDwgFile メソッドで読み込んだ Database インスタンス内の DBText オブジェクトや AttributeReference オブジェクトの位置合わせを変更する場合、Database インスタンスを HostApplicationServices.WorkingDatabase プロパティで設定していないと予期しないズレが発生する場合があります。これは、DBText クラス実装が、位置合わせ時に現在の図面データベース(Database)を参照している関係で発生してしまう現象です。Database インスタンスを HostApplicationServices.WorkingDatabase プロパティで設定して処理した場合、処理後にオリジナル(ユーザーインターフェースに開いている図面の Database インスタンスを HostApplicationServices.WorkingDatabase プロパティで再設定する必要があります。
記事全体を表示
Issue AutoCAD .NET API でアプリケーションのフォーカス イベントを処理することは出来ますか?   Solution AutoCAD の Application ウィンドウ レベルでは、COM API の Application.AppActivate イベントと Application.AppDeactivate イベントを COM Interop でお使いいただくことが出来ます。 ご参考:COM ベースのイベントを登録する(.NET)   Autodesk.AutoCAD.Interop.AcadApplication? acAppCom = null; [CommandMethod("AddCOMEvent")] public void AddCOMEvent() { acAppCom = (Autodesk.AutoCAD.Interop.AcadApplication)Autodesk.AutoCAD.ApplicationServices.Application.AcadApplication; acAppCom.AppActivate += new _DAcadApplicationEvents_AppActivateEventHandler(appComAppActivate); acAppCom.AppDeactivate += new _DAcadApplicationEvents_AppDeactivateEventHandler(appComAppDeactivate); } [CommandMethod("RemoveCOMEvent")] public void RemoveCOMEvent() { if (acAppCom != null) { acAppCom.AppActivate -= new _DAcadApplicationEvents_AppActivateEventHandler(appComAppActivate); acAppCom.AppDeactivate -= new _DAcadApplicationEvents_AppDeactivateEventHandler(appComAppDeactivate); acAppCom = null; } } public void appComAppActivate() { Autodesk.AutoCAD.ApplicationServices.Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; ed.WriteMessage("\n*** AppActivate ***\n"); } public void appComAppDeactivate() { Autodesk.AutoCAD.ApplicationServices.Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; ed.WriteMessage("\n*** AppDeactivate ***\n"); }  
記事全体を表示
Issue AutoCAD .NET API を利用してサーフェス同士を結合することは出来ますか?   Solution AutoCAD .NET API には、サーフェス同士を結合する ObjectARX の AcDbSurface::booleanUnion() メンバ関数ラッパーである サーフェス同士を結合する Surface.BooleanUnion メソッドが用意されています。   次の C# 例は、Surface.BooleanUnion メソッドでサーフェス同士を結合するものです。   [CommandMethod("MyCommand", CommandFlags.Modal)] public void MyCommand() // This method can have any name { Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; PromptEntityOptions peo = new PromptEntityOptions("\n結合するサーフェスを選択:"); peo.SetRejectMessage("\nサーフェスを選択してください..."); peo.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Surface), false); PromptEntityResult per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; ObjectId objId1 = per.ObjectId; peo = new PromptEntityOptions("\n結合させたいサーフェスを選択:"); peo.SetRejectMessage("\nサーフェスを選択してください..."); peo.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Surface), false); per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; ObjectId objId2 = per.ObjectId; try { using (Transaction tr = db.TransactionManager.StartTransaction()) { Autodesk.AutoCAD.DatabaseServices.Surface surf1 = tr.GetObject(objId1, OpenMode.ForWrite) as Autodesk.AutoCAD.DatabaseServices.Surface; Autodesk.AutoCAD.DatabaseServices.Surface surf2 = tr.GetObject(objId2, OpenMode.ForWrite) as Autodesk.AutoCAD.DatabaseServices.Surface; Autodesk.AutoCAD.DatabaseServices.Surface surf = surf1.BooleanUnion(surf2); if (surf == null) { if (surf1 != null) { surf1.ColorIndex = 5; surf1.LineWeight = LineWeight.LineWeight100; } if (surf2 != null) { surf2.Erase(); } } else { surf1.Erase(); surf2.Erase(); ObjectId objId = SymbolUtilityServices.GetBlockModelSpaceId(db); BlockTableRecord btr = tr.GetObject(objId, OpenMode.ForWrite) as BlockTableRecord; surf.ColorIndex = 5; surf.LineWeight = LineWeight.LineWeight100; btr.AppendEntity(surf); tr.AddNewlyCreatedDBObject(surf, true); } tr.Commit(); } } catch(Autodesk.AutoCAD.Runtime.Exception ex) { ed.WriteMessage("\n" + ex.Message); } }  
記事全体を表示
Issue サーフェスの外形エッジやパス形状をオブジェクトとして再作成することは出来ますか?   Solution サーフェスの外形エッジやパス形状はサーフェスのサブエンティティ(FullSubentityPath)から形状を模倣して再作成することが出来ます。  Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; PromptEntityOptions peo = new PromptEntityOptions("\nサーフェスを選択:"); peo.SetRejectMessage("\nサーフェスを選択してください..."); peo.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Surface), false); PromptEntityResult per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) return; using (Transaction tr = db.TransactionManager.StartTransaction()) { ObjectId objId = per.ObjectId; Entity ent = tr.GetObject(objId, OpenMode.ForWrite) as Entity; ObjectId[] entId = new ObjectId[] { ent.ObjectId }; IntPtr pSubentityIdPE = ent.QueryX(AssocPersSubentityIdPE.GetClass(typeof(AssocPersSubentityIdPE))); if (pSubentityIdPE == IntPtr.Zero) return; AssocPersSubentityIdPE subentityIdPE = AssocPersSubentityIdPE.Create(pSubentityIdPE, false) as AssocPersSubentityIdPE; SubentityId[] edgeIds = subentityIdPE.GetAllSubentities(ent, SubentityType.Edge); objId = SymbolUtilityServices.GetBlockModelSpaceId(db); BlockTableRecord btr = tr.GetObject(objId, OpenMode.ForWrite) as BlockTableRecord; foreach (SubentityId subentId in edgeIds) { FullSubentityPath path = new FullSubentityPath(entId, subentId); Entity edge = ent.GetSubentity(path); if (edge != null) { edge.ColorIndex = 3; edge.LineWeight = LineWeight.LineWeight100; btr.AppendEntity(edge); tr.AddNewlyCreatedDBObject(edge, true); } } tr.Commit(); }  
記事全体を表示
Issue FIELD コマンドで選択したオブジェクトの面積をフィールド文字として作成することは出来ますか?        Solution AutoCAD .NET API では、文字(DBText)または、マルチテキスト(MText)ブロックを作成後、[フィールド] ダイアログ下部に表示される「フィールド式」を SetField メソッドで設定することで、特定のオブジェクトを参照するフィールド文字を作成することが出来ます。     フィールド式中の _ObjId の値は、選択したオブジェクトの ObjectId 値になります。 次の C# コードは、ポリラインの面積をフィールド文字として作図する例です。   [CommandMethod("MyCommand")] public void MyCommand() // This method can have any name { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; PromptEntityOptions options = new PromptEntityOptions("\nポリラインを選択:"); options.SetRejectMessage("\nポリラインを選択"); options.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Polyline), false); PromptEntityResult acSSPrompt = ed.GetEntity(options); if (acSSPrompt.Status != PromptStatus.OK) return; PromptPointOptions ppo = new PromptPointOptions("\n挿入点を指示:"); PromptPointResult ppr = ed.GetPoint(ppo); if (ppr.Status != PromptStatus.OK) return; using (Transaction tr = db.TransactionManager.StartTransaction()) { // %<\AcObjProp.16.2 Object(%<\_ObjId 3132455955584>%).Area \f "%lu2%pr1%ps[面積:, ㎡]%ct8[0.001]">% string strId = acSSPrompt.ObjectId.OldIdPtr.ToString(); string str1 = "%<\\AcObjProp.16.2 Object(%<\\_ObjId "; string str2 = ">%).Area \\f \"%lu2%pr1%ps[面積:, ㎡]%ct8[0.001]\">%"; string format = str1 + strId + str2; DBText text = new DBText(); text.Height = 10.0; text.Position = ppr.Value; ObjectId objId = SymbolUtilityServices.GetBlockModelSpaceId(db); BlockTableRecord btr = tr.GetObject(objId, OpenMode.ForWrite) as BlockTableRecord; btr.AppendEntity(text); tr.AddNewlyCreatedDBObject(text, true); Field entField = new Field(format); entField.Evaluate(); text.SetField(entField); tr.AddNewlyCreatedDBObject(entField, true); tr.Commit(); } }   作図したフィールド文字は、参照したポリラインの変化に追従するようになります。  
記事全体を表示
Issue AutoCAD .NET API で SURFEXTEND[延長サーフェス] コマンド と同じような処理は出来ますか?   Solution サーフェス エッジの延長は、Surface.ExtendEdges メソッド で実装することが出来ます。   次の C# コードは、選択した1つのサーフェス エッジを 100 ポイント延長するものです。 SURFEXTEND[延長サーフェス] コマンド と同様に、エッジの延長でサーフェスに自己交差すると延長に失敗する場合があります。   var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; try { PromptSelectionOptions pso = new PromptSelectionOptions(); pso.MessageForAdding = "\n延長するサーフェス エッジを選択:"; pso.SingleOnly = true; pso.SinglePickInSpace = true; pso.ForceSubSelections = true; PromptSelectionResult psr = ed.GetSelection(pso); if (psr.Status != PromptStatus.OK) return; SelectionSet ss = psr.Value; SelectedObject surfedge = ss[0]; ObjectId surfId = ss.GetObjectIds()[0]; if (!surfId.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(Autodesk.AutoCAD.DatabaseServices.Surface)))) { ed.WriteMessage("\nサーフェス エッジを選択を選択してください ..."); return; } using (var tr = db.TransactionManager.StartTransaction()) { var surf = tr.GetObject(surfId, OpenMode.ForWrite) as Autodesk.AutoCAD.DatabaseServices.Surface; if (surf != null) { if (surfedge.GetSubentities()[0].FullSubentityPath.SubentId.Type != SubentityType.Edge) { ed.WriteMessage("\n{0} が選択されました ...\nサーフェス エッジを選択を選択してください ...", surfedge.GetSubentities()[0].FullSubentityPath.SubentId.Type); tr.Abort(); return; } FullSubentityPath[] edges = { surfedge.GetSubentities()[0].FullSubentityPath }; surf.ExtendEdges(edges, 100.0, Autodesk.AutoCAD.DatabaseServices.Surface.EdgeExtensionType.ExtendEdge, true); } tr.Commit(); } } catch (Autodesk.AutoCAD.BoundaryRepresentation.Exception ex) { ed.WriteMessage("\nエラー: {0}\r\n at {1}", ex.Message, ex.StackTrace); }   なお、SURFEXTEND[延長サーフェス] コマンド によるサーフェス延長とは異なり、Surface.ExtendEdges メソッド  による延長では、延長されたサーフェス部分は、無条件に元のサーフェスに結合された状態になります。 SURFEXTEND[延長サーフェス] コマンド では、延長されたサーフェスを元のサーフェスと結合(合成)するか、別のサーフェスとして追加するか(既定値)をコマンド プションで指定することが出来ます。  
記事全体を表示
Issue コマンド フラグに CommandFlags.UsePickSet を指定したカスタムコマンドで Editor.SelectImplied メソッドを使い、事前選択したオブジェクトを取得しようとしています。使用しているコードは次のとおりです。   [CommandMethod("MyCommand", CommandFlags.Modal | CommandFlags.UsePickSet)] public void MyCommand() { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; PromptSelectionResult psr = ed.SelectImplied(); if (psr.Status == PromptStatus.OK) { ed.WriteMessage("\n{0} 個のオブジェクトが選択されました ...", psr.Value.GetObjectIds().Length.ToString()); } else { ed.WriteMessage("\nオブジェクトが選択されていません"); } }   このコードで事前選択したオブジェクトは取得出来ますが、コマンド実行時にオブジェクトの選択状態が解除されてしまいます。       コマンド実行時にのブジェクト選択状態の解除を抑止することは出来ますか?   Solution 事前選択したオブジェクトの選択状態を維持したままにするには、定義したカスタム コマンドのコマンド フラグに CommandFlags.Redraw を加える必要があります。コマンド定義内のコードになにかを加える必要はありません。   [CommandMethod("MyCommand", CommandFlags.Modal | CommandFlags.UsePickSet | CommandFlags.Redraw)] public void MyCommand() { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; PromptSelectionResult psr = ed.SelectImplied(); if (psr.Status == PromptStatus.OK) { ed.WriteMessage("\n{0} 個のオブジェクトが選択されました ...", psr.Value.GetObjectIds().Length.ToString()); } else { ed.WriteMessage("\nオブジェクトが選択されていません"); } }  
記事全体を表示
Issue カスタムコマンドで意図的にオブジェクトを選択させて、コマンド終了後も選択状態を維持させたいと思っています。次のようなコードを作成してみましたが、期待した状態になりません。   [CommandMethod("MyCommand", CommandFlags.Modal)] public void MyCommand() { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; Database db = doc.Database; PromptEntityOptions peo = new PromptEntityOptions("\nオブジェクトを選択:"); PromptEntityResult per = ed.GetEntity(peo); if (per.Status == PromptStatus.OK) { using (Transaction tr = db.TransactionManager.StartTransaction()) { Entity ent = (Entity)tr.GetObject(per.ObjectId, OpenMode.ForWrite); ent.Highlight(); tr.Commit(); } } }   具体的には、オブジェクトのハイライトは維持するものの、事前選択のようにグリップが表示されません。また、[プロパティ] パレットには選択したオブジェクトのプロパティも表示されません。      事前選択した状態のように、ハイライトとグリップ表示、[プロパティ] パレットへのプロパティ表示を実装するには、どのようにしたらいいでしょうか?   Solution 事前選択の状態は、Editor.SetImpliedSelection メソッドで選択状態にしたい ObjectId 配列を指定することで実装することが出来ます。   [CommandMethod("MyCommand", CommandFlags.Modal)] public void MyCommand() { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; PromptEntityOptions peo = new PromptEntityOptions("\nオブジェクトを選択:"); PromptEntityResult per = ed.GetEntity(peo); if (per.Status == PromptStatus.OK) { ObjectId[] objIds = [per.ObjectId]; ed.SetImpliedSelection(objIds); } }    
記事全体を表示
質問 AutoCAD .net APIで寸法拘束パラメータとユーザ定義パラメータをグループ化するグループフィルタを作成する方法はありますか。 回答 AutoCADのデータ構造として、グループフィルタはAssocNetworkのXData(拡張データ)にアプリケーション 名ACAD_NETWORK_GROUPSにて定義されます。グループフィルタの情報は、アプリケーションACAD_NETWORK_GROUPSの開始エントリ(1001,ACAD_NETWORK_GROUPS)に続いて、(1070,1)(1000, グループフィルタ名1)(1070,2)(1000, グループフィルタ名2)の形でデータを保持します。 ここで、Dxfグループ コード1070の値はグループフィルタの内部Idを、Dxfグループ コード1000の値はグループフィルタ名に対応します。   例えば拡張データの内容が(1001,ACAD_NETWORK_GROUPS)(1070,1)(1000, グループフィルタ1)(1070,2)(1000, グループフィルタ2)の場合、グループフィルタとして”グループフィルタ1”と”グループフィルタ2”が定義されており、それぞれの内部Idが1と2である状態となります。   グループフィルタ内のパラメータについては、上述の内部Idをパラメータ側から参照する形でAssocVariableのxDataに保存されている形となります。 AssocVariableのxDataにはアプリケーション名PARAMETER_GROUPSにて、(1001,PARAMETER_GROUPS)(1070,1)(1070,2)といった形でデータが保持されておりDxfグループコード1070の値が上述のグループフィルタのIdとなります。   このため、グループフィルタ(パラメータグループ)を新規作成する場合は、AssocNetworkのxDataに追加をする形となります。 また、既存のグループフィルタ内のパラメータを取得する場合には、AssocNetworkのxDataから対象のグループフィルタのIdを取得したのちに、各AssocVariableのxDataを参照して、PARAMETER_GROUPSアプリケーションに対象のグループフィルタのIdを持つデータがあるかを確認する形となります。   以下は新規のグループフィルタを作成するサンプルコードとなります。 [CommandMethod("AddFilterGroup")] public static void AddFilterGroup() { Document dc = Application.DocumentManager.MdiActiveDocument; Database db = dc.Database; Editor ed = dc.Editor; ObjectId networkId = AssocNetwork.GetInstanceFromObject(db.CurrentSpaceId, false, true, "ACAD_ASSOCNETWORK"); if (networkId == ObjectId.Null) return; const string APP_NAME = "ACAD_NETWORK_GROUPS"; PromptResult pr = ed.GetString("Type filter name to create."); string groupFilterName = pr.StringResult; using (Transaction tx = db.TransactionManager.StartTransaction()) { RegAppTable regTable = (RegAppTable)tx.GetObject(db.RegAppTableId, OpenMode.ForRead); if (!regTable.Has(APP_NAME)) { regTable.UpgradeOpen(); RegAppTableRecord app = new RegAppTableRecord(); app.Name = APP_NAME; regTable.Add(app); tx.AddNewlyCreatedDBObject(app, true); } using (AssocNetwork network = tx.GetObject(networkId, OpenMode.ForWrite, false) as AssocNetwork) { ResultBuffer rb = network.XData; if (rb != null) { IEnumerator enu = rb.GetEnumerator(); while (enu.MoveNext()) { TypedValue wk = (TypedValue)enu.Current; ed.WriteMessage("\n" + wk.TypeCode.ToString() + " = " + wk.Value.ToString()); } int nextIndex = 1; var wrapper = rb.Cast<TypedValue>(); //Find "ACAD_NETWORK_GROUPS" in xData if (0 != wrapper.Where<TypedValue>( tv => tv.Value.ToString() == APP_NAME && tv.TypeCode == 1001).Count<TypedValue>()) { //if there is "ACAD_NETWORK_GROUPS" in xData, check duplication of filter. bool inTargetApp = false; if (0 != wrapper.Where(tv =>{ if (inTargetApp) { if (tv.TypeCode == 1000 && tv.Value.ToString() == groupFilterName) { return true; } else if (tv.TypeCode == 1001) { inTargetApp = false; } } else { if (tv.Value.ToString() == APP_NAME && tv.TypeCode == 1001) { inTargetApp = true; } } return false; }).Count()) { //abort if there is same filter name. ed.WriteMessage("\nThe group fileter already exists."); tx.Abort(); return; } else { inTargetApp = false; nextIndex = wrapper.Max<TypedValue>(tv => { if (inTargetApp) { if (tv.TypeCode == 1070) { return Convert.ToInt32(tv.Value); } else if (tv.TypeCode == 1001) { inTargetApp = false; } } else { if (tv.Value.ToString() == APP_NAME && tv.TypeCode == 1001) { inTargetApp = true; } } return 0; } ) + 1; rb = new ResultBuffer(); inTargetApp = false; bool added = false; foreach (var tv in wrapper) { if (inTargetApp) { //start of next application if (tv.TypeCode == 1001) { inTargetApp = false; added = true; rb.Add(new TypedValue(1070, nextIndex)); rb.Add(new TypedValue(1000, groupFilterName)); } rb.Add(tv); } else { rb.Add(tv); if (tv.Value.ToString() == APP_NAME && tv.TypeCode == 1001) { inTargetApp = true; } } } if(!added) { rb.Add(new TypedValue(1070, nextIndex)); rb.Add(new TypedValue(1000, groupFilterName)); } } } else { //Add "ACAD_NETWORK_GROUPS" if not found. rb.Add(new TypedValue(1001, APP_NAME)); rb.Add(new TypedValue(1070, nextIndex)); rb.Add(new TypedValue(1000, groupFilterName)); } network.XData = rb; } else { network.XData = new ResultBuffer(new TypedValue(1001, APP_NAME), new TypedValue(1070, 1), new TypedValue(1000, groupFilterName)); } } tx.Commit(); } ed.Command("PARAMETERS"); }   また、以下は指定したパラメータグループに含まれているパラメータを確認するサンプルコードとなります。 [CommandMethod("GetFilterGroupParameters")] public static void GetFilterGroupParameters() { Document dc = Application.DocumentManager.MdiActiveDocument; Database db = dc.Database; Editor ed = dc.Editor; ObjectId networkId = AssocNetwork.GetInstanceFromObject(db.CurrentSpaceId, false, true, "ACAD_ASSOCNETWORK"); if (networkId == ObjectId.Null) return; const string APP_NAME = "ACAD_NETWORK_GROUPS"; PromptResult pr = ed.GetString("Type filter name to get parameters."); string groupFilterName = pr.StringResult; using (Transaction tx = db.TransactionManager.StartTransaction()) { RegAppTable regTable = (RegAppTable)tx.GetObject(db.RegAppTableId, OpenMode.ForRead); if (!regTable.Has(APP_NAME)) { ed.WriteMessage("\n There is no spcified parameter group."); return; } using (AssocNetwork network = tx.GetObject(networkId, OpenMode.ForWrite, false) as AssocNetwork) { ResultBuffer rb = network.XData; if (rb == null) { ed.WriteMessage("\n There is no spcified parameter group."); return; } IEnumerator enu = rb.GetEnumerator(); while (enu.MoveNext()) { TypedValue wk = (TypedValue)enu.Current; ed.WriteMessage("\n" + wk.TypeCode.ToString() + " = " + wk.Value.ToString()); } var wrapper = rb.Cast<TypedValue>(); bool inTargetApp = false; int targetIndex = 0; bool found = false; foreach(var tv in wrapper) if (inTargetApp) { if(tv.TypeCode == 1070) { targetIndex = Convert.ToInt32(tv.Value); } else if(tv.TypeCode == 1000 && tv.Value.ToString() == groupFilterName) { found = true; break; } } else { if (tv.Value.ToString() == APP_NAME && tv.TypeCode == 1001) { inTargetApp = true; } } } if (!found) { ed.WriteMessage("\n There is no spcified parameter group."); return; } //Now iterate though AssocActions to find variable which has targetIndex in it's xData. foreach (ObjectId actionId in network.GetActions) { if (actionId == ObjectId.Null) continue; DBObject obj = tx.GetObject(actionId, OpenMode.ForRead); if (actionId.ObjectClass.IsDerivedFrom(RXObject.GetClass(typeof(AssocVariable)))) { AssocVariable var = obj as AssocVariable; ed.WriteMessage("\n - AssocVariable " + var.Name + " = " + var.Expression); ResultBuffer pRb = var.XData; if (pRb != null) { var pWrapper = pRb.Cast<TypedValue>(); //loop thourough result buffer to find typedvalue with (1070, targetIndex). inTargetApp = false; foreach (var tv in pWrapper) { if (inTargetApp) { if (tv.TypeCode == 1070 && targetIndex == Convert.ToInt32(tv.Value)) { ed.WriteMessage(" is referenced by specified group."); } else if(tv.TypeCode == 1001) { inTargetApp = false; } } else { if (tv.Value.ToString() == "PARAMETER_GROUPS" && tv.TypeCode == 1001) { inTargetApp = true; } } } } } } } tx.Commit(); } }
記事全体を表示
質問 AutoCADの.net APIで寸法拘束の拘束フォームを変更する方法はありますか。 回答 残念ながら、AutoCADの.net APIには寸法拘束の拘束フォームを変更するAPIはありません。 一方でObjectARX C++のAPIには対応するAPIが存在するため、.netのプログラムからは、P/Invoke を用いることで.netから寸法拘束の拘束フォームを変更することが可能です。   寸法拘束の拘束フォームの変更には、ObjectARX C++の寸法線の基底クラスAcDbDimensionの、isConstraintDynamic()メソッドおよびsetConstraintDynamic(bool isDynamic)メソッドを使用します。   isConstraintDynamic()メソッドは、その戻り値で現在の拘束フォームの設定状態を確認することが出来ます(true:ダイナミック, false:注釈)。 また、setConstraintDynamic(bool isDynamic)メソッドは、引数に指定した値で拘束フォームの設定状態を変更することが可能です。   以下は、AutoCAD 2023でP/Invokeを用いて.net で作成したAutoCADのカスタムコマンドから、選択した寸法拘束の拘束フォームの設定を変更するサンプルコードとなります。 [CommandMethod("FlipConstraintsForm")] public void FlipConstraintsForm() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; var option = new PromptEntityOptions("\n" + "Select a dimension constraints."); PromptEntityResult result = ed.GetEntity(option); if (result.Status != PromptStatus.OK) return; ObjectId id = result.ObjectId; using (Transaction tx = db.TransactionManager.StartTransaction()) { Dimension dim = tx.GetObject(id, OpenMode.ForWrite) as Dimension; DatabaseExtensions.DatabaseExtensions.Dimension_FlipConstraintsForm(dim); tx.Commit(); } } //DatabaseExtensions.cs namespace DatabaseExtensions { public class DatabaseExtensions { private static class AcDbDimension { [DllImport("acdb24.dll", CallingConvention = CallingConvention.ThisCall, CharSet = CharSet.Unicode, EntryPoint = "?isConstraintDynamic@AcDbDimension@@QEBA_NXZ")] [return: MarshalAs(UnmanagedType.Bool)] public static extern Boolean isConstraintDynamic(IntPtr dimension); [DllImport("acdb24.dll", CallingConvention = CallingConvention.ThisCall, CharSet = CharSet.Unicode, EntryPoint = "?setConstraintDynamic@AcDbDimension@@QEAA?AW4ErrorStatus@Acad@@_N@Z")] public static extern ErrorStatus setConstraintDynamic(IntPtr dimension, [MarshalAs(UnmanagedType.Bool)] Boolean bDynamic); } public static void Dimension_FlipConstraintsForm(Dimension dim) { dim.UpgradeOpen(); bool isDynamic = AcDbDimension.isConstraintDynamic(dim.UnmanagedObject); AcDbDimension.setConstraintDynamic(dim.UnmanagedObject, !isDynamic); } } }   DllImportによりImportするdllファイル名は、対象のAutoCADのバージョンに依存して変わる点にご留意ください。   なお、P/Invokeを用いたAPIの実装方法についてはこちらの記事に開設がありますので、ご参照ください。
記事全体を表示