HwndHost for Windows Form - Win32 / WinForm Interoperability - c#

I need to host a Win32 window in a Windows Form control. I had the same problem with WPF and I solved this problem by using the HwndHost control.
I followed this tutorial:
Walkthrough: Hosting a Win32 Control in WPF
Is there any equivalent control in Windows Form?
I have a Panel and its Handle property, I use this handle as parent of my Direct2D render target window:
// Register the window class.
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
// Redraws the entire window if a movement or size adjustment changes the height
// or the width of the client area.
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = Core::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof(LONG_PTR);
wcex.hInstance = HINST_THISCOMPONENT;
wcex.hbrBackground = nullptr;
wcex.lpszMenuName = nullptr;
wcex.hCursor = LoadCursor(nullptr, IDI_APPLICATION);
wcex.lpszClassName = L"SVGCoreClassName";
RegisterClassEx(&wcex);
hwnd = CreateWindow(
L"SVGCoreClassName", // class name
L"", // window name
WS_CHILD | WS_VISIBLE, // style
CW_USEDEFAULT, // x
CW_USEDEFAULT, // y
CW_USEDEFAULT, // width
CW_USEDEFAULT, // height
parent, // parent window
nullptr, // window menu
HINST_THISCOMPONENT, // instance of the module to be associated with the window
this); // pointer passed to the WM_CREATE message
...
hr = d2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(hwnd, size, D2D1_PRESENT_OPTIONS_IMMEDIATELY),
&renderTarget);
The code works if I use the HwndHost parent handle with WPF. But it does not render anything if I use the System.Windows.Forms.Panel Handle.

What you had to do in WPF to create a valid D2D1 target window is not necessary in Winforms. It was necessary in WPF because controls are not windows themselves and don't have a Handle property, the one that CreateHwndRenderTarget() needs.
In Winforms the Panel class is already a perfectly good render target, you can use its Handle property to tell D2D1 where to render. You just need to tell it to stop painting itself. Add a new class to your project and paste this code:
using System;
using System.Windows.Forms;
class D2D1RenderTarget : Control {
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
if (!this.DesignMode) {
this.SetStyle(ControlStyles.UserPaint, false);
// Initialize D2D1 here, use this.Handle
//...
}
}
}
Compile. Drop the new control onto your form, replacing the existing panel.

