2010年11月22日月曜日

.NET - 図面が保存されることを認識する方法(AutoCAD 2011)

以前いただいていた .NET の質問があったのですが、AUJ などがあってそのままにしていたのですが、AUJ も終わったので今日はそのことについて調べてみました。

内容としては、SAVE コマンドでの保存、上書き保存、図面 CLOSE 時の保存、VBA の SAVE メソッドでの保存 など図面が保存されることを認識したいということでした。

Document.CloseWillStart Event でコマンド名が SAVE のときに処理するようにしてみたところ、図面 CLOSE 時の保存、VBA の SAVE メソッドでの保存 では認識してくれないということでした。

試してみたところ、確かに CommandWillStart では 図面 CLOSE 時の保存、VBA の SAVE メソッドでの保存 は認識してくれませんでした。
どうやら、その時は何のコマンドも動いていないみたい(AutoCAD 内部の機能で保存されている?)で、そのために認識することが出来ていないようです。

ということで、何かないかなーと思って arxmgd.chm を眺めていたら、Database.BeginSave Event っていうのを見つけました。きっとこれだと思って試してみたところ、図面 CLOSE 時の保存、VBA の SAVE メソッドでの保存 でもちゃんと認識してくれました。

試してみたときのコードはこんな感じです。

'------------------------------------------------
' AddHandler を実行したことがあるかどうかの判断用の変数
Dim jHandler As Boolean = False

' イベントを登録するコマンド
<CommandMethod("AVDO_BeginSave", "AVDO_BeginSave", "AVDO_BeginSave", CommandFlags.Modal)> _
Public Sub AVDO_BeginSave()
    Dim doc As Document = Application.DocumentManager.MdiActiveDocument
    Dim db As Database = doc.Database

    ' AddHandler を複数回実行すると正しく動作しないため、実行されているかどうかを判断する。
    If jHandler = False Then
        AddHandler db.BeginSave, New DatabaseIOEventHandler(AddressOf BeginSave)

        jHandler = True
    End If

End Sub

' Save が開始されたときに実行する関数
Public Sub BeginSave(ByVal sender As Object, ByVal e As DatabaseIOEventArgs)

    MsgBox("保存が始まったよ!!")

End Sub
'------------------------------------------------

AVDO_BeginSave コマンドを実行するとイベントが登録されます。
それぞれの図面で実行しておく必要があるようなので、acaddoc.lsp に AVDO_BeginSave コマンドを実行するように記述しておけばいいと思います。

2010年11月15日月曜日

.NET - ラバーバンドを表示して 2 点目を指定する(AutoCAD 2011)

今日も .NET 質問いただいたことについて書こうと思います。

例えば、左下点と右上点をクリックして範囲を指定する(クリックした左下点と右上点の座標値を取得する)ときにラバーバンドを表示したいっといった内容です。

こんなとき、下の絵のようにラバーバンドが出てくれないと、この範囲を指定してますって感じに見えないですよね。

GetCorner メソッドを使えば、2 点目を指定するときにラバーバンドが出てきます。

管理人が確認したコードは、こんな感じです。

'--------------------------------------
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Dim ed As Editor = doc.Editor

Dim ppo As New PromptPointOptions("1点目を指定")
ppo.AllowNone = False

Dim ppr As PromptPointResult = ed.GetPoint(ppo)

If ppr.Status <> PromptStatus.OK Then
    Return
End If

Dim pt1 As Point3d = ppr.Value

Dim pco As New PromptCornerOptions(vbLf & "2点目を指定", pt1)
ppr = ed.GetCorner(pco)

If ppr.Status <> PromptStatus.OK Then
    Return
End If

Dim pt2 As Point3d = ppr.Value

MsgBox("1点目:" & pt1.ToString & vbLf & "2点目:" & pt2.ToString)
'--------------------------------------

2010年11月12日金曜日

.NET - WMF ファイルに書き出す(AutoCAD 2011)

先日いただいたアンケートで、SendStringToExecute 以外の方法で WMF ファイルに書き出したいというものをいただきました。

VBA では、(ThisDrawing.Export fName, ""WMF"", sset) とやってできてたそうです。(管理人は、VBA はやったことないので勉強になりました。)

ということで、ググッてみたところ、ひとつ方法を見つけました。
実際に試してみたところ、WMF ファイルに書き出すことができました。

WMF 以外にも EXPORT コマンドで書き出せるファイルはできるんじゃないかと思います。

ただ、GetObject を使っているので、AutoCAD が複数起動しているときは、必ず最初に起動した AutoCAD が認識されちゃいます。
そのため、あとから起動した AutoCAD 上で実行しても、最初に起動した AutoCAD で開いている図面が WMF に書き出されちゃうので注意が必要です。
何かいい方法はないのかなと思いつつも見つけ出せないでいます。もし、何かご存じの方がいて教えてくれたら嬉しいです。

試してみたコードは以下のような感じです。
(プロジェクトを作るときに、「AutoCAD Interop Common」 と 「AutoCAD Interop」 にチェックを入れて、Imports Autodesk.AutoCAD.Interop と Imports Autodesk.AutoCAD.Interop.Common を追記しました。)

