0

I have following sample code

   [STAThread]
    static void Main(string[] args)
    {
        Thread thread = new Thread(() =>
        {
            using (var mww = new Form1())
            {
                Application.Run(mww);
            }
        });
        thread.Start();
        thread.Join();

        thread = new Thread(() =>
        {
            using (var mww = new Form1())
            {
                Application.Run(mww);
            }
        });
        thread.Start();
        thread.Join();

        thread = new Thread(() =>
        {
            using (var mww = new Form1())
            {
                Application.Run(mww);
            }
        });
        thread.Start();
        thread.Join();
    }

Which shows Form1 defined as:

public partial class Form1 : Form
{
    private readonly Timer _myTimer = new Timer();

    public Form1()
    {
        InitializeComponent();
        _myTimer.Interval = 10000;
        _myTimer.Tick += TOnTick;
        TopMost = true;
    }

    private void TOnTick(object sender, EventArgs eventArgs)
    {
        _myTimer.Stop();
        Close();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        _myTimer.Start();
    }
}

partial class Form1
{
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (_myTimer != null)
        {
            _myTimer.Dispose();
            _myTimer = null;
        }
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.SuspendLayout();
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(284, 262);
        this.Name = "Form1";
        this.Text = "Form1";
        this.Load += new System.EventHandler(this.Form1_Load);
        this.ResumeLayout(false);

    }

    #endregion
}

This code is very simple extraction of my production code, so please do not tell me it is useless.

If I would put all Application.Runs to one thread everything would be working and all three forms would be TopMost. If I run code as it is the first form is shown as TopMost and second and third does not.

