user674311
user674311

Reputation:

C# show notification form without it taking focus

So, we deiced that it'd be a good idea to add some sort of Toast ability to our chat application, which actually IS working fine, however, when the form shows, it steals focus for a breif second, which can make the chat input box (when your typing into it) flash or clear all text (as the focus was stolen away from it).

I have reviewed several threads on this site, about how to stop it from happening by overriding the createparms and even doing the showwithoutactivating or some such, but it's not working quite right.

This is what i have (i do apologize for all the comments, our bosses want everything documented):

 public partial class ToastForm : DevComponents.DotNetBar.Metro.MetroForm
{
    #region Variables

    private readonly int _location;

    /// <summary>
    /// The list of currently open ToastForms.
    /// </summary>
    private static readonly List<ToastForm> OpenForms = new List<ToastForm>();

    /// <summary>
    /// Set the window to top most
    /// </summary>
    private const int WsExTopmost = 0x00000008;
    #endregion // Variables


    #region Constructors

    /// <summary>
    /// Creates a new ToastForm object that is displayed for the specified length of time.
    /// </summary>
    /// <param name="lifeTime">
    /// The length of time, in milliseconds, that the form will be displayed.
    /// </param>
    /// <param name="title">title of tooltip</param>
    /// <param name="message">message for tooltip</param>
    /// <param name="location">Location:  0 = top left, 1 = top right, 2 = bottom left, 3 = bottom right</param>
    public ToastForm(int lifeTime,
                     string title,
                     string message,
                     int location)
    {
        InitializeComponent();


        // Set the time for which the form should be displayed and the message to display.
        lifeTimer.Interval = lifeTime;
        tooltipTitle.Text =
            string.Format("<b><font size=\"+6\"><i>New</i><font color=\"#B02B2C\">{0}</font></font></b>",
                          title);
        tooltipText.Text = message;
        _location = location;

    }

    /// <summary>
    /// Do not activate the window just show it
    /// </summary>
    protected override bool ShowWithoutActivation
    {
        get { return true; }
    }

    /// <summary>
    /// Force the ExStyle to be TopMost
    /// </summary>
    protected override CreateParams CreateParams
    {
        get
        {
            var param = base.CreateParams;
            param.ExStyle |= WsExTopmost; // make the form topmost
            return param;
        }
    }

    #endregion // Constructors


    #region Event Handlers

    /// <summary>
    /// Do this when the form loads
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void ToastFormLoad(object sender,
                               EventArgs e)
    {

        switch (_location)
        {
            case 0: // top left corner of screen
                Location = new Point(0,
                                     0);
                break;
            case 1: // top right corner of screen
                Location = new Point(Screen.PrimaryScreen.WorkingArea.Width - Width - 5,
                                     0);
                break;
            case 2: // bottom left corner of screen
                Location = new Point(0,
                                     Screen.PrimaryScreen.WorkingArea.Height - Height - 5);
                break;
            case 3: // bottom right corner of screen
                Location = new Point(Screen.PrimaryScreen.WorkingArea.Width - Width - 5,
                                     Screen.PrimaryScreen.WorkingArea.Height - Height - 5);
                break;
        }

        // Move each open form upwards to make room for this one.
        foreach (var openForm in OpenForms)
        {
            switch (_location)
            {
                case 0:
                case 1:
                    openForm.Top += Height + 5;
                    break;
                case 2:
                case 3:
                    openForm.Top -= Height + 5;
                    break;
            }

        }
        OpenForms.Add(this);
        lifeTimer.Start();
    }

    /// <summary>
    /// Happens when the form closes
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void ToastFormFormClosed(object sender,
                                     FormClosedEventArgs e)
    {
        // Move down any open forms above this one.
        foreach (var openForm in OpenForms.TakeWhile(openForm => openForm != this))
        {
            switch (_location)
            {
                case 0:
                    openForm.Top -= Height + 5;
                    break;
                case 1:
                    openForm.Top -= Height + 5;
                    break;
                case 2:
                    openForm.Top += Height + 5;
                    break;
                case 3:
                    openForm.Top += Height + 5;
                    break;
            }
        }

        // Remove this form from the open form list.
        OpenForms.Remove(this);
    }

    /// <summary>
    /// If the tooltip has expired
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void LifeTimerTick(object sender,
                               EventArgs e)
    {
        // The form's lifetime has expired.
        Close();
    }

    #endregion // Event Handlers

    #region Methods