Sounds like an MDI application. Something like this?
[DllImport("user32.dll")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
Form f = new Form();
Button b1 = new Button { Text = "Notepad" };
b1.Click += delegate {
using (var p = Process.Start("notepad.exe")) {
p.WaitForInputIdle();
SetParent(p.MainWindowHandle, f.Handle);
MoveWindow(p.MainWindowHandle, 50, 50, 300, 300, true);
}
};
f.Controls.Add(b1);
Application.Run(f);

Related

Cannot force my window to be on top

I need my window to be on top of another window. That "other" window (application) is from different developer. I do not have source codes for it. I can only use Spy++ to get information about it.
I am using Windows 7.
I tryed several things but they did not work.
This is what I tryed so far:
1) Timer + BringWindowToTop
2) I changed Owner of my window
IntPtr handle = User32.FindWindow("Vega Prime", "Vega Prime");
NativeWindow win = new NativeWindow();
win.AssignHandle(handle);
ChildForm form = new ChildForm();
form.Show(win);
When I am saying that it does not work I mean this:
1) at first everything looks alright: my window is on top
2) then I click on window (Vega Prime) which is below mine
3) my window disappears
4) I click on place where my window should be and it reappears (!!!!!!)
What is that? How is it possible at all?
UPDATE:
I spent some time trying to find solution to my problem.
Here is what I found:
TopMost window going behind non-TopMost fullscreen window sometimes
https://social.msdn.microsoft.com/Forums/vstudio/en-US/92e66584-6cb8-4976-9531-eab3b9a129e3/mfc-window-with-wsextopmost-hidden-by-full-screen-window?forum=vcgeneral
I am pretty sure that my problem has something to do with "Full Screen Issue" at Windows 7 (sometimes, when not top most window becomes full screen it forces top most windows to become hidden). That explains described above weird behaviour, right?
I realized this example that seems to do the trick:
public partial class Form1 : Form
{
IntPtr hWndToStayOver = User32.FindWindow("Vega Prime", "Vega Prime");
private System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
this.Load += new EventHandler(this.Form1_Load);
this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Start(); // Routine starts now that I'm sure my Form exists
}
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
timer1.Stop(); // Stop routine (my Form doesn't exist anymore)
}
private bool AmIAboveOtherWindow()
{
IntPtr tmpHwnd = User32.GetNextWindow(hWndToStayOver, User32.GetNextWindowCmd.GW_HWNDPREV);
while (tmpHwnd != (IntPtr)0)
{
if (tmpHwnd == this.Handle)
return true;
tmpHwnd = User32.GetNextWindow(tmpHwnd, User32.GetNextWindowCmd.GW_HWNDPREV);
}
return false;
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
if (!AmIAboveOtherWindow()) // Check if I am behind the target window
{
User32.SetWindowPos(this.Handle, hWndToStayOver, 0, 0, 0, 0, // Move my Form behind the target
User32.SetWindowPosFlags.SWP_NOMOVE |
User32.SetWindowPosFlags.SWP_NOSIZE |
User32.SetWindowPosFlags.SWP_SHOWWINDOW |
User32.SetWindowPosFlags.SWP_NOACTIVATE |
User32.SetWindowPosFlags.SWP_ASYNCWINDOWPOS);
User32.SetWindowPos(hWndToStayOver, this.Handle, 0, 0, 0, 0, // Move target behind my Form
User32.SetWindowPosFlags.SWP_NOMOVE |
User32.SetWindowPosFlags.SWP_NOSIZE |
User32.SetWindowPosFlags.SWP_SHOWWINDOW |
User32.SetWindowPosFlags.SWP_NOACTIVATE |
User32.SetWindowPosFlags.SWP_ASYNCWINDOWPOS);
}
timer1.Start();
}
}
User32 class
public class User32
{
[Flags]
public enum SetWindowPosFlags : uint
{
SWP_ASYNCWINDOWPOS = 0x4000,
SWP_DEFERERASE = 0x2000,
SWP_DRAWFRAME = 0x0020,
SWP_FRAMECHANGED = 0x0020,
SWP_HIDEWINDOW = 0x0080,
SWP_NOACTIVATE = 0x0010,
SWP_NOCOPYBITS = 0x0100,
SWP_NOMOVE = 0x0002,
SWP_NOOWNERZORDER = 0x0200,
SWP_NOREDRAW = 0x0008,
SWP_NOREPOSITION = 0x0200,
SWP_NOSENDCHANGING = 0x0400,
SWP_NOSIZE = 0x0001,
SWP_NOZORDER = 0x0004,
SWP_SHOWWINDOW = 0x0040,
}
public enum GetNextWindowCmd : uint
{
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
}
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindow")]
public static extern IntPtr GetNextWindow(IntPtr hWnd, GetNextWindowCmd uCmd);
}
Clearly, you have to add to this code some check on the target handle: this example works only if the target window exists when the Form is loaded and if it isn't closed until the end
Make sure you set the owner of your window to the other window.
That will make sure that your window is always on top of the owner.
Here's how to import the appr. native functions, just change the 'Get' to 'Set' everywhere.
Then you can invoke it like this:
SetWindowLong(handle_of_owned_window, -8, handle_of_owner_window);
Hint #1: it's easy to set the ownership b/w 2 Form instances via the Form.Owner property, however you don't have access to one of them Forms.
Hint #2: in order to access the handle of a window, it needs to be shown at least once first.
It turned out that "other" application (which I was trying to merge with my application) is using Full-Screen Exclusive Mode. That explains why my topmost window disappeared from view and never reappeared (unless I switch off fullscreen mode with mouse click).
Details about Full-Screen Exclusive Mode are here:
https://docs.oracle.com/javase/tutorial/extra/fullscreen/exclusivemode.html
Basic idea is that Full-Screen Exclusive Mode "allows the programmer to suspend the windowing system so that drawing can be done directly to the screen". I believe that means that during Full-Screen Exclusive Mode (some experts call it "real full screen") OS ignores different windows (to save resources).
The only solution in situation like that is to configure "other" application to disable full screen mode.
It helped in my case - I studied documentation and found a place in configuration file where to set full screen mode to false.