BUT: If I close form by red X Close button on shown form (thus Close method in timer's tick eventhandler is not called), next form will be shown as TopMost. It seems to me that there must be some difference between X Close button on form and Close method called in code. But I cannot figure out what is the difference and how to reach wanted behavior: closing by timer, all windows top most.

Thanks for help

user2126375
  • 1,594
  • 12
  • 29

3 Answers3

6
    TopMost = true;

That doesn't quite do what you think it does. Windows has a "mild" and "crude" version of making a window top-most. Winforms implements the mild version of it, the one least likely to upset a user. Whom, in general, do get quite upset when a program shoves a window into the user's face.

Windows has counter-measures for this, ensuring that programs cannot do this unless it is likely that a user won't be upset by it. It is a heuristic, based on when a window owned by a process got user input. Like you clicking the Close button. A signal that the user is actively using the window. It doesn't just take clicking the Close button, you can for example click the window or press a cursor key and you'll see that your program manages to keep the foreground love.

The principal problem in your program is what happens when the first window closes. Your process has no windows left that can get the focus. So the Windows window manager is forced to find another one, that is going to be a window owned by an entirely different program. Like Visual Studio. Now the heuristic starts to act up, you can't gain the foreground back if Windows isn't convinced that you deserve it. Displaying another window in a different thread is not good enough to convince it.

Well, you are doing it wrong, starting with the condition you created where your program temporarily has no window. You can invoke the crude version of TopMost, that's pretty easy to do:

protected override CreateParams CreateParams {
    get {
        var cp = base.CreateParams;
        cp.ExStyle |= 8;  // Turn on WS_EX_TOPMOST
        return cp;
    }
}

And you'll see that your window now is truly top-most. The crude kind, used by programs that completely depend on having a topmost window, like osk.exe (the On Screen Keyboard program). The kind that tends to get users upset as well, for obvious reasons.

I do need to post a strong warning about your approach, displaying UI on different threads is very troublesome in other ways. The really bad kind, where your program randomly hangs for no discernible reason. The SystemEvents class is a major trouble-maker, it tries to fire events on the UI thread but of course cannot do this reliably when your program has more than one. Which tends to end quite poorly in many Winforms programs, there are a bunch of controls in the toolbox that cannot deal with the UserPreferenceChanged event when it is fired on the wrong thread. The kind of debug session you need to diagnose the hang looks like this. That was meant to scare you, don't do it.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • General question (not sure if its okay to ask here), because I remember that FoxPro was much more annoying with being on top of everything, is it implemented as "crude" by default if you set a window TopMost there? Anyway very nice answer! – DatRid Oct 28 '14 at 11:30
  • 1
    As noted in the post: "Winforms implements the mild version of it". Underlying winapi call is SetWindowPos() with HWND_TOPMOST for the 2nd argument. Not sure if it behaves the same on all Windows versions, I never use it. – Hans Passant Oct 28 '14 at 11:55
  • Hello, Thank you very much for the answer.Your code works well with my sample.To be honest,whole idea behind this question was following:I have program which works without any Gui in most of cases and I am logging progress to log files. But there is a new request from customer - to see progress of program if application is run with special parameter. Thus to minimize rework on guiless application I created new thread with form in it and I am feeding it by progress information. TopMost functionality and running reporting Form again is part of test whether my solution is good enough – user2126375 Oct 28 '14 at 12:03
  • There is a very simple and superior way to do this, you cannot get helpful answers if you ask [an XY question](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – Hans Passant Oct 28 '14 at 12:10
  • I was trying to omit all irrelevant staff from my code. I need just to log into WinForm same way how I am logging into logging framework. With minimal impact on code or rework. Thus I created WinForm in separate thread and using interface I can log into. You are mentioning superior way. Can you be please more detail about it? Thank you – user2126375 Oct 28 '14 at 12:27
1

Every form has a OnClosed & OnClosing event. What you do is explicit closing the form.

If you want to to stop the Timer when you click on the red X you need to use OnClosed or OnClosing.

private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
    _myTimer.Stop();
    _myTimer.Dispose();
}

private void TOnTick(object sender, EventArgs eventArgs)
{
    //_myTimer.Stop();
    //_myTimer.Dispose();  No need to call, as it will be redundant
    Close();   // Will call FormClosed
}

What you actually do now is to stop the timer on every tick of the timer, dispose it and then close the form.


EDIT:

I actually can reproduce your problem now if I try to move some other window over the form1, then the next one is not topmost.

I made some research then, and Raymond Chen explained perfectly why it doesn't work.

The question "What if two programs did this?" is also helpful in evaluating a feature or a design request. Combining this with "Imagine if this were possible" leads to an impressive one-two punch. Here are a few examples:

"How do I create a window that is never covered by any other windows, not even other topmost windows?"

Imagine if this were possible and imagine if two programs did this. Program A creates a window that is "super-topmost" and so does Program B. Now the user drags the two windows so that they overlap. What happens? You've created yourself a logical impossibility. One of those two windows must be above the other, contradicting the imaginary "super-topmost" feature.

I found this at this answer, which says:

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.

Warning: The text beneath is pure speculation, but it seems to make the most sense from what I experienced now.

So it seems like the first form is TopMost first, then you move another Window, which get's queued as "you-are-the-next-TopMost". Then you create the next form, which is also TopMost, but it is in the "you-are-TopMost"-queue behind the window you selected between those two.

Community
  • 1
  • 1
DatRid
  • 1,169
  • 2
  • 21
  • 46
  • Hello, I improved my code to call dispose on timer in dispose method of Form. Thus it is disposed correctly even if user click red close button on form. However it is wanted behavior for me to close form on first timer's tick. Thus Close in OnTick method is right – user2126375 Oct 28 '14 at 08:19
  • Problem is that only first shown window is topmost. Second and third not. Code for showing all three windows is same and I am looking for reason why second and third window have different TopMost behavior than the first window. Also I noticed that TopMost works great for second window if first window is not closed by calling Close method in Timer's tick method but first window is closed by red X button of first form. Very same it works for third window if second is closed by red cross – user2126375 Oct 28 '14 at 09:00
  • @user2126375 If I test it with your code above it works as expected. every window opens as TopMost. Or do you expect all three to appear at the same time ? – DatRid Oct 28 '14 at 09:16
  • No, they should be displayed one after one... But it is strange that you have all three topmost.. I do not. Did you tried to take another window (eg. explorer) and move it on location of Form1? Was Form1 in all three cases topmost? Please not that after second window is show it is shown in front of all windows but it is not TopMost, thus if you focus eg. explorer window it will cover second/third Form1. What operation system do you have? I have Windows 7 64bit – user2126375 Oct 28 '14 at 09:24
  • Hello, thank you for investigating this issue with me but still your answer does not solve the issue why TopMost works perfectly when I close previous window by red X, but do not work when I close it by calling Close method. I understand possible conflict when more than one process wants to have TopMost windows. But this is not the case. I have one process which is trying to have TopMost windows consecutively. – user2126375 Oct 28 '14 at 10:09
  • @user2126375 So you say, even if you have NO other window open, and you let the first form get closed by code, the second form is not top most ? – DatRid Oct 28 '14 at 10:19
  • In main method you can see thread.Join(). Thus window of Form1 is closed, disposed, thread which created it is finished and after that code move behind thread.Join() and start opening new window of Form1 in new thread. Of course I have a lot of opened windows in windows from different applications but these are different processes. – user2126375 Oct 28 '14 at 10:25
  • @user2126375 Yes sure, and I understand this. It doesn't matter if they are different processes, they are causing the conflict. Do all three forms show correct if you only open Visual Studio and let the program run ? – DatRid Oct 28 '14 at 10:57
  • It does not matter if I run program using Visual Studio, or executable. In both cases second and third windows are not TopMost. They can be - again in both cases - if I close previous instance of Form1 by red button X on previous form1 – user2126375 Oct 28 '14 at 11:04
  • @user2126375 even if you don't move any other window? – DatRid Oct 28 '14 at 11:12
0

Closing a form programmatically by calling close is somewhat different from closing it using the red cross. Using the red cross with a mouse click will result in additional windows messages which will activate the "next" window of your process. Your application is still active and it will therefore reactivate the next form. Have a look at the windows messages, you can use the pinvokes. For more information about these messages please visit pinvoke.net.

protected override void WndProc(ref Message m)
{
    Console.WriteLine(m.Msg);
    base.WndProc(ref m);
}

To solve your problem you might want to call activate when the form is shown. This will make the new form active and therefore TopMost. Keep in mind that calling active also claims the focus. You should use the ShowWindow pinvoke if you want to activate the form without hijacking the focus.

Dibran
  • 1,435
  • 16
  • 24
  • Hello, adding Activate mehtod call in Shown event handler did not help. Also there are no "next windows" between Form1 are show to user. All of them are in separated threads and thus focus cannot skipped between them and reactivate them. – user2126375 Oct 28 '14 at 08:57