web-dev-qa-db-ja.com

WPFでウィンドウを前面に移動する

WPFアプリケーションをデスクトップの前面に表示するにはどうすればよいですか?これまで私は試しました:

SwitchToThisWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle, true);

SetWindowPos(new WindowInteropHelper(Application.Current.MainWindow).Handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

SetForegroundWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle);

いずれもジョブを実行していません(Marshal.GetLastWin32Error()はこれらの操作が正常に完了したと言っており、各定義のP/Invoke属性にはSetLastError=trueがあります)。

新しい空のWPFアプリケーションを作成し、タイマーを使用してSwitchToThisWindowを呼び出すと、期待どおりに動作するため、元のケースで動作しない理由がわかりません。

編集:グローバルホットキーと組み合わせてこれを行っています。

203
Factor Mystic

さて、私は回避策を見つけました。ホットキーの実装に使用されるキーボードフックから呼び出しを行っています。一時停止してBackgroundWorkerに呼び出した場合、呼び出しは期待どおりに機能します。それはちょっとしたことですが、なぜ元々機能していなかったのか分かりません。

void hotkey_execute()
{
    IntPtr handle = new WindowInteropHelper(Application.Current.MainWindow).Handle;
    BackgroundWorker bg = new BackgroundWorker();
    bg.DoWork += new DoWorkEventHandler(delegate
        {
            Thread.Sleep(10);
            SwitchToThisWindow(handle, true);
        });
    bg.RunWorkerAsync();
}
2
Factor Mystic
myWindow.Activate();

ウィンドウをフォアグラウンドに移動してアクティブ化しようとします。

私が誤解していて、常に手前の動作が必要な場合を除き、これでうまくいくはずです。その場合、以下が必要です。

myWindow.TopMost = true;
291

ウィンドウを一番上に表示するソリューションを見つけましたが、通常のウィンドウとして動作します。

if (!Window.IsVisible)
{
    Window.Show();
}

if (Window.WindowState == WindowState.Minimized)
{
    Window.WindowState = WindowState.Normal;
}

Window.Activate();
Window.Topmost = true;  // important
Window.Topmost = false; // important
Window.Focus();         // important
156
Jader Dias

初めてロードするときにウィンドウを前面に表示する必要がある場合は、次を使用する必要があります。

private void Window_ContentRendered(object sender, EventArgs e)
{
    this.Topmost = false;
}

private void Window_Initialized(object sender, EventArgs e)
{
    this.Topmost = true;
}
29
Amir

これを簡単にコピー&ペーストするために-
このクラスのDoOnProcessメソッドを使用して、プロセスのメインウィンドウを前景に移動します(ただし、他のウィンドウからフォーカスを奪うことはありません)

public class MoveToForeground
{
    [DllImportAttribute("User32.dll")]
    private static extern int FindWindow(String ClassName, String WindowName);

    const int SWP_NOMOVE        = 0x0002;
    const int SWP_NOSIZE        = 0x0001;            
    const int SWP_SHOWWINDOW    = 0x0040;
    const int SWP_NOACTIVATE    = 0x0010;
    [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);

    public static void DoOnProcess(string processName)
    {
        var allProcs = Process.GetProcessesByName(processName);
        if (allProcs.Length > 0)
        {
            Process proc = allProcs[0];
            int hWnd = FindWindow(null, proc.MainWindowTitle.ToString());
            // Change behavior by settings the wFlags params. See http://msdn.Microsoft.com/en-us/library/ms633545(VS.85).aspx
            SetWindowPos(new IntPtr(hWnd), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
        }
    }
}

HTH

20

私はこの質問がかなり古いことを知っていますが、この正確なシナリオに出会ったばかりで、実装したソリューションを共有したかったのです。

このページのコメントで述べたように、提案されたソリューションのいくつかはXPで機能しません。これは私のシナリオでサポートする必要があります。 @Matthew Xavierによる一般的にこれは悪いUXプラクティスであるという意見には同意しますが、それが完全に妥当なUXである場合があります。

WPFウィンドウを一番上に表示するソリューションは、グローバルホットキーを提供するために使用しているのと同じコードによって実際に提供されました。 Joseph Cooneyのブログ記事 には、元のコードが含まれている 彼のコードサンプルへのリンク が含まれています。

コードを少し整理して修正し、System.Windows.Windowの拡張メソッドとして実装しました。これをXP 32ビットとWin7 64ビットでテストしましたが、どちらも正常に動作します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Interop;
using System.Runtime.InteropServices;

namespace System.Windows
{
    public static class SystemWindows
    {
        #region Constants