Why can't I embed these Applications in my Form?

Intention
Using the following code, I managed to load some applications in my windows form.
Code
What this function does is...
stating a process
embedding the process into a panel of my form
maximizing the embedded process
adding a resize event handler to the panel to update the size of the embedded process on panel resize
adding a closed event handler to the form to terminate the embedded process on form close
Usings
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
Constants
const int GWL_STYLE = -16;
const long WS_VISIBLE = 0x10000000,
WS_MAXIMIZE = 0x01000000,
WS_BORDER = 0x00800000,
WS_CHILD = 0x40000000;
Function
IntPtr LoadExtern(Control Panel, string Path)
{
try
{
Process Process = Process.Start(Path);
Process.WaitForInputIdle();
IntPtr Handle = Process.MainWindowHandle;
SetParent(Handle, Panel.Handle);
SetWindowLong(Handle, GWL_STYLE, (int)(WS_VISIBLE+(WS_MAXIMIZE|WS_BORDER)));
MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true);
Panel.Resize += new EventHandler(
delegate(object sender, EventArgs e)
{
MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true);
}
);
this.FormClosed += new FormClosedEventHandler(
delegate(object sender, FormClosedEventArgs e) {
SendMessage(Handle, 83, 0, 0);
Thread.Sleep(1000);
Handle = IntPtr.Zero;
}
);
return Handle;
}
catch (Exception e) { MessageBox.Show(this, e.Message, "Error"); }
return new IntPtr();
}
DLL Imports
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
static extern bool MoveWindow(IntPtr Handle, int x, int y, int w, int h, bool repaint);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr Handle, int Msg, int wParam, int lParam);
Result
This code works nice with some applications, like the windows notepad. Notepad is started and included in the panel of my form. There is no caption and the are no borders, as it should be.
LoadExtern(panel1, "notepad.exe");
After closing the form the embedded process gets terminated like expected.
Problem
Unfortunately my code doesn't work for some other (bigger) applications like firefox or sublimetext.
LoadExtern(panel2, #"C:\Program Files (x86)\Mozilla Firefox\firefox.exe");
What happens is that my form starts and firefox starts, but in its own window. Could you help me to include sublimetext or firefox in my applications?
Part of the solution
Thanks to Sheng Jiang's answers, I got it working for some more applications. What I did is to wait for a main window handle.
Process.WaitForInputIdle();
IntPtr Handle = new IntPtr();
for (int i = 0; Handle == IntPtr.Zero && i < 300; i++)
{
Handle = Process.MainWindowHandle;
Thread.Sleep(10);
}
But I still can't embed applications like the windows explorer.
Your code worked nice by coincidence.
WaitForInputIdle wouldn't necessary wait for the UI thread. For example an input method or a hook created by some other program may create a simple thread that becomes idle while the UI thread is still busy doing initialization.
MainWindowHandle searches for the first visible top level window. It won't return the logical main window when
The main window is not the first visible window created (e.g. a login dialog is created first)
The main window is not created with the visible style (think about a program that has only an icon in the notification area on the system tray)
There is no main window created at all (e.g. some applications open new documents/urls in an existing instance, like browsers and Windows Explorer)
There isn't a main window but multiple top level windows that have equal status. Think about IE6/Outlook/Word.
Even if the main window is created visibly and in fact is the first visible window in the new process, you may still have issues.
From the documentation of SetParent:
An application can use the SetParent function to set the parent window of a pop-up, overlapped, or child window.
It does not say you can reparent a top level window. In fact the top level window offers a lot of services that the program may be relying on, such as
Act as the measuring tool to determine if a full screen request is complete (conflicts with your requirement that new program needs to appear inside your panel)
Getting notified when a new DDE conversation starts, when the active window/program changes, when new hardware arrives, when system setting changes, when the user is logging off, when Windows Explorer is started, when the user pressed the Enter key on a nested dialog, etc. The list of window messages that only sent to top level windows is too long to list here,
Act as the default owner window of modal dialogs if the program choose to (and if you display modal dialog in your program as well, watch out for crashes)
This code works for most applcations. I embedded the file explorer simply using a webbrowser control on my form and set its url to a file location. The internet explorer control magically turns into a file explorer then.
This is my final code, feel free to use this for you own projects.
IntPtr EmbedProcess(Control Panel, string Path)
{
string Name = NameFromPath(Path);
foreach (Process Task in Process.GetProcesses())
{
if (NameFromPath(Task.ProcessName).Contains(Name))
{
try { Task.Kill(); }
catch (Exception e) { }
}
}
try
{
Process Task = Process.Start(Path);
Task.WaitForInputIdle();
IntPtr Handle = new IntPtr();
for (int i = 0; Handle == IntPtr.Zero && i < 10; i++) { Handle = Task.MainWindowHandle; Thread.Sleep(100); }
SetParent(Handle, Panel.Handle);
SetWindowLong(Handle, GWL_STYLE, (int)(WS_VISIBLE + (WS_MAXIMIZE | WS_BORDER)));
MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true);
Panel.Resize += new EventHandler(delegate(object sender, EventArgs e) { MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true); });
this.FormClosed += new FormClosedEventHandler(delegate(object sender, FormClosedEventArgs e)
{
SendMessage(Handle, 83, 0, 0);
Thread.Sleep(100);
Handle = IntPtr.Zero;
});
return Handle;
}
catch (Exception e) { MessageBox.Show(this, e.Message, "Error"); }
return new IntPtr();
}
I somebody is interested in the hole C# classes for embedding window processes and console processes into your form, check out this github repository.

