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
を呼び出すと、期待どおりに動作するため、元のケースで動作しない理由がわかりません。
編集:グローバルホットキーと組み合わせてこれを行っています。
さて、私は回避策を見つけました。ホットキーの実装に使用されるキーボードフックから呼び出しを行っています。一時停止して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();
}
myWindow.Activate();
ウィンドウをフォアグラウンドに移動してアクティブ化しようとします。
私が誤解していて、常に手前の動作が必要な場合を除き、これでうまくいくはずです。その場合、以下が必要です。
myWindow.TopMost = true;
ウィンドウを一番上に表示するソリューションを見つけましたが、通常のウィンドウとして動作します。
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
初めてロードするときにウィンドウを前面に表示する必要がある場合は、次を使用する必要があります。
private void Window_ContentRendered(object sender, EventArgs e)
{
this.Topmost = false;
}
private void Window_Initialized(object sender, EventArgs e)
{
this.Topmost = true;
}
これを簡単にコピー&ペーストするために-
このクラスの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
私はこの質問がかなり古いことを知っていますが、この正確なシナリオに出会ったばかりで、実装したソリューションを共有したかったのです。
このページのコメントで述べたように、提案されたソリューションのいくつかは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
}
}
このコードが、この問題に遭遇した他の人に役立つことを願っています。
ユーザーが別のアプリケーションと対話している場合、自分のアプリケーションを前面に表示できない場合があります。一般的なルールとして、プロセスは、そのプロセスが既にフォアグラウンドプロセスである場合にのみフォアグラウンドウィンドウを設定することを期待できます。 (Microsoftは SetForegroundWindow() MSDNエントリで制限を文書化しています。)これは次の理由によります。
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();
}
window.Focus()
を使用する回答はすべて間違っています。
window.Focus()
は、ユーザーがその時点で入力しているものからフォーカスを奪います。特にポップアップが非常に頻繁に発生する場合、これはエンドユーザーにとって非常にイライラします。window.Activate()
を使用する回答はすべて間違っています。
window.ShowActivated = false
を省略する回答はすべて間違っています。Visibility.Visible
を使用しない回答は間違っています。window.Show()
およびwindow.Hide()
は使用できません。基本的に:
このコードは、Citrixと完全に互換性があります(画面の空白部分はありません)。通常のWPFとDevExpressの両方でテストされています。
この回答は、他のウィンドウの前に常に小さな通知ウィンドウが必要なユースケースを対象としています(ユーザーが設定でこれを選択した場合)。
この回答が他の回答よりも複雑に見える場合、それは堅牢なエンタープライズレベルのコードであるためです。このページの他の回答のいくつかは単純ですが、実際には機能しません。
この添付プロパティをウィンドウ内のUserControl
に追加します。添付プロパティは:
Loaded
イベントが発生するまで待機します(それ以外の場合は、ビジュアルツリーを検索して親ウィンドウを見つけることができません)。いつでも、添付プロパティの値を反転することにより、ウィンドウを前面または前面に設定できます。
<UserControl x:Class="..."
...
attachedProperties:EnsureWindowInForeground.EnsureWindowInForeground=
"{Binding EnsureWindowInForeground, Mode=OneWay}">
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で、画面外にあるウィンドウを画面に移動する方法は? 。
私はこれが遅い答えであることを知っています
if (!WindowName.IsVisible)
{
WindowName.Show();
WindowName.Activate();
}
さて、これは非常にホットなトピックなので...ここに私のために働くものがあります。ウィンドウを表示できない場合、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()でない限り機能しません。
現在開いているウィンドウを表示するには、これらの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);
}
問題は、フックからコードを呼び出すスレッドがランタイムによって初期化されていないため、ランタイムメソッドの呼び出しが機能しないことです。
おそらく、Invokeを実行して、コードをUIスレッドにマーシャリングして、ウィンドウを前面に表示するコードを呼び出すことができます。
これらのコードは常に正常に機能します。
最初に、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();
}
これらの手順は正常に機能し、他のすべてのウィンドウを親ウィンドウに前面に表示します。
たとえば、ウィンドウを最小化するなど、ウィンドウを非表示にしようとしている場合、
this.Hide();
それを正しく非表示にし、次に単に使用します
this.Show();
次に、ウィンドウを一番上のアイテムとしてもう一度表示します。
この質問に別のソリューションを追加したかっただけです。この実装は、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();
}
}
アクティブなウィンドウはイベントを処理したウィンドウに戻るため、PreviewMouseDoubleClickハンドラー内にそのウィンドウを表示するコードを配置しないでください。 MouseDoubleClickイベントハンドラーに配置するか、e.HandledをTrueに設定してバブリングを停止します。
私の場合、私はListviewでPreviewMouseDoubleClickを処理していましたが、e.Handled = trueを設定していなかったため、元のウィンドウにフォーカスを戻すMouseDoubleClickイベントが発生しました。
簡単に再利用できるように拡張メソッドを作成しました。
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