        const UInt32 SWP_NOSIZE = 0x0001;
        const UInt32 SWP_NOMOVE = 0x0002;
        const UInt32 SWP_SHOWWINDOW = 0x0040;

        #endregion

        /// <summary>
        /// Activate a window from anywhere by attaching to the foreground window
        /// </summary>
        public static void GlobalActivate(this Window w)
        {
            //Get the process ID for this window's thread
            var interopHelper = new WindowInteropHelper(w);
            var thisWindowThreadId = GetWindowThreadProcessId(interopHelper.Handle, IntPtr.Zero);

            //Get the process ID for the foreground window's thread
            var currentForegroundWindow = GetForegroundWindow();
            var currentForegroundWindowThreadId = GetWindowThreadProcessId(currentForegroundWindow, IntPtr.Zero);

            //Attach this window's thread to the current window's thread
            AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, true);

            //Set the window position
            SetWindowPos(interopHelper.Handle, new IntPtr(0), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

            //Detach this window's thread from the current window's thread
            AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, false);

            //Show and activate the window
            if (w.WindowState == WindowState.Minimized) w.WindowState = WindowState.Normal;
            w.Show();
            w.Activate();
        }

        #region Imports

        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

        [DllImport("user32.dll")]
        private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

        [DllImport("user32.dll")]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        #endregion
    }
}

このコードが、この問題に遭遇した他の人に役立つことを願っています。

17
Zodman

ユーザーが別のアプリケーションと対話している場合、自分のアプリケーションを前面に表示できない場合があります。一般的なルールとして、プロセスは、そのプロセスが既にフォアグラウンドプロセスである場合にのみフォアグラウンドウィンドウを設定することを期待できます。 (Microsoftは SetForegroundWindow() MSDNエントリで制限を文書化しています。)これは次の理由によります。

  1. ユーザーはフォアグラウンドを「所有」します。たとえば、ユーザーが入力している最中に別のプログラムがフォアグラウンドを盗み、少なくともワークフローを中断すると、変更に気付くまで1つのアプリケーションのキーストロークが犯罪者によって誤って解釈されるため、意図しない結果を引き起こす可能性があります。
  2. 2つのプログラムのそれぞれが、そのウィンドウがフォアグラウンドであるかどうかを確認し、そうでない場合はフォアグラウンドに設定しようとすることを想像してください。 2番目のプログラムが実行されるとすぐに、タスクスイッチごとにフォアグラウンドが2つの間でバウンドするため、コンピューターは役に立たなくなります。
13
Matthew Xavier

Shellオブジェクトを介してAccessアプリケーションから呼び出されるWPFアプリケーションでも同様の問題が発生しました。

私の解決策は以下です-x86ターゲットにコンパイルされたアプリでXPおよびWin7 x64で動作します。

Alt-tabをシミュレートするよりもむしろこれをやりたいと思います。

void Window_Loaded(object sender, RoutedEventArgs e)
{
    // make sure the window is normal or maximised
    // this was the core of the problem for me;
    // even though the default was "Normal", starting it via Shell minimised it
    this.WindowState = WindowState.Normal;

    // only required for some scenarios
    this.Activate();
}
6
Seth

このページの回答の一部が間違っている理由!

  • window.Focus()を使用する回答はすべて間違っています。

    • どうして?通知メッセージが表示された場合、window.Focus()は、ユーザーがその時点で入力しているものからフォーカスを奪います。特にポップアップが非常に頻繁に発生する場合、これはエンドユーザーにとって非常にイライラします。
  • window.Activate()を使用する回答はすべて間違っています。

    • どうして?親ウィンドウも表示されます。
  • window.ShowActivated = falseを省略する回答はすべて間違っています。
    • どうして?メッセージがポップアップするとき、別のウィンドウからフォーカスを奪いますが、これは非常に迷惑です!
  • ウィンドウの非表示/表示にVisibility.Visibleを使用しない回答は間違っています。
    • どうして? Citrixを使用している場合、ウィンドウを閉じたときにウィンドウが折りたたまれないと、画面に奇妙な黒い長方形のホールドが残ります。したがって、window.Show()およびwindow.Hide()は使用できません。

基本的に:

  • ウィンドウは、アクティブになったときに他のウィンドウからフォーカスを奪ってはなりません。
  • ウィンドウは、表示されるときに親をアクティブにしないでください。
  • ウィンドウはCitrixと互換性があるはずです。

MVVMソリューション