Attach window to window of another process

My WPF application has more than one window, I want to attach some of these windows to a window of another process. My problem is that once I attach my window it becomes invisible.
I'm trying this with the following code:
public static bool setParentWindow(IntPtr hWndChild, IntPtr hWndNewParent)
{
IntPtr previousParent = SetParent(hWndChild, hWndNewParent);
return (previousParent == null ? false : true);
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
setParentWindow(myWindowHwnd, newParentHwnd);
So, the above code successfully attaches the window, but unfortunately makes it invisible.
My reason for doing this is that I'm trying to extend an application by building "Widgets" for it, my widgets will hook in and show the user extra information.
Both windows have the following styles: WS_OVERLAPPEDWINDOW, WS_OVERLAPPED, WS_VISIBLE, WS_CLIPSIBLINGS, WS_CLIPCHILDREN.
I found that I could do this without even using the setParent call. I used HwndSource class as follows:
MyWindow window = new MyWindow();
window.ShowActivated = true;
HwndSourceParameters parameters = new HwndSourceParameters();
parameters.WindowStyle = 0x10000000 | 0x40000000;
parameters.SetPosition(0, 0);
parameters.SetSize((int)window.Width, (int)window.Height);
parameters.ParentWindow = newParent;
parameters.UsesPerPixelOpacity = true;
HwndSource src = new HwndSource(parameters);
src.CompositionTarget.BackgroundColor = Colors.Transparent;
src.RootVisual = (Visual)window.Content;
This is working great now without any problems.
I'm not sure what you need to do with overlapped windows, but from MSDN:
For compatibility reasons, SetParent does not modify the WS_CHILD or WS_POPUP window styles of the window whose parent is being changed. Therefore, if hWndNewParent is NULL, you should also clear the WS_CHILD bit and set the WS_POPUP style after calling SetParent. Conversely, if hWndNewParent is not NULL and the window was previously a child of the desktop, you should clear the WS_POPUP style and set the WS_CHILD style before calling SetParent.
private void TryToAttach(IntPtr ownerHandle)
{
if(ownerHandle == IntPtr.Zero)
{
return;
}
try
{
var helper = new WindowInteropHelper(window) { Owner = ownerHandle };
}
catch(Exception e)
{
Logger.Error(e, "Could not attach window.");
}
}

Docking Window inside another Window

I have a winform application (.NET 2.0 C#). From this application, I want to start another process (another winform application) and dock it to my window (or at least make it look like it is docked). So far, I can only find information about docking controls, not windows in separate processes. My first thought is to get the handle of the window and use unmanaged system calls to set the height/width and position of the window to my docking area. But before I got started, I wanted to check to see if any of you good people have done something similar. I have access to the source code of the application I want docked but would rather not make any changes if I can avoid it. I have complete programming control over what will be the parent application. Any advice? Thanks in advance!
The solution I have used before is to set the application window as a child of the control you want to dock it in.
using System.Diagnostics;
using System.Runtime.InteropServices;
private Process pDocked;
private IntPtr hWndOriginalParent;
private IntPtr hWndDocked;
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
private void dockIt()
{
if (hWndDocked != IntPtr.Zero) //don't do anything if there's already a window docked.
return;
hWndParent = IntPtr.Zero;
pDocked = Process.Start(#"notepad");
while (hWndDocked == IntPtr.Zero)
{
pDocked.WaitForInputIdle(1000); //wait for the window to be ready for input;
pDocked.Refresh(); //update process info
if (pDocked.HasExited)
{
return; //abort if the process finished before we got a handle.
}
hWndDocked = pDocked.MainWindowHandle; //cache the window handle
}
//Windows API call to change the parent of the target window.
//It returns the hWnd of the window's parent prior to this call.
hWndOriginalParent = SetParent(hWndDocked, Panel1.Handle);
//Wire up the event to keep the window sized to match the control
Panel1.SizeChanged += new EventHandler(Panel1_Resize);
//Perform an initial call to set the size.
Panel1_Resize(new Object(), new EventArgs());
}
private void undockIt()
{
//Restores the application to it's original parent.
SetParent(hWndDocked, hWndOriginalParent);
}
private void Panel1_Resize(object sender, EventArgs e)
{
//Change the docked windows size to match its parent's size.
MoveWindow(hWndDocked, 0, 0, Panel1.Width, Panel1.Height, true);
}
* Adding some solution in Answer..**
This code has helped me to dock some executable in windows form. like NotePad, Excel, word, Acrobat reader n many more...
But it wont work for some applications.
As sometimes when you start process of some application.... wait for idle time... and the try to get its mainWindowHandle.... till the time the main window handle becomes null.....
so I have done one trick to solve this
If you get main window handle as null... then search all the runnning processes on sytem and find you process ... then get the main hadle of the process and the set panel as its parent.
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "xxxxxxxxxxxx.exe";
info.Arguments = "yyyyyyyyyy";
info.UseShellExecute = true;
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Maximized;
info.RedirectStandardInput = false;
info.RedirectStandardOutput = false;
info.RedirectStandardError = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForInputIdle();
Thread.Sleep(3000);
Process[] p1 ;
if(p.MainWindowHandle == null)
{
List<String> arrString = new List<String>();
foreach (Process p1 in Process.GetProcesses())
{
// Console.WriteLine(p1.MainWindowHandle);
arrString.Add(Convert.ToString(p1.ProcessName));
}
p1 = Process.GetProcessesByName("xxxxxxxxxxxx");
//p.WaitForInputIdle();
Thread.Sleep(5000);
SetParent(p1[0].MainWindowHandle, this.panel2.Handle);
}
else
{
SetParent(p.MainWindowHandle, this.panel2.Handle);
}
This is a lot clunkier than I hoped for, but working so far. I am using system calls to force the child window in the location that reflects the docking area. It is not working perfectly yet. I get a few oddities caused by the HWND_TOPMOST and I still need to add logic preventing the user from moving the child window directly.
//This is my docking window
private System.Diagnostics.Process notepad;
private void windowDockTest()
{
/*
* Docking notepad to panel2 of the splitcontainer
*/
//if panel2 moves or is resized, call the docking function
spcScript.Panel2.Move += new EventHandler(Panel2_Resize);
spcScript.Panel2.SizeChanged += new EventHandler(Panel2_Resize);
//Call the docking function if main form is moved
this.LocationChanged += new EventHandler(Panel2_Resize);
//Start the notepad process
notepad = new System.Diagnostics.Process();
notepad.StartInfo.FileName = "notepad";
notepad.Start();
//Wait a second for notpad to fully load
notepad.WaitForInputIdle(1000);
//Dock it
Panel2_Resize(new Object(), new EventArgs());
}
void Panel2_Resize(object sender, EventArgs e)
{
//Get the screen location of panel2
Rectangle r = spcScript.Panel2.RectangleToScreen(spcScript.Panel2.ClientRectangle);
//Dock it
redock(notepad.MainWindowHandle, r.X, r.Y, r.Width, r.Height);
}
[DllImport("user32.dll")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
public static void redock(IntPtr handle, int x, int y, int width, int height)
{
IntPtr HWND_TOPMOST = new IntPtr(-1);
const short SWP_NOACTIVATE = 0x0010;
SetWindowPos(handle,HWND_TOPMOST, x, y, width, height,SWP_NOACTIVATE);
}

Bring a window to the front in WPF

How can I bring my WPF application to the front of the desktop? So far I've tried:
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);
None of which are doing the job (Marshal.GetLastWin32Error() is saying these operations completed successfully, and the P/Invoke attributes for each definition do have SetLastError=true).
If I create a new blank WPF application, and call SwitchToThisWindow with a timer, it works exactly as expected, so I'm not sure why it's not working in my original case.
Edit: I'm doing this in conjunction with a global hotkey.
myWindow.Activate();
Attempts to bring the window to the foreground and activates it.
That should do the trick, unless I misunderstood and you want Always on Top behavior. In that case you want:
myWindow.TopMost = true;
I have found a solution that brings the window to the top, but it behaves as a normal window:
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
In case you need the window to be in front the first time it loads then you should use the following:
private void Window_ContentRendered(object sender, EventArgs e)
{
this.Topmost = false;
}
private void Window_Initialized(object sender, EventArgs e)
{
this.Topmost = true;
}
Or by overriding the methods:
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
Topmost = false;
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
Topmost = true;
}
I know this question is rather old, but I've just come across this precise scenario and wanted to share the solution I've implemented.
As mentioned in comments on this page, several of the solutions proposed do not work on XP, which I need to support in my scenario. While I agree with the sentiment by #Matthew Xavier that generally this is a bad UX practice, there are times where it's entirely a plausable UX.
The solution to bringing a WPF window to the top was actually provided to me by the same code I'm using to provide the global hotkey. A blog article by Joseph Cooney contains a link to his code samples that contains the original code.
I've cleaned up and modified the code a little, and implemented it as an extension method to System.Windows.Window. I've tested this on XP 32 bit and Win7 64 bit, both of which work correctly.
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
}
}
I hope this code helps others who encounter this problem.
To make this a quick copy-paste one -
Use this class' DoOnProcess method to move process' main window to foreground (but not to steal focus from other windows)
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
Why some of the answers on this page are wrong!
Any answer that uses window.Focus() is wrong.
Why? If a notification message pops up, window.Focus() will grab the focus away from whatever the user is typing at the time. This is insanely frustrating for end users, especially if the popups occur quite frequently.
Any answer that uses window.Activate() is wrong.
Why? It will make any parent windows visible as well.
Any answer that omits window.ShowActivated = false is wrong.
Why? It will grab the focus away from another window when the message pops up which is very annoying!
Any answer that does not use Visibility.Visible to hide/show the window is wrong.
Why? If we are using Citrix, if the window is not collapsed when it is closed, it will leave a weird black rectangular hold on the screen. Thus, we cannot use window.Show() and window.Hide().
Essentially:
The window should not grab the focus away from any other window when it activates;
The window should not activate its parent when it is shown;
The window should be compatible with Citrix.
MVVM Solution
This code is 100% compatible with Citrix (no blank areas of the screen). It is tested with both normal WPF and DevExpress.
This answer is intended for any use case where we want a small notification window that is always in front of other windows (if the user selects this in the preferences).
If this answer seems more complex than the others, it's because it is robust, enterprise level code. Some of the other answers on this page are simple, but do not actually work.
XAML - Attached Property
Add this attached property to any UserControl within the window. The attached property will:
Wait until the Loaded event is fired (otherwise it cannot look up the visual tree to find the parent window).
Add an event handler that ensures that the window is visible or not.
At any point, you can set the window to be in front or not, by flipping the value of the attached property.
<UserControl x:Class="..."
...
attachedProperties:EnsureWindowInForeground.EnsureWindowInForeground=
"{Binding EnsureWindowInForeground, Mode=OneWay}">
C# - Helper Method
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".
}
}
}
Usage
In order to use this, you need to create the window in your 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);
}
}
Additional links
For tips on how ensure that a notification window always shifts back onto the visible screen, see my answer: In WPF, how to shift a window onto the screen if it is off the screen?.
If the user is interacting with another application, it may not be possible to bring yours to the front. As a general rule, a process can only expect to set the foreground window if that process is already the foreground process. (Microsoft documents the restrictions in the SetForegroundWindow() MSDN entry.) This is because:
The user "owns" the foreground. For example, it would be extremely annoying if another program stole the foreground while the user is typing, at the very least interrupting her workflow, and possibly causing unintended consequences as her keystrokes meant for one application are misinterpreted by the offender until she notices the change.
Imagine that each of two programs checks to see if its window is the foreground and attempts to set it to the foreground if it is not. As soon as the second program is running, the computer is rendered useless as the foreground bounces between the two at every task switch.
I know that this is late answer, maybe helpful for researchers
if (!WindowName.IsVisible)
{
WindowName.Show();
WindowName.Activate();
}
I have had a similar problem with a WPF application that gets invoked from an Access application via the Shell object.
My solution is below - works in XP and Win7 x64 with app compiled to x86 target.
I'd much rather do this than simulate an 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();
}
Well, since this is such a hot topic... here is what works for me. I got errors if I didn't do it this way because Activate() will error out on you if you cannot see the window.
Xaml:
<Window ....
Topmost="True"
....
ContentRendered="mainWindow_ContentRendered"> .... </Window>
Codebehind:
private void mainWindow_ContentRendered(object sender, EventArgs e)
{
this.Topmost = false;
this.Activate();
_UsernameTextBox.Focus();
}
This was the only way for me to get the window to show on top. Then activate it so you can type in the box without having to set focus with the mouse. control.Focus() wont work unless the window is Active();
Well I figured out a work around. I'm making the call from a keyboard hook used to implement a hotkey. The call works as expected if I put it into a BackgroundWorker with a pause. It's a kludge, but I have no idea why it wasn't working originally.
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();
}
To show ANY currently opened window import those 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);
and in program We search for app with specified title (write title without first letter (index > 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);
}
These codes will work fine all times.
At first set the activated event handler in XAML:
Activated="Window_Activated"
Add below line to your Main Window constructor block:
public MainWindow()
{
InitializeComponent();
this.LocationChanged += (sender, e) => this.Window_Activated(sender, e);
}
And inside the activated event handler copy this codes:
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();
}
These steps will works fine and will bring to front all other windows into their parents window.
Just wanted to add another solution to this question. This implementation works for my scenario, where CaliBurn is responsible for displaying the main Window.
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();
}
}
The problem could be that the thread calling your code from the hook hasn't been initialized by the runtime so calling runtime methods don't work.
Perhaps you could try doing an Invoke to marshal your code on to the UI thread to call your code that brings the window to the foreground.
If you are trying to hide the window, for example you minimize the window, I have found that using
this.Hide();
will hide it correctly, then simply using
this.Show();
will then show the window as the top most item once again.
Remember not to put the code that shows that window inside a PreviewMouseDoubleClick handler as the active window will switch back to the window who handled the event.
Just put it in the MouseDoubleClick event handler or stop bubbling by setting e.Handled to True.
In my case i was handling the PreviewMouseDoubleClick on a Listview and was not setting the e.Handled = true then it raised the MouseDoubleClick event witch sat focus back to the original window.
This is a combination of a few suggestions above that works well and is simple. It only comes to front when those events fire, so any window that pops up after the event will stay on top of course.
public partial class MainWindow : Window
{
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
Topmost = true;
Topmost = false;
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
Topmost = true;
Topmost = false;
}
....
}
I wanted to create a launcher with the keyboard hook and had the same problem.
After much trial and error, this solved the problem.
void Active()
{
MainWindow0.Show();
MainWindow0.Focus();
MainWindow0.Activate();
MainWindow0.WindowState = WindowState.Normal;
}
void Deactive()
{
MainWindow0.Hide();
MainWindow0.WindowState = WindowState.Minimized;
}
I built an extension method to make for easy reuse.
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
Call in the Form Constructor
namespace YourNamespace{
public partial class FormName : Form {
public FormName(){
this.PutOnTop();
InitalizeComponents();
}// END Constructor
} // END Form
}// END namespace

Categories