Softex CelwareTech Blog
VSTO Officeアドイン2026-05-09

VSTO + WPF のキーボードフォーカス問題を CustomTaskPane で解決する

VSTO アドインで WPF Window を表示すると Excel にフォーカスを奪われる問題を、CustomTaskPane と ElementHost で解決する実装パターンを紹介します。

VSTOWPFCustomTaskPaneElementHostExcel

はじめに

VSTO アドインから WPF Window を Show() で表示すると、見た目はウィンドウが出ていても、キーボード入力や矢印キーが Excel 側へ戻ってしまうことがあります。

Activate()SetForegroundWindowAttachThreadInput などを組み合わせても、Office のフォーカス管理とぶつかると安定しません。ShowDialog() なら動くこともありますが、Excel 操作と並行できないため、実用上困る場面があります。

この問題は、WPF Window を独立ウィンドウとして出すのではなく、VSTOCustomTaskPaneWPF UserControl を埋め込むことで解決しやすくなります。

こんな場面で使えます

  • Excel VSTO アドインに WPF UI を載せたい
  • WPF Window の ListView や TextBox がキーボード入力を受け取れない
  • Excel と並行して操作できるツール画面を作りたい
  • ShowDialog() ではなく非モーダル表示にしたい
  • 見た目は独立ウィンドウに近いが、Office のフォーカス管理に乗せたい

実装パターン

構成は次のようにします。

[VSTO Add-in]
  CustomTaskPane を作る

[WinForms UserControl]
  ElementHost を置く

[WPF UserControl]
  実際の画面を実装する

WPF Window ではなく WPF UserControl を作り、WinForms の ElementHost 経由で CustomTaskPane に載せます。

実装コード

WinForms 側のホストコントロールを用意します。

public partial class AddinTaskPaneHost : System.Windows.Forms.UserControl
{
    public MainView WpfView { get; }

    public AddinTaskPaneHost(IAddinHost host)
    {
        InitializeComponent();

        WpfView = new MainView(host);

        var elementHost = new System.Windows.Forms.Integration.ElementHost
        {
            Dock = System.Windows.Forms.DockStyle.Fill,
            Child = WpfView,
        };

        Controls.Add(elementHost);
    }
}

VSTO 側で CustomTaskPane を生成します。

private Microsoft.Office.Tools.CustomTaskPane _taskPane;
private AddinTaskPaneHost _hostControl;

private void EnsureTaskPane()
{
    if (_taskPane != null) return;

    _hostControl = new AddinTaskPaneHost(new HostImpl(this));
    _taskPane = CustomTaskPanes.Add(_hostControl, "Tool Window");

    _taskPane.DockPosition =
        Microsoft.Office.Core.MsoCTPDockPosition.msoCTPDockPositionFloating;

    SafeSet(() => _taskPane.Width = 1200);
    SafeSet(() => _taskPane.Height = 800);
}

public void ToggleTaskPane()
{
    EnsureTaskPane();
    _taskPane.Visible = !_taskPane.Visible;
}

private static void SafeSet(System.Action action)
{
    try { action(); }
    catch { }
}

リボンのボタンから切り替えます。

public void OnToggleClick(Office.IRibbonControl control)
{
    Globals.ThisAddIn.ToggleTaskPane();
}

設計のポイント

CustomTaskPane は Office のメインウィンドウと同じフォーカス管理の中で扱われます。そのため、独立した WPF Window よりも、Excel とのキーボードフォーカス衝突が起きにくくなります。

MsoCTPDockPositionFloating を使うと、見た目は独立ウィンドウに近くなります。ただし、内部的には Office のタスクペインとして管理されます。

WPF UI と VSTO の間は、コールバック interface で分離しておくと再利用しやすくなります。

public interface IAddinHost
{
    string PickFolder(string title);
    void OpenVbe(string projectName, string moduleName, int line);
    void HidePane();
}

注意点・ハマりポイント

  • VSTO プロジェクトに WindowsFormsIntegration.dll 参照が必要
  • System.Windows.Forms 参照も必要
  • CustomTaskPane.WidthHeight はドック状態によって COM 例外を投げるため、設定は try-catch で守る
  • Floating のウィンドウ位置は CustomTaskPane API だけでは細かく制御できない
  • WPF 側を Window ではなく UserControl として作る
  • WPF UserControl が VSTOCOM に直接依存しないようにする

実際の活用事例

VBA 解析や Excel 支援ツールのように、Excel を開いたまま横にツール画面を出したい場合に有効です。検索結果リスト、ツリー、コードビューアなどを WPF で作り、Excel 側の状態取得や VBE ジャンプだけを VSTO 側の host interface に任せます。

これにより、UI は WPF として作り込める一方で、Office のフォーカス問題を避けられます。

まとめ

VSTOWPF Window のフォーカス問題に詰まったら、Win32 API で無理に前面化するより、CustomTaskPane に載せる構成を検討します。

CustomTaskPane + WinForms ElementHost + WPF UserControl の組み合わせは、Office アドインで実用的な WPF UI を作るための安定したパターンです。

この技術で業務改善しませんか?

Excel VBA・GAS・Webアプリで業務の自動化ツールを開発しています。 「こんなことできる?」というご相談だけでもお気軽にどうぞ。

無料相談はこちら →