122

I have a C# winforms app that runs a macro in another program. The other program will continually pop up windows and generally make things look, for lack of a better word, crazy. I want to implement a cancel button that will stop the process from running, but I cannot seem to get the window to stay on top. How do I do this in C#?

Edit: I have tried TopMost = true; , but the other program keeps popping up its own windows over top. Is there a way to send my window to the top every n milliseconds?

Edit: The way I solved this was by adding a system tray icon that will cancel the process by double-clicking on it. The system tray icon does no get covered up. Thank you to all who responded. I read the article on why there is not a 'super-on-top' window... it logically does not work.

4
  • 77
    Yes, set a timer for every few milliseconds that will set your Form.TopMost to true. Then, just to make it interesting, when the "crazy" program loads, play the audio clip from Mortal Kombat "FIGHT!" :-P
    – BFree
    Commented Mar 25, 2009 at 20:46
  • 2
    You might thought your comment was hilarious, you might thought you could ridicule bad practice. My problem was creating a context menu that floats over a form with a flowlayoutpanel. A flowlayoutpanel can only be scrolled if you call it's Activate() method, Focus() is NOT enough in certain circumstances. You just won't be able to scroll it. That steals focus from the contextmenu even if it has exclusive topmost = true! As any sensible person knows it's godly practive to let your winform applications run in MTAThread mode and give every form it's own thread which makes the solution simple:
    – ASA
    Commented Feb 18, 2014 at 9:57
  • 1
    Behold, a devil: pastebin.com/sMJX0Yav It works flawlessly without flickering and the sleep(1) is enough to keep it from draining serious performance. Who keeps looking in his taskmanager anyways while he focuses on a context menu? Once the context menu closes it hopefully runs into the empty exception handler and dies. You might build in a isDisposed break though.
    – ASA
    Commented Feb 18, 2014 at 9:59
  • @Traubenfuchs That will fail because of Cross-thread operation exception. This should work.
    – mekb
    Commented Jul 21, 2019 at 2:29

15 Answers 15

207

Form.TopMost will work unless the other program is creating topmost windows.

There is no way to create a window that is not covered by new topmost windows of another process. Raymond Chen explained why.

3
  • 15
    In case any other complete newbies see this in 2016 and beyond, try Form.ActiveForm.TopMost
    – user736893
    Commented Sep 6, 2016 at 13:33
  • 3
    In my case I also had to give the Window a Start Position, otherwise it was not staying at the top topMostForm.StartPosition = FormStartPosition.CenterScreen see also: learn.microsoft.com/en-us/dotnet/api/…
    – Thomas
    Commented Feb 18, 2021 at 11:51
  • ..."because window is garbage" got it!
    – aydunno
    Commented Sep 18, 2022 at 14:16
72

I was searching to make my WinForms application "Always on Top" but setting "TopMost" did not do anything for me. I knew it was possible because WinAmp does this (along with a host of other applications).

What I did was make a call to "user32.dll." I had no qualms about doing so and it works great. It's an option, anyway.

First, import the following namespace:

using System.Runtime.InteropServices;

Add a few variables to your class declaration:

private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const UInt32 SWP_NOSIZE = 0x0001;
private const UInt32 SWP_NOMOVE = 0x0002;
private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

Add prototype for user32.dll function:

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

Then in your code (I added the call in Form_Load()), add the call:

SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);

[Reference][1] [1]: http://www.c-sharpcorner.com/uploadfile/kirtan007/make-form-stay-always-on-top-of-every-window/

11
  • 2
    This works not only for WinForms applications, but also for console windows. Nice find!
    – rojo
    Commented Jun 19, 2016 at 23:09
  • 1
    Nice, can confirm this works. But how would I be able to change it back to not be the top most? is there a HWND_BOTTOMMOST flag that you can share?
    – Mark
    Commented Apr 26, 2018 at 14:35
  • 1
    @Mitra M, that's not real at all. Windows works entirely with handles, and any window have one. Check out my edit in this answer: I've added a way to use it in WPF, too. Commented Nov 9, 2018 at 19:15
  • 3
    @Mark Yes, there is a flag HWND_NOTOPMOST (= -2). See learn.microsoft.com/en-us/windows/win32/api/winuser/… Commented Jul 17, 2019 at 7:02
  • 1
    this works even for IE.hwnd! great
    – Alander
    Commented Jan 2, 2022 at 14:28
