Autodesk Inventor API Hacking (WPFのWindowを表示)

# 0. はじめに
[Freeradical](https://www.freeradical.jp)の中の人、[yamarah](https://qiita.com/yamarah)です。
いつまでもWinFormsではつらいので、WPFを使おうという話しです。
WPF自体については解説しません。Formの代わりにWPFのWindowを表示するには、どうすれば良いのかを説明します。

# 1. ProjectにWPFを追加するための準備
Visual Studio → `ソリューション エクスプローラー`で右クリック → `追加` → `新しい項目`で'WPF'が無い場合があります。
![AddWPF.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424028/6654a0b2-c486-abdc-340b-9ba7dc076f38.png)
上の画像には`WPF`がありますが、もし見当たらない場合は、Projectで`UseWPF`を`true`にします。
これで、`新しい項目の追加`に`WPF`が表示されるはずです。
また、実際にWPFのWindowを追加した後に、

> Windows デスクトップ アプリケーションを作成するには、Microsoft.NET.Sdk.WindowsDesktop が必要です。現在の SDK では、'UseWpf' と 'UseWindowsForms' はサポートされていません。

と警告が出る場合があります。この場合は、警告に従って、Projectの1行目を

```xml
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
```

に変更しましょう。
これらの変更を施したProjectの冒頭部分は、このようになると思います。

```xml
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

  <PropertyGroup>
    <OutputType>Library</OutputType>
    <TargetFramework>net472</TargetFramework>
    <UseWPF>true</UseWPF>
    <RootNamespace>InvAddIn</RootNamespace>
    <LangVersion>8.0</LangVersion>
    <Nullable>enable</Nullable>
  </PropertyGroup>
```

[以前の記事(C#8.0、そして.NET Core3.0)](https://qiita.com/yamarah/items/06bb1a67c88ad85277e2)に従って、`nullable`も有効にしています。
ちなみに、WindowsFormsも使う場合は、

```xml
    <UseWindowsForms>true</UseWindowsForms>
```

を加えます。

# 2. 実際に表示する
表示するだけなら、単純に

```c#
var wpfWindow = new Window1();
wpfWindow.ShowDialog();    // modalの場合
// wpfWindow.Show();       // modelessの場合
```

とすれば、表示されます。しかし、これだけだと、WinFormsについて書いた[以前の記事(Modeless FormのZ order)](https://qiita.com/yamarah/items/80ea83d6697581bb7eb7)と同じ問題が発生して、WindowがInventorの後ろに隠れうる状態です。
WPFでは、WinFormsの場合と違って、modalでもInventorの後ろに回ります。
解決策は、modelessなWinFormsと同じように親子関係の設定をする必要があり、WPFでは次のようにします。

```c#
var wpfWindow = new Window1();
// Window Handleを操作するためのHelper
var helper = new System.Windows.Interop.WindowInteropHelper(wpfWindow);
// 親を設定する
helper.Owner = new System.IntPtr(InventorApplication.MainFrameHWND);
// 表示する
wpfWindow.ShowDialog();    // modalの場合
// wpfWindow.Show();       // modelessの場合
```

# 3. Dockable Windowに表示したい
基本的には、WinFormsの場合と同じ手順でWPF Windowを表示できます。
(helperからHandleを取る前に、EnsureHandle()を呼ぶのと、VisibilityをVisbleにするのを忘れないように!!)
しかしながら、残念なことにキー入力ができません。[Inventor Forum](https://forums.autodesk.com/t5/inventor-forum/bd-p/78)の

[Dockable Window with WPF controls, don't receive keyboard input](https://forums.autodesk.com/t5/inventor-forum/dockable-window-with-wpf-controls-don-t-receive-keyboard-input/m-p/9115997)

によると、Inventor 2018から発病しているbugっぽいです。現時点(Inventor 2020.2)では、直接WPF Windowを貼っても、WinFormsで包んでも、キー入力ができません。
&nbsp;&nbsp;&nbsp; ↓
**2020.01.09 追記**
Messageをhookして、`WM_GETDLGCODE`に応答することで、キー入力できるようになることが分かりました。
以下は、そのサンプルコードです。

```c#
public void Activate(ApplicationAddInSite addInSiteObject, bool firstTime)
{
    InventorApplication = addInSiteObject.Application;

    // WPF Windowの初期化
    var wpfWindow = new WpfWindow();
    wpfWindow.WindowStyle = System.Windows.WindowStyle.None;
    wpfWindow.ResizeMode = System.Windows.ResizeMode.NoResize;
    wpfWindow.Visibility = System.Windows.Visibility.Visible;

    // WPF WindowのHandleを得る
    var helper = new WindowInteropHelper(wpfWindow);
    helper.EnsureHandle();
    var handle = helper.Handle;

    // Dockable Windowの作成
    var dockableWindow = InventorApplication.UserInterfaceManager.DockableWindows.Add(System.Guid.NewGuid().ToString(), "Test", "Test");
    dockableWindow.AddChild(handle);

    // Key hookをセット
    HwndSource.FromHwnd(handle).AddHook(WndProc);
}

private const UInt32 DLGC_WANTARROWS = 0x0001;
private const UInt32 DLGC_WANTTAB = 0x0002;
private const UInt32 DLGC_WANTALLKEYS = 0x0004;
private const UInt32 DLGC_HASSETSEL = 0x0008;
private const UInt32 DLGC_WANTCHARS = 0x0080;
private const UInt32 WM_GETDLGCODE = 0x0087;

private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == WM_GETDLGCODE)
    {
        handled = true;
        return new IntPtr(DLGC_WANTCHARS | DLGC_WANTARROWS | DLGC_HASSETSEL | DLGC_WANTTAB | DLGC_WANTALLKEYS);
    }
    return IntPtr.Zero;
}

```

# 99. 親の記事に戻る
[Autodesk Inventor API Hacking (概略)](https://qiita.com/yamarah/items/cee08eb8f2b7aecbda83)