'----------------------------------------------------------------------------
Public Sub AVDO_ExportWMF()
    Dim acadApp As Autodesk.AutoCAD.Interop.AcadApplication
    Dim acadDoc As Autodesk.AutoCAD.Interop.AcadDocument
    Dim ssetObj As AcadSelectionSet = Nothing

    ' 起動している AutoCAD のインスタンスを読み込む(AutoCAD が複数起動しているときは、最初に起動した AutoCAD で実行されるので注意!!)
    acadApp = GetObject(, "AutoCAD.Application.18.1")
    acadDoc = acadApp.ActiveDocument

    Try
        ' セレクションセットの作成
        ssetObj = acadDoc.SelectionSets.Add("AVDO_SSET01")

        ' すべてのオブジェクトをセレクションセットに入れる
        ssetObj.Select(AcSelect.acSelectionSetAll)    'puts everything into the selection set object

        ' WMF ファイルに書き出し
        acadDoc.Export("C:\Temp\test", "wmf", ssetObj)

    Catch ex As System.Exception
        Application.ShowAlertDialog(ex.Message)

    Finally
        ' ssetObj を削除(これをやっておかないと、2回目からエラーになってしまう。)
        ssetObj.Delete()

    End Try
End Sub
'----------------------------------------------------------------------------

2010年11月6日土曜日

.NET(サンプル プログラム) - 「ハッチングの境界をまとめて選択」(AutoCAD 2011)

先日いただいたアンケートの中に、『ハッチングの境界だけをまとめて選択したい』 というものがありました。

普通のコマンドでは、そのようなことはできないと思うので、.NET でコマンドをひとつ作ってみました。

DLL ファイル(AVDO_SelectBoundaries.dll)は こちら です。

使い方は、こんな感じです。

  1. NETLOAD コマンドを実行して、AVDO_SelectBoundaries.dll をロードします。
  2. AVDO_SelectBoundaries コマンドを実行します。
  3. ハッチングの境界を探す対象の図形を選択します。(すべての図形を対象とするときは、ALL と入力します。)
  4. Enter キーを押して、選択を完了します。

すると、ハッチングの境界だけが選択された状態になります。

※ PICKFIRST にも対応しているので、図形を選択したあとに AVDO_SelectBoundaries コマンドを実行しても OK です。
※ 自動調整ハッチングの境界を探しているので、自動調整でないハッチングの境界は対象外です。

動画も作りましたので、見てみてください。


ハッチングの境界を見つける方法は比較的簡単に分かったんですが、コマンド終了後に境界が選択された状態にしておく方法がなかなか見つけられなくて苦労しました。

SetImpliedSelection っていうのでできるっていうのをググッて見つけたんですが、arxmgd.chm にはそれが載ってないんですよね。それなのに、みんななんで知ってるんでしょうね。

また、SetImpliedSelection がうまく動かなかったときに、Twitter で takamoda さんや u2suzuki さんに助言いただき助かりました。どうもありがとうございました。

ということで、コードはこんな感じです。

'-----------------------------------------------
' CommandFlags.Redraw の記述がないと、コマンド終了後に図形が選択された状態にならないので、必ず付けること!!
<CommandMethod("AVDO_SelectBoundaries", "AVDO_SelectBoundaries", CommandFlags.Modal + CommandFlags.Redraw)> _
Public Sub AVDO_SelectBoundaries()
    Dim doc As Document = Application.DocumentManager.MdiActiveDocument
    Dim db As Database = doc.Database
    Dim ed As Editor = doc.Editor

    ' コマンド実行時に選択されている図形設定
    Dim result As PromptSelectionResult = ed.SelectImplied()

    ' コマンド実行時に、図形が選択されていないときは、図形を選択するよう要求
    If (result.Status <> PromptStatus.OK) Then
        result = ed.GetSelection()
    End If

    ' 図形が選択されているときは以下を実行する
    If (result.Status = PromptStatus.OK) Then
        Using trans As Transaction = db.TransactionManager.StartTransaction
            Try
                Dim bt As BlockTable = trans.GetObject(db.BlockTableId, OpenMode.ForRead)
                Dim btRec As BlockTableRecord

                Dim cSpace As BlockTableRecord = trans.GetObject(db.CurrentSpaceId, OpenMode.ForRead)
                If cSpace.Name = "*Model_Space" Then
                    btRec = trans.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForWrite)
                Else
                    btRec = trans.GetObject(bt(BlockTableRecord.PaperSpace), OpenMode.ForWrite)
                End If

                'ハッチング境界用のコレクション
                Dim bObjIdColl As New ObjectIdCollection


                For Each selObj As SelectedObject In result.Value
                    Dim ent As Entity = trans.GetObject(selObj.ObjectId, OpenMode.ForRead)
                    Dim objIdColl As ObjectIdCollection = ent.GetPersistentReactorIds
                    For Each objId As ObjectId In objIdColl
                        Dim dbObj As DBObject = trans.GetObject(objId, OpenMode.ForRead)

                        'もし、リアクターのタイプが Hatch だったら、コレクションに追加
                        If TypeOf dbObj Is Hatch Then
                            bObjIdColl.Add(selObj.ObjectId)
                        End If
                    Next
                Next

                ' pickfirst set をクリア
                ed.SetImpliedSelection(New ObjectId(-1) {})

                ' コレクションを配列にダンプ
                Dim ids As ObjectId() = New ObjectId(bObjIdColl.Count - 1) {}
                bObjIdColl.CopyTo(ids, 0)

                ' pickfirst set を設定
                ed.SetImpliedSelection(ids)

            Catch ex As Autodesk.AutoCAD.Runtime.Exception
                Application.ShowAlertDialog(ex.Message)

            End Try
        End Using
    End If
End Sub
'-----------------------------------------------