24

If by "going crazy" you mean that each window keeps stealing focus from the other, TopMost will not solve the problem.

Instead, try:

CalledForm.Owner = CallerForm;
CalledForm.Show();

This will show the 'child' form without it stealing focus. The child form will also stay on top of its parent even if the parent is activated or focused. This code only works easily if you've created an instance of the child form from within the owner form. Otherwise, you might have to set the owner using the API.

2
  • Setting CalledForm.Owner to itself (CalledForm) will cause a System.ArgumentException: 'A circular control reference has been made. A control cannot be owned by or parented to itself.'
    – mekb
    Commented Jul 21, 2019 at 2:16
  • 2
    Which is why you use CallerForm instead of CalledForm :)
    – Jesper
    Commented Sep 9, 2019 at 9:52
18

Set Form.TopMost

2
  • I tried, this... do I need to be continually doing it? The 'crazy program' takes over immediately...
    – jle
    Commented Mar 25, 2009 at 20:40
  • 2
    No - if you set your form.TopMost = true, it should work. The "crazy" program must have it's dialogs set to TopMost as well, in which case, you can't override it. Commented Mar 25, 2009 at 20:48
15

I had a momentary 5 minute lapse and I forgot to specify the form in full like this:

  myformName.ActiveForm.TopMost = true;

But what I really wanted was THIS!

  this.TopMost = true;
2
  • Worked perfect for me. if (checkBox1.Checked == true) { this.TopMost = true; } else { this.TopMost = false; }
    – yosh
    Commented Feb 7, 2020 at 12:05
  • @Yosh, you could just do: this.TopMost = checkBox1.Checked; And I should have replied 2 years ago :D
    – Dave
    Commented Mar 2, 2023 at 9:53
9

Set the form's .TopMost property to true.

You probably don't want to leave it this way all the time: set it when your external process starts and put it back when it finishes.

6

Why not making your form a dialogue box:

myForm.ShowDialog();
1
  • 1
    Yes! This is what I wanted. Setting TopMost = true forced my form on top of everything, including chrome, when it reality it's just a settings box and I needed it on top of the main form. Kudos to you internet person.
    – MDMoore313
    Commented Oct 18, 2016 at 0:29
5

The way i solved this was by making a system tray icon that had a cancel option.

5

The following code makes the window always stay on top as well as make it frameless.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace StayOnTop
{
    public partial class Form1 : Form
    {
        private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        private const UInt32 SWP_NOSIZE = 0x0001;
        private const UInt32 SWP_NOMOVE = 0x0002;
        private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

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

        public Form1()
        {
            InitializeComponent();
            FormBorderStyle = FormBorderStyle.None;
            TopMost = true;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            SetWindowPos(this.Handle, HWND_TOPMOST, 100, 100, 300, 300, TOPMOST_FLAGS);
        }

        protected override void WndProc(ref Message m)
        {
            const int RESIZE_HANDLE_SIZE = 10;

            switch (m.Msg)
            {
                case 0x0084/*NCHITTEST*/ :
                    base.WndProc(ref m);

                    if ((int)m.Result == 0x01/*HTCLIENT*/)
                    {
                        Point screenPoint = new Point(m.LParam.ToInt32());
                        Point clientPoint = this.PointToClient(screenPoint);
                        if (clientPoint.Y <= RESIZE_HANDLE_SIZE)
                        {
                            if (clientPoint.X <= RESIZE_HANDLE_SIZE)
                                m.Result = (IntPtr)13/*HTTOPLEFT*/ ;
                            else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
                                m.Result = (IntPtr)12/*HTTOP*/ ;
                            else
                                m.Result = (IntPtr)14/*HTTOPRIGHT*/ ;
                        }
                        else if (clientPoint.Y <= (Size.Height - RESIZE_HANDLE_SIZE))
                        {
                            if (clientPoint.X <= RESIZE_HANDLE_SIZE)
                                m.Result = (IntPtr)10/*HTLEFT*/ ;
                            else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
                                m.Result = (IntPtr)2/*HTCAPTION*/ ;
                            else
                                m.Result = (IntPtr)11/*HTRIGHT*/ ;
                        }
                        else
                        {
                            if (clientPoint.X <= RESIZE_HANDLE_SIZE)
                                m.Result = (IntPtr)16/*HTBOTTOMLEFT*/ ;
                            else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
                                m.Result = (IntPtr)15/*HTBOTTOM*/ ;
                            else
                                m.Result = (IntPtr)17/*HTBOTTOMRIGHT*/ ;
                        }
                    }
                    return;
            }
            base.WndProc(ref m);
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.Style |= 0x20000; // <--- use 0x20000
                return cp;
            }
        }
    }
}
1
  • Agree with Alexan - what makes your program topmost? It seems like it's actually just the "topmost = true" statement, which isn't working in a lot of cases. All the rest of the code doesn't really answer the problem.
    – Fhaab
    Commented Jul 13, 2017 at 22:33
