LoginSignup
0
0

More than 3 years have passed since last update.

Autodesk Inventor API Hacking (BrowserNodeの複数選択)

Last updated at Posted at 2019-05-30

0. はじめに

DrawingDocumentのBrowserNodeには、難があります。
これらNodeをmouseで選択した時、SelectSetに入るObjectの型はunknownなのです。
突然で「何を言っているんだ・・・」と思うかもしれませんが、DrawingView内の要素(線)をいじろうとすると、すぐにわかります。
手作業ではBrowserNode単位で選択 → 線種変更できるのですが、現状のAPIで自動化しようとすると、全DrawingCurveについて1つずつ処理しないといけないので、ものすごく遅くなります。

「線種変更は手作業で右クリックプロパティーするから、せめて条件に当てはまる項目を自動選択出来れば・・・」というのが、発端です。

1. BrowserNode.DoSelect()

普通に考えれば、

InventorApplication.ActiveDocument.SelectSet.SelectMultiple(collection);

すれば良いだけの話です。でも、このcollectionに入れる要素の型が公開されていないので、この方法では選択できません。

SelectSetに頼らない、もう1つの選択方法があります。それは、DoSelect()です。
BrowserNodesを辿り、目的のBrowserNodeを見つけてnode.DoSelect()すれば、選択できます。
しかし、この方法では、前回選択した内容がクリアされる == 常に最後の1つしか選択されないという欠点があります。

2. Control key

しかし、私は発見しました。keyboard(そう、物理キーボードです)のControl keyを押しっぱなしにしながら、DoSelect()を複数回呼び出すと、全てのObjectが選択されるのです。
これは、DoSelect()がまさにmouseの左クリックをエミュレートしているからです。(というか、実際は逆で、OnMouseClickがDoSelect()を呼んでいるのでしょう)

ここまで来れば、やることは1つです。あたかもControl keyが押されているかのごとく、振舞えば良いのです。

3. GetKeyboardState / SetKeyboardState

SendMessage, PostMessage, WH_KEYBOARD_LLなどと遠回りをしましたが、最終的にはGetKeyboardStateとSetKeyboardStateを使って、Control keyを押している振りをさせることに成功しました。

internal class NativeMethods
{
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    public static extern bool GetKeyboardState(byte[] lpKeyState);
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    public static extern bool SetKeyboardState(byte[] lpKeyState);
}
const int VK_CONTROL = 0x11;

void foo()
{
    byte[] keys = new byte[256];
    byte savedControlKey;

    // Controlキーを押しっぱなしにする
    NativeMethods.GetKeyboardState(keys);
    savedControlKey = keys[VK_CONTROL];
    keys[VK_CONTROL] = 0x80;
    NativeMethods.SetKeyboardState(keys);

    try
    {
        //
        //  ループか何かで、複数回BrowserNode.DoSelect()する
        //
    }
    finally
    {
        // Controlキーを復元する
        keys[VK_CONTROL] = savedControlKey;
        NativeMethods.SetKeyboardState(keys);
    }
}

実行時にユーザーがControl Keyを押している可能性も考慮して、変更前の状態を保管しておいて、作業終了後に元に戻しています。
まずはこの実装で問題ないと思いますが、

  • 処理時間が長い
  • 実行頻度が非常に高い (例えば、キーを1文字入力ごとに実行)

などの場合は、書き戻す前にもう一度GetkeyboardState()した方が良いかもしれません。
その場合、物理Control keyの状態が変化した場合はどうするんだ、と言われるとその通りで、厳密にはGetAsyncKeyState(VK_CONTROL)で状態を確認した方が良いのでしょう。
(GetAsyncKeyState()は、SetKeyboardState()の影響を受けないようです)

また別の問題として、DoSelect()するループ中に、物理Control keyをdown→upされると、状態が解除されてしまいます。

これらの問題を避けるには、DoSelect()の直前/直後で、Control keyを設定/解除すれば良いと思いますが、そこまでするかどうかは皆様の判断にお任せします。

4. DoSelect()を確実に実行するための補足 (19.06.01)

DrawingDocumentを開きたての状態で下層のnodeをDoSelect()しても、選択されないことがあります。遅延バインディングをしているのか、nodeを開かないと選択できないようです。一旦開けば、後は閉じても選択できます。
とりあえず、以下のような回避方法を考えました。

if (node.Parent is BrowserNode parentNode)
{
    // BrowserNodeを開く
    parentNode.Expanded = true;
}
node.DoSelect();

選択した後にnodeが(厳密には親nodeが)開きっぱなしになるのですが、私は自分用AddInなので、別に良しとしています。

parentNode.Expanded = falseしても、親の親は閉じません。
なので、厳密に実行前の状況を保管したいのなら、親を再帰的に辿りながら、nodeがkeyでnode.ExpandedがvalueのDictionaryを作り、最後にforeachで復帰すると良いでしょう。

99. 親の記事に戻る

Autodesk Inventor API Hacking (概略)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0