このコードは、Citrixと完全に互換性があります(画面の空白部分はありません)。通常のWPFとDevExpressの両方でテストされています。

この回答は、他のウィンドウの前に常に小さな通知ウィンドウが必要なユースケースを対象としています(ユーザーが設定でこれを選択した場合)。

この回答が他の回答よりも複雑に見える場合、それは堅牢なエンタープライズレベルのコードであるためです。このページの他の回答のいくつかは単純ですが、実際には機能しません。

XAML-添付プロパティ

この添付プロパティをウィンドウ内のUserControlに追加します。添付プロパティは:

  • Loadedイベントが発生するまで待機します(それ以外の場合は、ビジュアルツリーを検索して親ウィンドウを見つけることができません)。
  • ウィンドウが表示されるかどうかを確認するイベントハンドラーを追加します。

いつでも、添付プロパティの値を反転することにより、ウィンドウを前面または前面に設定できます。

<UserControl x:Class="..."
         ...
         attachedProperties:EnsureWindowInForeground.EnsureWindowInForeground=
             "{Binding EnsureWindowInForeground, Mode=OneWay}">

C#-ヘルパーメソッド

public static class HideAndShowWindowHelper
{
    /// <summary>
    ///     Intent: Ensure that small notification window is on top of other windows.
    /// </summary>
    /// <param name="window"></param>
    public static void ShiftWindowIntoForeground(Window window)
    {
        try
        {
            // Prevent the window from grabbing focus away from other windows the first time is created.
            window.ShowActivated = false;

            // Do not use .Show() and .Hide() - not compatible with Citrix!
            if (window.Visibility != Visibility.Visible)
            {
                window.Visibility = Visibility.Visible;
            }

            // We can't allow the window to be maximized, as there is no de-maximize button!
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;
            }

            window.Topmost = true;
        }
        catch (Exception)
        {
            // Gulp. Avoids "Cannot set visibility while window is closing".
        }
    }

    /// <summary>
    ///     Intent: Ensure that small notification window can be hidden by other windows.
    /// </summary>
    /// <param name="window"></param>
    public static void ShiftWindowIntoBackground(Window window)
    {
        try
        {
            // Prevent the window from grabbing focus away from other windows the first time is created.
            window.ShowActivated = false;

            // Do not use .Show() and .Hide() - not compatible with Citrix!
            if (window.Visibility != Visibility.Collapsed)
            {
                window.Visibility = Visibility.Collapsed;
            }

            // We can't allow the window to be maximized, as there is no de-maximize button!
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;
            }

            window.Topmost = false;
        }
        catch (Exception)
        {
            // Gulp. Avoids "Cannot set visibility while window is closing".
        }
    }
}

使用法

これを使用するには、ViewModelにウィンドウを作成する必要があります。

private ToastView _toastViewWindow;
private void ShowWindow()
{
    if (_toastViewWindow == null)
    {
        _toastViewWindow = new ToastView();
        _dialogService.Show<ToastView>(this, this, _toastViewWindow, true);
    }
    ShiftWindowOntoScreenHelper.ShiftWindowOntoScreen(_toastViewWindow);
    HideAndShowWindowHelper.ShiftWindowIntoForeground(_toastViewWindow);
}

private void HideWindow()
{
    if (_toastViewWindow != null)
    {
        HideAndShowWindowHelper.ShiftWindowIntoBackground(_toastViewWindow);
    }
}

追加のリンク

通知ウィンドウが常に表示画面に戻るようにするためのヒントについては、私の答えを参照してください: WPFで、画面外にあるウィンドウを画面に移動する方法は?

6
Contango

私はこれが遅い答えであることを知っています

 if (!WindowName.IsVisible)
 {
     WindowName.Show();
     WindowName.Activate();
 }
6
Jamaxack

さて、これは非常にホットなトピックなので...ここに私のために働くものがあります。ウィンドウを表示できない場合、Activate()がエラーを表示するため、このようにしないとエラーが発生します。

Xaml:

<Window .... 
        Topmost="True" 
        .... 
        ContentRendered="mainWindow_ContentRendered"> .... </Window>

分離コード:

private void mainWindow_ContentRendered(object sender, EventArgs e)
{
    this.Topmost = false;
    this.Activate();
    _UsernameTextBox.Focus();
}

これが、ウィンドウを一番上に表示するための唯一の方法でした。次に、マウスでフォーカスを設定せずにボックスに入力できるようにアクティブにします。 control.Focus()は、ウィンドウがActive()でない限り機能しません。

