web-dev-qa-db-ja.com

表示せずにWPFウィンドウをロードする

PInvoking RegisterHotKey()でウィンドウを表示するグローバルホットキーを作成します。しかし、これを行うには、ウィンドウが読み込まれるまで存在しない、つまり初めて表示されるウィンドウのHWNDが必要です。しかし、ホットキーを設定する前にウィンドウを表示したくありません。ユーザーには見えないそのウィンドウのHWNDを作成する方法はありますか?

50
svick

.NET 4.0をターゲットにしている場合は、EnsureHandleで利用可能な新しいWindowInteropHelperメソッドを利用できます。

public void InitHwnd()
{
    var helper = new WindowInteropHelper(this);
    helper.EnsureHandle();
}

これを指摘してくれたThomas Levesqueに感謝します。

古いバージョンの.NET Frameworkをターゲットにしている場合、最も簡単な方法は、いくつかのプロパティを設定しながらウィンドウを表示してHWNDにアクセスし、ウィンドウが非表示になり、フォーカスが奪われないようにすることです。

var window = new Window() //make sure the window is invisible
{
    Width = 0,
    Height = 0,
    WindowStyle = WindowStyle.None,
    ShowInTaskbar = false,
    ShowActivated = false
};
window.Show();

実際のウィンドウを表示したい場合は、コンテンツ、サイズを設定し、スタイルを通常のウィンドウに戻すことができます。

64
Patrick Klug

ウィンドウをいわゆるメッセージ専用ウィンドウに変更することもできます。このウィンドウタイプはグラフィック要素をサポートしていないため、表示されることはありません。基本的にそれは呼び出すことになります:

    SetParent(hwnd, (IntPtr)HWND_MESSAGE);

常に非表示になる専用のメッセージウィンドウを作成するか、実際のGUIウィンドウを使用して、表示するときに通常のウィンドウに戻します。より完全な例については、以下のコードを参照してください。

    [DllImport("user32.dll")]
    static extern IntPtr SetParent(IntPtr hwnd, IntPtr hwndNewParent);

    private const int HWND_MESSAGE = -3;

    private IntPtr hwnd;
    private IntPtr oldParent;

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;

        if (hwndSource != null)
        {
            hwnd = hwndSource.Handle;
            oldParent = SetParent(hwnd, (IntPtr)HWND_MESSAGE);
            Visibility = Visibility.Hidden;
        }
    }

    private void OpenWindowMenuItem_Click(object sender, RoutedEventArgs e)
    {
        SetParent(hwnd, oldParent);
        Show();
        Activate();
    }

私にとって、幅、高さをゼロに設定し、スタイルをなしに設定するという解決策はうまくいきませんでした。それでも、0x0ウィンドウの周りの境界のように見えるのに迷惑な影が付いた小さなウィンドウが表示されたためです(Windows 7でテスト済み) )。したがって、私はこの代替オプションを提供しています。

17
DJP

これは汚いハックですが、動作するはずであり、不透明度を変更することの欠点はありません。

  • WindowStartupLocationManualに設定します
  • TopおよびLeftプロパティを画面外のどこかに設定します
  • ShowInTaskbarをfalseに設定して、ユーザーが新しいウィンドウがあることに気付かないようにします
  • ShowおよびHideウィンドウ

これで、HWNDを取得できるはずです。

編集:別のオプション、おそらくより良い:ShowInTaskBarをfalseに設定し、WindowStateMinimizedに設定して、それを表示する:まったく表示されない

16
Thomas Levesque

その質問への回答は既に投稿していましたが、より良い解決策を見つけました。

実際にウィンドウを表示せずにHWNDが作成されていることを確認するだけの場合は、次のようにできます。

    public void InitHwnd()
    {
        var helper = new WindowInteropHelper(this);
        helper.EnsureHandle();
    }

(実際にはEnsureHandleメソッドは、質問の投稿時には利用できませんでした。NET4.0で導入されました)

