Reputation: 567
I have a WPF C# desktop application that's installed on Windows 10 PC's. The app is used by customers at the front desk reception, where they complete a specific set of tasks. In the last step the app launches a URL using the 'default' Web browser so they can complete the final step. This last step has to be done on the Web.
Once the customer completes the final task in the browser Window (usually takes about 15 seconds), I need the Windows app to once again become the active/focused Window. Otherwise, when the next customer comes to the counter, they see the browser window. But they need to see the WPF app.
In my code I create a DispatcherTimer with a 30 second timer that gets started just before the app launches the URL:
DispatcherTimer focus_timer = new DispatcherTimer();
focus_timer.Interval = TimeSpan.FromSeconds(30);
focus_timer.Tick += new EventHandler(focus_timer_Tick);
Code in the timer tick event:
focus_timer.Stop();
this.Activate();
this.Focus();
The above code had no effect - Chrome still sits in the foreground over the top of the WPF Windows app. I then tried using the SetForegroundWindow function from the user32.dll with the following unmanaged code:
// code in the Window class
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool SetForegroundWindow(IntPtr hWnd);
// code in the timer tick event
focus_timer.Stop();
IntPtr windowHandle = new WindowInteropHelper(this).Handle;
bool is_ok = SetForegroundWindow(windowHandle);
This also didn't work. The above code simply causes the WPF app's icon on the Windows Taskbar to pulse, and again Chrome stays on top. I also tried the following code:
// code in the Window class
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern IntPtr SetActiveWindow(IntPtr hWnd);
// code in the timer tick event
IntPtr windowHandle = new WindowInteropHelper(this).Handle;
IntPtr ptrActive = SetActiveWindow(windowHandle);
IntPtr ptrFocus = SetFocus(windowHandle);
The above code also doesn't appear to do anything.
ptrActive returns: 0x00000000
ptrFocus returns: 0x0004086a
Does anyone know how to solve this?
Upvotes: 1
Views: 1187
Reputation: 1307
Try this code, the method You will need to call is TrySetWindowToForeground()
[DllImport("user32.dll")]
public static extern bool AttachThreadInput(uint idAttach,
uint idAttachTo, bool fAttach);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("kernel32.dll")]
public static extern uint GetCurrentThreadId();
public static bool TrySetWindowToForeground() {
try {
var currentProcessHandle = Process.GetCurrentProcess().MainWindowHandle;
ForceWindowToForeground(currentProcessHandle);
return true;
}
catch {
return false;
}
}
public static void ForceWindowToForeground(IntPtr hwnd) {
AttachedThreadInputAction(
() => {
BringWindowToTop(hwnd);
ShowWindow(hwnd, SW_SHOW);
});
}
public static void AttachedThreadInputAction(Action action) {
var foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
var appThread = GetCurrentThreadId();
bool threadsAttached = false;
try {
threadsAttached = foreThread == appThread || AttachThreadInput(foreThread, appThread, true);
if (threadsAttached) {
action();
}
else {
throw new ThreadStateException("AttachThreadInput failed.");
}
}
finally {
if (threadsAttached) {
AttachThreadInput(foreThread, appThread, false);
}
}
}
Upvotes: 1