Give focus to WPF application if other instances are running - c#

I want to create a Spotify-like behavior for my WPF application. When the application is running, but minimized in the taskbar and the user tries to start a new instace, it should restore it from the taskbar, and give focus to the already running instance. The problem is stumble upon is to open to application from minimized state in the taskbar to be in focus. It works if the application is open, but other windows are placed over it.
var processHndl = FindWindow(null, "MyApplicationName!");
if (processHndl != IntPtr.Zero)
{
var isIconic = IsIconic(processHndl);
MessageBox.Show("Iconic: " + isIconic);
if (IsIconic(processHndl))
{
ShowWindow(processHndl, 9);
}
SetForegroundWindow(processHndl);
}
Any ideas?

Please try
SwitchToThisWindow
Windows Function Reference.

Related

How can I force a redraw of a process whoʼs parent is a panel-control?

I've been tasked with creating an application launcher that hosts both Winforms and WPF applications and via a slightly different methodology Web applications. The user is presented with a list of applications they can launch and these are "captured" upon launch and are lodged inside a panel on a form by using SetParent to make the panel the parent of the processes MainWindowHandle. This bit appears to work well and the applications when launched are captured and displayed in the given panel.
What Iʼm having a particular issue with is that not all applications that are captured are happy to initially draw themselves in the panel. It seems to be isolated to those applications that are WPF based, but thatʼs not guaranteed.
Effectively what happens is that if a WPF application is launched, it is captured and moved to the panel, and the panel will remain blank until I click the panel at which point the application will happily repaint itself. From this point forward the application seems to be happy enough repainting itself as required without intervention.
Basically Iʼm now at my wits ends and have tried the following User32 native and .NET methods;
Invalidate (on the form, on the panel, on the tab control that hosts both of these)
Update
Refresh
SendMessage with a plethora of parameters, including attempting the, "you shouldnʼt do this" WM_PAINT.
RedrawWindow with the UpdateNow and Invalidate flags.
None of the above makes any visible difference and itʼs only when I physically click the panel or move the window that the contained application will behave and repaint itself.
Has anyone else produced anything similar and has a solution to the redraw/repaint issue? Iʼve scoured the entire Google/Bing/Duck Duck Go spheres trying to hunt down an answer but to no avail.
Hopefully one of you out there has an answer.
The following code represents the bulk of the feature in that it starts a process and captures the handle for the main window of the process and sets itsʼ parent to a panel control on a bog-standard WinForms window. I should probably point out that the bog-standard WinForms window is itself “hosted” inside an application using the (EasyTabs) Library. Which, I believe, is not causing any problems.
Example:
// Try to acquire a the process.
ProcessStartInfo startInfo = new ProcessStartInfo(path);
try
{
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.WindowStyle = ProcessWindowStyle.Normal;
Process.StartInfo = startInfo;
Process.EnableRaisingEvents = true;
Process.Exited += TabManagerContainerForm_ProcessExited;
Process.Start();
if (Process != null)
{
// Wait until the process has created a main window or exited.
while (Process.MainWindowHandle == IntPtr.Zero && !Process.HasExited)
{
Thread.Sleep(100);
Process.Refresh();
}
if (!Process.HasExited) // We have acquired a MainWindowHandle
{
// Capture the Process's main window and show it inside the applicationPanel panel control.
SetParent(Process.MainWindowHandle, applicationPanel.Handle);
// Change the captured Process's window to one without the standard chrome. Itʼs provided by our tabbed application.
SetWindowLong(Process.MainWindowHandle, (int)WindowLongFlags.GWL_STYLE, (int)WindowStyles.WS_VISIBLE);
}
else // Process has exited.
{
if (Process.MainWindowHandle == IntPtr.Zero) Log.Information("{0} failed to execute.", Process.ProcessName);
throw new FailedProcessException(string.Format("{0} failed to execute.", path));
}
}
else
{
throw new Exception(string.Format("Invalid path: {0}", path));
}
}
catch (Exception ex) when (!(ex is FailedProcessException)) // Catch everything but FailedProcessExceptions. FPEs are simply passed up the chain.
{
Log.Error(ex.Message);
throw;
}
Summary:
The problem seems to happen when attaching an application to a Panel in a tab that is not the currently visible tab.
Solution:
Add WS_CHILD flag to the call to SetWindowLong:
SetWindowLong(Process.MainWindowHandle, (int)WindowLongFlags.GWL_STYLE, (int)(WindowStyles.WS_VISIBLE | WindowStyles.WS_CHILD));
Details:
I tried reproducing the example in the question (without using EasyTabs). I used a simple Form with a single Panel. With a button press I call a simple WPF app and attach it to the Panel. It works OK, it renders immediately. The only problem I found was the position of the WPF window which was random. I fixed it with calling SetWindowPos (using pinvoke) like this:
SetWindowPos(Process.MainWindowHandle, IntPtr.Zero, 0, 0, 0, 0, SetWindowPosFlags.IgnoreResize);
Than, I tried using a TabControl with two tabs, each containing a Panel and two buttons, each one when pressed, launches a different WPF app to one of the Panels. I found that when a tab (TabPage) is not the visible tab, the problem occurs - the launched application is not visible until the Panel is clicked. I solved this problem by adding WS_CHILD flag to the call to SetWindowLong. I'm not sure why but it works...
My code:
// Capture the Process's main window and show it inside the applicationPanel panel control.
SetParent(Process.MainWindowHandle, applicationPanel.Handle);
// Change the captured Process's window to one without the standard chrome. Itʼs provided by our tabbed application.
SetWindowLong(Process.MainWindowHandle, (int)WindowLongFlags.GWL_STYLE, (int)(WindowStyles.WS_VISIBLE | WindowStyles.WS_CHILD));
// Change the Process's window position to the top-left corner of the panel
SetWindowPos(Process.MainWindowHandle, IntPtr.Zero, 0, 0, 0, 0, SetWindowPosFlags.IgnoreResize);