    /// <summary>
    /// Quickly close the tooltip
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void TooltipCancelClick(object sender,
                                    EventArgs e)
    {
        lifeTimer.Interval = 1;
    }
    #endregion
}

We trigger these popups based on events in our main form (like when a new public message comes in, by referencing a command in another class (saves room in our main form's class):

class ToastControl
{
    public static int SliceCount { get; private set; }

    internal static void ShowAlert(string msg, string title, Font fnt)
    {
        switch (Settings.Default.PopUpSide)
        {
            case 0:
                AlertTopLeft(msg, title, fnt);
                break;
            case 1:
                AlertTopRight(msg, title, fnt);
                break;
            case 2:
                AlertBottomLeft(msg, title, fnt);
                break;
            case 3:
                AlertBottomRight(msg, title, fnt);
                break;
            default:
                 AlertBottomRight(msg, title, fnt);
                break;
        }
    }

    internal static void AlertBottomRight(string msg, string title, Font fnt)
    {
        SliceCount += 1;
        var slice = new ToastForm(5000,
                                  title,
                                  msg, 3)
        {
            Height = (25 + 82) + ((int)(msg.Length / fnt.Size)) * 2
        };

        slice.Show();
    }

   internal static void AlertBottomLeft(string msg, string title, Font fnt)
    {
        SliceCount += 1;
        var slice = new ToastForm(5000,
                                  title,
                                  msg,
                                  2)
                                  {
                                      Height = (25 + 82) + ((int)(msg.Length / fnt.Size)) * 2
                                  };

        slice.Show();
    }

    internal static void AlertTopLeft(string msg, string title, Font fnt)
    {
        SliceCount += 1;
        var slice = new ToastForm(5000,
                                  title,
                                  msg, 0)
        {
            Height = (25 + 82) + ((int)(msg.Length / fnt.Size)) * 2
        };
        slice.Show();
    }

    internal static void AlertTopRight(string msg, string title, Font fnt)
    {
        SliceCount += 1;
        var slice = new ToastForm(5000,
                                  title,
                                  msg, 1)
        {
            Height = (25 + 82) + ((int)(msg.Length / fnt.Size)) * 2
        };
        slice.Show();
    }
}

And an example of the call to this class:

if (Settings.Default.PopUpEnabledChat)
            {
                if (!(Settings.Default.NoAlerts))
                    ToastControl.ShowAlert(string.Format("{0}: {1}", user.Nick, description.Replace("\r",
                                                              "").Replace("\n",
                                                                          "").Replace("\0",
                                                                                      "")), channel, Font);
            }

How can i get this notification form to show without ever stealing focus from the main app?

Upvotes: 2

Views: 4379

Answers (1)

user674311
user674311

Reputation:

Looks like the solution is to do pretty much what i did, except that i needed to add the following to the Notification Form:

/// <summary>
    /// Do not activate the window just show it
    /// </summary>
    protected override bool ShowWithoutActivation
    {
        get { return true; }
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x00000008; //WS_EX_TOPMOST 
            return cp;
        }
    } 

And change the Toast Control class to use 'Visible' instead of '.Show':

 internal static void AlertBottomRight(string msg, string title, Font fnt)
    {
        SliceCount += 1;
        new ToastForm(5000,
                      title,
                      msg,
                      3) {
                             Height = (25 + 82) + ((int) (msg.Length / fnt.Size)) * 2,
                             Visible = true
                         };
    }

    internal static void AlertBottomLeft(string msg, string title, Font fnt)
    {
        SliceCount += 1;
        new ToastForm(5000,
                      title,
                      msg,
                      2) {
                             Height = (25 + 82) + ((int) (msg.Length / fnt.Size)) * 2,
                             Visible = true
                         };
    }

    internal static void AlertTopLeft(string msg, string title, Font fnt)
    {
        SliceCount += 1;
        new ToastForm(5000,
                      title,
                      msg,
                      0) {
                             Height = (25 + 82) + ((int) (msg.Length / fnt.Size)) * 2,
                             Visible = true
                         };
    }

    internal static void AlertTopRight(string msg, string title, Font fnt)
    {
        SliceCount += 1;
        new ToastForm(5000,
                      title,
                      msg,
                      1) {
                             Height = (25 + 82) + ((int) (msg.Length / fnt.Size)) * 2,
                             Visible = true
                         };
    }

Now when the notifications are shown, they do not steal focus, and they appear just fine on the top of other windows :)

Upvotes: 6

Related Questions