10
Thomas Levesque

私はあなたがしていることをやろうとしたことはありませんが、HWNDを取得するためにウィンドウを表示する必要があるが、showを使用したくない場合は、ウィンドウの不透明度を0に設定します。また、ヒットテストの発生を防ぎます。次に、ウィンドウにパブリックメソッドを設定して、表示させたいときに不透明度を100に変更できます。

5
Joel Cochran

私はWPFについてまったく何も知りませんが、WM_HOTKEYメッセージを受信するために他の手段(たとえば、PInvoke)を使用して メッセージのみのウィンドウ を作成できますか?はいの場合、WM_HOTKEYを受け取ったら、そこからWPFウィンドウを起動できます。

3
Agnel Kurian

ウィンドウが初期化されているときに最後に起こることは、通常と異なる場合のWindowStateの変更であることに気付きました。したがって、実際にそれを利用することができます:

public void InitializeWindow(Window window) {
    window.Top = Int32.MinValue;
    window.Left = Int32.MinValue;

    window.Width = 0;
    window.Height = 0;

    window.ShowActivated = false;
    window.ShowInTaskbar = false;
    window.Opacity = 0;

    window.StateChanged += OnBackgroundStateChanged;

    window.WindowStyle = WindowStyle.None;
}

public void ShowWindow(Window window) {
    window.Show();
    window.WindowState = WindowState.Maximized;
}

protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
    if (isStateChangeFirst) {
        isStateChangeFirst = false;

        window.Top = 300;
        window.Left = 200;

        window.Width = 760;
        window.Height = 400;

        window.WindowState = WindowState.Normal;

        window.ShowInTaskbar = true;
        window.Opacity = 1;
        window.Activate();
    }
}

それは私には十分にうまくいきます。また、ハンドルなどを操作する必要はなく、さらに重要なことに、ウィンドウのカスタムクラスを用意する必要もありません。これは、動的に読み込まれるXAMLに最適です。また、フルスクリーンアプリを作成する場合にも最適です。状態を通常に戻したり、適切な幅と高さを設定したりする必要さえありません。ただ行く

protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
    if (isStateChangeFirst) {
        isStateChangeFirst = false;

        window.ShowInTaskbar = true;
        window.Opacity = 1;
        window.Activate();
    }
}

これで完了です。

そして、ウィンドウのロード時に状態の変更が最後に行われるという私の推測が間違っていても、他のイベントに変更することができますが、それは実際には重要ではありません。

0
Nullcaller

ウィンドウのサイズを0 x 0ピクセルにし、ShowInTaskBarをfalseに設定して表示し、必要に応じてサイズを変更します。

0
luvieere

WindowInteropHelperクラスを使用すると、WPFウィンドウのHWNDを取得できます。

MyWindow win = new MyWindow();
WindowInteropHelper helper = new WindowInteropHelper(win);

IntPtr hwnd = helper.Handle;

MSDNドキュメント

0
Shannon Cornish

不透明度を0に設定するのと同様の別のオプションは、サイズを0に設定し、位置を画面の外に設定することです。これには、AllowsTransparency = Trueは必要ありません。

また、一度表示したら、非表示にしてもhwndを取得できることを忘れないでください。

0
Ben Childs

非表示のウィンドウを表示するための拡張メソッドを作成しました。次のShow呼び出しは問題なく動作します。

public static class WindowHelper
{
    public static void ShowInvisible(this Window window)
    {
        // saving original settings
        bool needToShowInTaskbar = window.ShowInTaskbar;
        WindowState initialWindowState = window.WindowState;

        // making window invisible
        window.ShowInTaskbar = false;
        window.WindowState = WindowState.Minimized;

        // showing and hiding window
        window.Show();
        window.Hide();

        // restoring original settings
        window.ShowInTaskbar = needToShowInTaskbar;
        window.WindowState = initialWindowState;
    }
}
0