Start another application on top most in tablet mode

When I run another .exe from my application it starts in the background and does not show the application on top of the screen instead shows tablet mode home screen , it's working fine in normal desktop mode but when I run it in Windows 10 Tablet mode then it does not show on top it starts in the background.
I've used myWindow.TopMost = true; , but it does not work as intended in Windows 10 Tablet Mode.
Code used to start exe file
Process p = new Process();
p.StartInfo.RedirectStandardOutput= true;
p.RedirectStandardInput = true;
p = Process.Start("myApp.exe");
p.WaitForExit();
the exe I'm invoking(starting) is my own exe application(it's not system app), I'm running app on windows 10.
It's only not working on top in Tablet mode( and I'm targeting my application only for Tablets).
Any help is appreciated..!
As I faced a similar situation, (it is not tablet, or windows-10 related. Has similarities only by WPF and TopMost tags) I'll show you how I resolve it:
I would like to have the FilterWindow always TopMost (but only over my application, not over entire set of apps in my Operating System)
See my code. May it'll helps you.
private void OnFilter() {
var filterViewModel = ViewModelLocator.FilterViewModel;
/* ... */
var filterWindow = new FilterWindow {
DataContext = filterViewModel,
Owner = GetParentWindow()
};
filterWindow.ShowDialog();
SelectedIndex = 0;
}
private static Window GetParentWindow() {
Window parent = null;
var activeWindows = Application.Current.Windows.Cast<Window>().Where(item => (item).IsActive).ToList();
if (activeWindows.Any()) {
parent = activeWindows[activeWindows.Count - 1];
}
else {
foreach (var item in
Application.Current.Windows.Cast<object>().Where(item => item.GetType().Name == typeof(RibbonWindow).Name)) {
parent = item as Window;
}
}
return parent;
}
The magic is Owner = GetParentWindow().
Without setting the Owner the FilterWindow had a ridiculous behavior.
Hope it helps you. If no, I will remove the response. (it does not fit in a comment)
Moerfi's solution of using Owner = GetParentWindow() worked surperb, much thanks for this solution. It also solved another problem I had.
I am writing an application for Surface 3 that runs on Windows 10 Pro on tablet mode, whenever a MessageBox or custom dialog control box is closed, as opposed to going back to parent window, Win 10 goes to start menu.
As if once the dialog control is opened the parent window is being placed into background, so when dialog control is closed there is no active window for Win 10 to switch back.
Setting owner on child dialog control solved that problem. Thank you very much.

Unit Test WPF Control Get Access to Child Dialog

