Show a Form without stealing focus?
Asked Answered
O

20

156

I'm using a Form to show notifications (it appears at the bottom right of the screen), but when I show this form it steals the focus from the main Form. Is there a way to show this "notification" form without stealing focus?

Orchidaceous answered 1/10, 2008 at 2:49 Comment(0)
I
181

Hmmm, isn't simply overriding Form.ShowWithoutActivation enough?

protected override bool ShowWithoutActivation
{
  get { return true; }
}

And if you don't want the user to click this notification window either, you can override CreateParams:

protected override CreateParams CreateParams
{
  get
  {
    CreateParams baseParams = base.CreateParams;

    const int WS_EX_NOACTIVATE = 0x08000000;
    const int WS_EX_TOOLWINDOW = 0x00000080;
    baseParams.ExStyle |= ( int )( WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW );

    return baseParams;
  }
}
Inductee answered 1/10, 2008 at 14:21 Comment(5)
ShowWithoutActivation, Can't believe I didn't find it, wasted one whole afternoon!Dosh
I also needed to set form1.Enabled = false to prevent inner controls from stealing focusMajoriemajority
And if you do want TopMost, see the other answer.Kinson
The values of WS_EX_NOACTIVATE and WS_EX_TOOLWINDOW are 0x08000000 and 0x00000080 respectively.Hibbard
I have both of those solutions, they do not work when you change Visible to true. Dealing with Windows APIs is like playing with a rattlensake, at one point you'll be bitten no matter how careful you are.Mismate
E
77

Stolen from PInvoke.net's ShowWindow method:

private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = -1;
private const uint SWP_NOACTIVATE = 0x0010;

[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(
     int hWnd,             // Window handle
     int hWndInsertAfter,  // Placement-order handle
     int X,                // Horizontal position
     int Y,                // Vertical position
     int cx,               // Width
     int cy,               // Height
     uint uFlags);         // Window positioning flags

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

static void ShowInactiveTopmost(Form frm)
{
     ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
     SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
     frm.Left, frm.Top, frm.Width, frm.Height,
     SWP_NOACTIVATE);
}

(Alex Lyman answered this, I'm just expanding it by directly pasting the code. Someone with edit rights can copy it over there and delete this for all I care ;) )

Extrinsic answered 1/10, 2008 at 3:39 Comment(13)
I was wondering, do he really need that if the form he displays at the lower left of his screen it's in an other thread?Roveover
I find it unbelievable that we still need to link to external DLL files to interact with forms. We're at .NET framework version 4!! Time to wrap it Microsoft.Tidewater
The accepted answer is incorrect. Look for ShowWithoutActivationStitch
Just add frm.Hide(); at the beginning of the ShowInactiveTopmost function if you want your form unfocused directly. Do not forget : using System.Runtime.InteropServices; to make this code runningMoonlit
@Extrinsic when i used this code, the Load event of form is not fired... can u help me?Rabat
@Rabat This code has nothing to do with the Load event. The Load event fires when the form is being loaded, not when it is being shown.Extrinsic
@Tidewater – This is not true, it can be achieved with pure .NET. Just override ShowWithoutActivation property.Incentive
@Incentive welcome to the Internet, where threads get OLD like me. Yeah, there are better ways now! :)Extrinsic
@Extrinsic – I understand :) But I used "@" what is notification symbol at Stack Overflow comments also indicating to which person I reply :) And in this case, post I replied to was already addressing .NET 4.0 with complaint it is still not there. So I wanted to serve readers suggesting the statement might not be necessarily true for case of .NET 4.Incentive
@Incentive edit the answer and add quality new material thereExtrinsic
Topmost is deprecated since Windows 8 where Microsoft punishes you when using it. The effect is that after opening a topmost window and then closing it, Windows moves the other windows of your application to the background. This is surely not the desired behavior for your application. Microsoft implemented this because in the past many programmers abused the very intrusive topmost. Topmost is very aggressive. I never use it.Unhurried
Can this be modified to bring the window to the front without stealing focus (which this does) but without making the window topmost?Dump
Ah yes, to not leave the window topmost, just call SetWindowPos again with HWND_NOTOPMOST (= -2) instead instead of HWND_TOPMOST.Dump
A
17