3

What is the other application you are trying to suppress the visibility of? Have you investigated other ways of achieving your desired effect? Please do so before subjecting your users to such rogue behaviour as you are describing: what you are trying to do sound rather like what certain naughty sites do with browser windows...

At least try to adhere to the rule of Least Surprise. Users expect to be able to determine the z-order of most applications themselves. You don't know what is most important to them, so if you change anything, you should focus on pushing the other application behind everything rather than promoting your own.

This is of course trickier, since Windows doesn't have a particularly sophisticated window manager. Two approaches suggest themselves:

  1. enumerating top-level windows and checking which process they belong to, dropping their z-order if so. (I'm not sure if there are framework methods for these WinAPI functions.)
  2. Fiddling with child process permissions to prevent it from accessing the desktop... but I wouldn't try this until the othe approach failed, as the child process might end up in a zombie state while requiring user interaction.
3

Here is the SetForegroundWindow equivalent:

form.Activate();

I have seen people doing weird things like:

this.TopMost = true;
this.Focus();
this.BringToFront();
this.TopMost = false;

http://blog.jorgearimany.com/2010/10/win32-setforegroundwindow-equivalent-in.html

2
  • What if I don't want my window to be active, I just want it topmost (informative, not interactive)? I ask only because actually issuing a "topmost=True" is not working in my case (it works on systems, not on others).
    – Fhaab
    Commented Jul 13, 2017 at 22:36
  • Found this to work for us: this.Show(); this.Activate(); this.BringToFront(); But this answer got us to that solution. Thanks!
    – jibbs
    Commented Oct 31, 2017 at 14:22
2

If you just want to Have Window Forms on top of a Game:(CSGOYour game needs to be in Fulscreen WINDOWED mode) or while you open different programs. Just use TopMost = true; In the:

private void Forms1_Load(object sender, EventArgs e)
        {
            TopMost = true;
        }

In the Latest Version of .Net 6 Visual studio 2022 Just wanted to point this one if you are trying to do this for games :D

1

I know this is old, but I did not see this response.

In the window (xaml) add:

Deactivated="Window_Deactivated"

In the code behind for Window_Deactivated:

private void Window_Deactivated(object sender, EventArgs e)
    {
        Window window = (Window)sender;
        window.Activate();
    }

This will keep your window on top.

1
  • 1
    You didn't see this response because the question is about winform.
    – Kinetic
    Commented Jul 5, 2018 at 16:35
1

Based on clamum's answer, and Kevin Vuilleumier's comment about the other flag responsible for the behavior, I made this toggle that switches between on-top and not on-top with a button press.

private void button1_Click(object sender, EventArgs e)
    {
        if (on)
        {
            button1.Text = "yes on top";
            IntPtr HwndTopmost = new IntPtr(-1);
            SetWindowPos(this.Handle, HwndTopmost, 0, 0, 0, 0, TopmostFlags);
            on = false;
        }
        else
        {
            button1.Text = "not on top";
            IntPtr HwndTopmost = new IntPtr(-2);
            SetWindowPos(this.Handle, HwndTopmost, 0, 0, 0, 0, TopmostFlags);
            on = true;
        }
    }
0

I did something i little bit differnt kinda found it much easier

so first on Form Load

private void Form1_Load(object sender, EventArgs e)
{       
   this.Shown += new EventHandler(Form1_Shown);//let your form show up here
}

private void Form1_Shown(Object sender, EventArgs e)
{
  Form1.ActiveForm.TopMost = true //and then do your TopMost logic 
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.