4
Omzig

現在開いているウィンドウを表示するには、これらのDLLをインポートします。

public partial class Form1 : Form
{
    [DllImportAttribute("User32.dll")]
    private static extern int FindWindow(String ClassName, String WindowName);
    [DllImportAttribute("User32.dll")]
    private static extern int SetForegroundWindow(int hWnd);

そして、プログラムで指定されたタイトルのアプリを検索します(最初の文字なしでタイトルを書きます(インデックス> 0))

  foreach (Process proc in Process.GetProcesses())
                {
                    tx = proc.MainWindowTitle.ToString();
                    if (tx.IndexOf("Title of Your app WITHOUT FIRST LETTER") > 0)
                    {
                        tx = proc.MainWindowTitle;
                        hWnd = proc.Handle.ToInt32(); break;
                    }
                }
                hWnd = FindWindow(null, tx);
                if (hWnd > 0)
                {
                    SetForegroundWindow(hWnd);
                }
2
rahmud

問題は、フックからコードを呼び出すスレッドがランタイムによって初期化されていないため、ランタイムメソッドの呼び出しが機能しないことです。

おそらく、Invokeを実行して、コードをUIスレッドにマーシャリングして、ウィンドウを前面に表示するコードを呼び出すことができます。

1
joshperry

これらのコードは常に正常に機能します。

最初に、XAMLでアクティブ化されたイベントハンドラーを設定します。

Activated="Window_Activated"

メインウィンドウのコンストラクタブロックに次の行を追加します。

public MainWindow()
{
    InitializeComponent();
    this.LocationChanged += (sender, e) => this.Window_Activated(sender, e);
}

そして、アクティブ化されたイベントハンドラー内で次のコードをコピーします。

private void Window_Activated(object sender, EventArgs e)
{
    if (Application.Current.Windows.Count > 1)
    {
        foreach (Window win in Application.Current.Windows)
            try
            {
                if (!win.Equals(this))
                {
                    if (!win.IsVisible)
                    {
                        win.ShowDialog();
                    }

                    if (win.WindowState == WindowState.Minimized)
                    {
                        win.WindowState = WindowState.Normal;
                    }

                    win.Activate();
                    win.Topmost = true;
                    win.Topmost = false;
                    win.Focus();
                }
            }
            catch { }
    }
    else
        this.Focus();
}

これらの手順は正常に機能し、他のすべてのウィンドウを親ウィンドウに前面に表示します。

1
Matrix

たとえば、ウィンドウを最小化するなど、ウィンドウを非表示にしようとしている場合、

    this.Hide();

それを正しく非表示にし、次に単に使用します

    this.Show();

次に、ウィンドウを一番上のアイテムとしてもう一度表示します。

0
Chris

この質問に別のソリューションを追加したかっただけです。この実装は、CaliBurnがメインウィンドウの表示を担当する私のシナリオで機能します。

protected override void OnStartup(object sender, StartupEventArgs e)
{
    DisplayRootViewFor<IMainWindowViewModel>();

    Application.MainWindow.Topmost = true;
    Application.MainWindow.Activate();
    Application.MainWindow.Activated += OnMainWindowActivated;
}

private static void OnMainWindowActivated(object sender, EventArgs e)
{
    var window = sender as Window;
    if (window != null)
    {
        window.Activated -= OnMainWindowActivated;
        window.Topmost = false;
        window.Focus();
    }
}
0
d.moncada

アクティブなウィンドウはイベントを処理したウィンドウに戻るため、PreviewMouseDoubleClickハンドラー内にそのウィンドウを表示するコードを配置しないでください。 MouseDoubleClickイベントハンドラーに配置するか、e.HandledをTrueに設定してバブリングを停止します。

私の場合、私はListviewでPreviewMouseDoubleClickを処理していましたが、e.Handled = trueを設定していなかったため、元のウィンドウにフォーカスを戻すMouseDoubleClickイベントが発生しました。

0
Michel P.

簡単に再利用できるように拡張メソッドを作成しました。

using System.Windows.Forms;
    namespace YourNamespace{
        public static class WindowsFormExtensions {
            public static void PutOnTop(this Form form) {
                form.Show();
                form.Activate();
            }// END PutOnTop()       
        }// END class
    }// END namespace

フォームコンストラクターを呼び出す

namespace YourNamespace{
       public partial class FormName : Form {
       public FormName(){
            this.PutOnTop();
            InitalizeComponents();
        }// END Constructor
    } // END Form            
}// END namespace
0
Mike