We are using NUnit to test a WPF control.
The text fixture basically opens a test window containing the control to be tested on a new thread. Then the Microsoft UI Automation (UIA) is used to interact with the control.
The new thread shows the window and starts the dispatcher. Things work as expected.
The issue we are running into is that this control can launch a dialog. Once the dialog is launched we need to interact with it and close it. I have been unable to get a reference to this dialog to accomplish this task.
One solution that does not work is to use Application.Current.Windows to get all the windows and then iterate through them until the dialog is found. This does not work because during unit testing Application.Current = null. Now if we only care about this test we can just instantiate an Application. This will however interfere with other tests, because the Application will automatically enter shutting down mode when our Application variable goes out of scope (at the end of the test). As a result other tests will fail (most notably because InitializeComponent typically calls System.Windows.Application.LoadComponent which can't be called during shutting down mode).
I suppose what we need is an alternative to Application.Current.Windows.
I found a working solution to my problem.
UIA fires a number of events. One of them indicates that a new window has opened.
Subscribe a handler to the WindowOpenedEvent:
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, new AutomationEventHandler(NewWindowHandler));
public void NewWindowHandler(Object sender, AutomationEventArgs e)
{
AutomationElement element = (AutomationElement)sender;
if (element.Current.Name == "PUT YOUR NAME HERE")
{
HwndSource hSource = HwndSource.FromHwnd(new IntPtr(element.Current.NativeWindowHandle));
MyWindow = hSource.RootVisual as WavefrontToolkit.FormulaEditor.FormulaEditor;
Assert.IsNotNull(_MyWindow );
}
}
}
In the handler you do not have a reference to the window that was opened. You can however get it from the Win32 handle.
The other issue I encountered is that the test will continue on as the Window is opening. Some of the test might be depended on that window. To deal with that I cause a delay until the Window is ready.
while (MyWindow == null)
{
System.Threading.Thread.Sleep(10);
}

c# remove 3rd party application from taskbar

How to remove an 3rd party application from the Windows taskbar by its handle?
I've found this:
Remove application from taskbar with C# wrapper?
But it doesnt worked for me.
It only sets another style (small x to close, no maximize/minimize button) to the Window i selected (notepad).
Any ideas about this?
EDIT: I dont want to remove MY application from the taskbar, i want to remove an external application by handle
If you have the handle to the window you can call ShowWindow() through the Win32 API. Then you can do:
// Let the window disappear (even from taskbar)
ShowWindow(this.Handle, WindowShowStyle.Hide);
// Revive the window back to the user
ShowWindow(this.Handle, WindowShowStyle.ShowNoActivate);
So from now, all your problem is to get the handle of the window you like to hide:
Process[] procs = Process.GetProcesses();
IntPtr hWnd;
foreach(Process proc in procs)
{
if ((hWnd = proc.MainWindowHandle) != IntPtr.Zero)
{
Console.WriteLine("{0} : {1}", proc.ProcessName, hWnd);
}
}
To hide it from windows task bar you just need to set ShowInTaskbar property to false :
this.ShowInTaskbar = false;
As for moving of windows you can use spy++ to check windows events and identify it.
How to remove an application from the Windows taskbar?
this.ShowInTaskbar = false;
Easy:
this.ShowInTaskbar = false;
As for the Form movement: you can use the Move event under Layout events

How do i set this window to topmost?

How do i set my window above all other? I need a bad but noticeable msg box that closes on its own.
Msg is a dummy form which is empty. All i want is its title.
The problems with the code is the window isnt created 0,0 (its just whereever windows feels like putting it). The width is correct but i notice if i click firefox or another app window my app doesnt pop up. I know it is being shown bc i can see it in the taskbar at the bottom for a brief second. So the bugs so far
Doesnt go topmost if i click another app
Isnt 0,0
How do i fix this?
{
var msg = new Msg();
msg.Text = (has ? "*" : "+") + args[0];
msg.TopMost = true;
msg.Width = 2000;
msg.Top = 0;
msg.Left = 0;
msg.Show();
System.Threading.Thread.Sleep(1000);
msg.Close();
}
Sounds like TopMost doesn't always do it; here's an answer to a similar question showing how to hook into Win32 for the call: Form top most?
Update: just read the rest of the answer; it might only fail running in Debug mode within Visual Studio (where your app is actually executed with vshost.exe, rather than running independently).

Categories