This is what worked for me. It provides TopMost but without focus-stealing.

    protected override bool ShowWithoutActivation
    {
       get { return true; }
    }

    private const int WS_EX_TOPMOST = 0x00000008;
    protected override CreateParams CreateParams
    {
       get
       {
          CreateParams createParams = base.CreateParams;
          createParams.ExStyle |= WS_EX_TOPMOST;
          return createParams;
       }
    }

Remember to omit setting TopMost in Visual Studio designer, or elsewhere.

This is stolen, err, borrowed, from here (click on Workarounds):

https://connect.microsoft.com/VisualStudio/feedback/details/401311/showwithoutactivation-is-not-supported-with-topmost

Antelope answered 9/8, 2014 at 14:9 Comment(2)
Topmost + unfocused works, and it looks like the cleanest of all the answers.Monnet
Topmost is deprecated since Windows 8 where Microsoft punishes you when using it. The effect is that after opening a topmost window and then closing it, Windows moves the other windows of your application to the background. This is surely not the desired behavior for your application. Microsoft implemented this because in the past many programmers abused the very intrusive topmost. Topmost is very aggressive. I never use it.Unhurried
C
14

If you're willing to use Win32 P/Invoke, then you can use the ShowWindow method (the first code sample does exactly what you want).

Crutch answered 1/10, 2008 at 3:8 Comment(0)
S
9

Doing this seems like a hack, but it seems to work:

this.TopMost = true;  // as a result the form gets thrown to the front
this.TopMost = false; // but we don't actually want our form to always be on top

Edit: Note, this merely raises an already created form without stealing focus.

Stereotyped answered 1/10, 2008 at 3:4 Comment(5)
doesn't seems to work here ... could be because this "notify form" is opened in another thread?Orchidaceous
Probably, in that case you need to do a this.Invoke() call to call the method again as the right thread. Generally working with forms from the wrong thread causes an exception to be thrown though.Stereotyped
While this does work, it is a hacky method as mentioned and has caused BSOD for me under certain conditions, so beware of this.Lubricate
Topmost is deprecated since Windows 8 where Microsoft punishes you when using it. The effect is that after opening a topmost window and then closing it, Windows moves the other windows of your application to the background. This is surely not the desired behavior for your application. Microsoft implemented this because in the past many programmers abused the very intrusive topmost. Topmost is very aggressive. I never use it.Unhurried
This does steal the focusDump
M
9

The sample code from pinvoke.net in Alex Lyman/TheSoftwareJedi's answers will make the window a "topmost" window, meaning that you can't put it behind normal windows after it's popped up. Given Matias's description of what he wants to use this for, that could be what he wants. But if you want the user to be able to put your window behind other windows after you've popped it up, just use HWND_TOP (0) instead of HWND_TOPMOST (-1) in the sample.

Mastership answered 1/10, 2008 at 4:44 Comment(0)
W
6

In WPF you can solve it like this:

In the window put these attributes:

<Window
    x:Class="myApplication.winNotification"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Notification Popup" Width="300" SizeToContent="Height"
  WindowStyle="None" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" Topmost="True" Focusable="False" ShowActivated="False" >
</Window>

The last one attribute is the one you need ShowActivated="False".

Wendiwendie answered 20/2, 2012 at 23:36 Comment(0)
H
4

I have something similar, and I simply show the notification form and then do

this.Focus();

to bring the focus back on the main form.

Hypochondriac answered 15/11, 2011 at 22:25 Comment(1)
Simple but effective!Antetype
P
3

You might want to consider what kind of notification you would like to display.

If it's absolutely critical to let the user know about some event, using Messagebox.Show would be the recommended way, due to its nature to block any other events to the main window, until the user confirms it. Be aware of pop-up blindness, though.

If it's less than critical, you might want to use an alternative way to display notifications, such as a toolbar on the bottom of the window. You wrote, that you display notifications on the bottom-right of the screen - the standard way to do this would be using a balloon tip with the combination of a system tray icon.

Pox answered 1/10, 2008 at 3:14 Comment(1)
- Balloon tips is not an option because can be disabled - The statusbar could be hidden if you have the program minimized Anyway thanks for your recomendationsOrchidaceous
T
3

Create and start the notification Form in a separate thread and reset the focus back to your main form after the Form opens. Have the notification Form provide an OnFormOpened event that is fired from the Form.Shown event. Something like this:

private void StartNotfication()
{
  Thread th = new Thread(new ThreadStart(delegate
  {
    NotificationForm frm = new NotificationForm();
    frm.OnFormOpen += NotificationOpened;
    frm.ShowDialog();
  }));
  th.Name = "NotificationForm";
  th.Start();
} 

private void NotificationOpened()
{
   this.Focus(); // Put focus back on the original calling Form
}

You can also keep a handle to your NotifcationForm object around so that it can be programmatically closed by the main Form (frm.Close()).

Some details are missing, but hopefully this will get you going in the right direction.

Textual answered 1/10, 2008 at 3:24 Comment(3)
This will only work if your form was the originally active form. That kind of goes against the main purpose of this kind of notification.Crutch
Huh? That is the purpose of the notification -- to put it up and regain focus back to the originally active form.Textual
This only gives focus to a form in your application -- what if some other program is active at the time? Showing a notification window (usually to give the user an update on your application's status) is only really useful when they're not watching your application.Crutch
C
3

This works well.

See: OpenIcon - MSDN and SetForegroundWindow - MSDN

using System.Runtime.InteropServices;

[DllImport("user32.dll")]
static extern bool OpenIcon(IntPtr hWnd);

[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);

public static void ActivateInstance()
{
    IntPtr hWnd = IntPtr hWnd = Process.GetCurrentProcess().MainWindowHandle;

    // Restore the program.
    bool result = OpenIcon(hWnd); 
    // Activate the application.
    result = SetForegroundWindow(hWnd);

    // End the current instance of the application.
    //System.Environment.Exit(0);    
}
Chub answered 19/5, 2009 at 19:52 Comment(0)
T
1

You can handle it by logic alone too, although I have to admit that the suggestions above where you end up with a BringToFront method without actually stealing focus is the most elegant one.

Anyhow, I ran into this and solved it by using a DateTime property to not allow further BringToFront calls if calls were made already recently.

Assume a core class, 'Core', which handles for example three forms, 'Form1, 2, and 3'. Each form needs a DateTime property and an Activate event that call Core to bring windows to front:

internal static DateTime LastBringToFrontTime { get; set; }

private void Form1_Activated(object sender, EventArgs e)
{
    var eventTime = DateTime.Now;
    if ((eventTime - LastBringToFrontTime).TotalMilliseconds > 500)
        Core.BringAllToFront(this);
    LastBringToFrontTime = eventTime;
}

And then create the work in the Core Class:

internal static void BringAllToFront(Form inForm)
{
    Form1.BringToFront();
    Form2.BringToFront();
    Form3.BringToFront();
    inForm.Focus();
}

On a side note, if you want to restore a minimized window to its original state (not maximized), use:

inForm.WindowState = FormWindowState.Normal;

Again, I know this is just a patch solution in the lack of a BringToFrontWithoutFocus. It is meant as a suggestion if you want to avoid the DLL file.

Testify answered 7/3, 2013 at 12:36 Comment(0)
F
1

I don't know if this is considered as necro-posting, but this is what I did since I couln't get it working with user32's "ShowWindow" and "SetWindowPos" methods. And no, overriding "ShowWithoutActivation" doesn't work in this case since the new window should be always-on-top. Anyway, I created a helper method that takes a form as parameter; when called, it shows the form, brings it to the front and makes it TopMost without stealing the focus of the current window (apparently it does, but the user won't notice).

    [DllImport("user32.dll")]
    static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll")]
    static extern IntPtr SetForegroundWindow(IntPtr hWnd);

    public static void ShowTopmostNoFocus(Form f)
    {
        IntPtr activeWin = GetForegroundWindow();

        f.Show();
        f.BringToFront();
        f.TopMost = true;

        if (activeWin.ToInt32() > 0)
        {
            SetForegroundWindow(activeWin);
        }
    }
Faitour answered 3/6, 2015 at 6:54 Comment(0)
B
0

I know it may sound stupid, but this worked:

this.TopMost = true;
this.TopMost = false;
this.TopMost = true;
this.SendToBack();
Baldridge answered 24/9, 2009 at 14:27 Comment(1)
If you send the front window to the back, it may no longer be showing if the background windows overlaps the new foreground one.Reprieve
M
0

I needed to do this with my window TopMost. I implemented the PInvoke method above but found that my Load event wasn't getting called like Talha above. I finally succeeded. Maybe this will help someone. Here is my solution:

        form.Visible = false;
        form.TopMost = false;
        ShowWindow(form.Handle, ShowNoActivate);
        SetWindowPos(form.Handle, HWND_TOPMOST,
            form.Left, form.Top, form.Width, form.Height,
            NoActivate);
        form.Visible = true;    //So that Load event happens
Modernize answered 10/3, 2016 at 23:38 Comment(0)
E
0

You don't need to make it anywhere near as complicated.

a = new Assign_Stock(); 
a.MdiParent = this.ParentForm;
a.Visible = false;   //hide for a bit.                 
a.Show(); //show the form. Invisible form now at the top.
this.Focus(); //focus on this form. make old form come to the top.
a.Visible = true; //make other form visible now. Behind the main form.
Enamel answered 26/10, 2021 at 16:6 Comment(0)
R
0

enter image description here

Github Sample

Form.ShowWithoutActivation Property

Add this in your child form class

    protected override bool ShowWithoutActivation
        {
            get { return true; }
        }

Working Code

Form2

   public partial class Form2 : Form
    {
        Form3 c;
        public Form2()
        {
            InitializeComponent();
             c = new Form3();
        }

        private void textchanged(object sender, EventArgs e)
        {


            c.ResetText(textBox1.Text.ToString());
            c.Location = new Point(this.Location.X+150, this.Location.Y);
            c .Show();

//removethis
//if mdiparent 2 add this.focus() after show form

            c.MdiParent = this.MdiParent;
            c.ResetText(textBox1.Text.ToString());
            c.Location = new Point(this.Location.X+150, this.Location.Y);
            c .Show();
           this.Focus();
////-----------------


        }


       
    }

Form3

   public partial class Form3 : Form
    {
        public Form3()
        {
            InitializeComponent();
            //ShowWithoutActivation = false;
        }
        protected override bool ShowWithoutActivation
        {
            get { return true; }
        }


        internal void ResetText(string toString)
        {
            label2.Text = toString;
        }

     
    }
Rapt answered 5/1, 2022 at 17:5 Comment(0)
D
0

Slight modification to this answer so that the window doesn't remain topmost

private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = -1;
private const int HWND_NOTOPMOST = -2;
private const uint SWP_NOACTIVATE = 0x0010;

[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(
     int hWnd,             // Window handle
     int hWndInsertAfter,  // Placement-order handle
     int X,                // Horizontal position
     int Y,                // Vertical position
     int cx,               // Width
     int cy,               // Height
     uint uFlags);         // Window positioning flags

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

static void ShowInactiveTopmost(Form frm)
{
     ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
     SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
     frm.Left, frm.Top, frm.Width, frm.Height,
     SWP_NOACTIVATE);
     SetWindowPos(frm.Handle.ToInt32(), HWND_NOTOPMOST,
     frm.Left, frm.Top, frm.Width, frm.Height,
     SWP_NOACTIVATE);
}
Dump answered 17/5, 2023 at 20:42 Comment(0)
M
-4

When you create a new form using

Form f = new Form();
f.ShowDialog();

it steals focus because your code can't continue executing on the main form until this form is closed.

The exception is by using threading to create a new form then Form.Show(). Make sure the thread is globally visible though, because if you declare it within a function, as soon as your function exits, your thread will end and the form will disappear.

Munitions answered 1/10, 2008 at 3:10 Comment(0)
E
-5

Figured it out: window.WindowState = WindowState.Minimized;.

Estevez answered 9/12, 2012 at 18:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.