views:

635

answers:

2

This post is continuation of http://stackoverflow.com/questions/2167625/way-to-quickly-show-hide-winforms-gui-c, as it doesn't work for me in this particular case.

I've got 2 problems:

  • 1 is that the mainAnnounceWindow gui should start hidden and later on when called by: windowStateChange("Show") it should show, by windowStateChange("Hide") it should hide. It doesn't do that properly as when i start app it's visible for like 0,5 s (i see it blinking). Is there a way to make it start hidden and not blink for half a second.

  • 2 is that windowStateChange doesn't work properly when called from myThreadHandler (Queue.Work).

    internal class Program {
    public delegate void StateCallBack(string varState);
    public static readonly Announce mainAnnounceWindow = new Announce();
    public static readonly Thread myThreadGuiAnnounce = new Thread(showGuiAnnounce);
    public static readonly Thread myThreadTcpClient = new Thread(threadTcpClient);
    public static readonly Thread myThreadUdpMonitor = new Thread(threadUdpMonitor);
    public static readonly Thread myThreadHandler = new Thread(Queue.work);
    
    
    public static void Main() 
    {
        myThreadGuiAnnounce.Start();
        myThreadTcpClient.Start();
        myThreadUdpMonitor.Start();
        myThreadHandler.Start();
        windowStateChange("Hide");
    
    
    
    while (true) {
        Thread.Sleep(1000);
    }
    
    } public static void windowStateChange(string varState) { if (mainAnnounceWindow.InvokeRequired) { mainAnnounceWindow.Invoke(new StateCallBack(windowStateChange), new object[] {varState}); } else { if (varState == "Hide") { mainAnnounceWindow.Hide(); mainAnnounceWindow.TopMost = false; } else { mainAnnounceWindow.Show(); mainAnnounceWindow.TopMost = true; } } } private static void showGuiAnnounce() { mainAnnounceWindow.ShowDialog(); } }

Another class:

  public class Queue : IDisposable {
 public static void work() {
        while (true) {
            string task = null;
            lock (locker)
                if (tasks.Count > 0) {
                    task = tasks.Dequeue();
                    if (task == null) {
                        return;
                    }
                }
            if (task != null) {
                //MessageBox.Show("Performing task: " + task);
                Program.mainAnnounceWindow.setLogTextBox(task);
                Program.mainAnnounceWindow.setLogTrayTip(task);
                Program.windowStateChange("Show");
                Thread.Sleep(5000); // simulate work...
                Program.windowStateChange("Hide");
            } else {
                wh.WaitOne(); // No more tasks - wait for a signal
            }
        }
    }

}

The problem is with:

                Program.windowStateChange("Show");
                Thread.Sleep(5000); // simulate work...
                Program.windowStateChange("Hide");

When i call Program.windowStateChange("Show"); from inside other thread the gui shows but not quite.. like i can see that it would like to show, but it doesn't. Like a hang of app. But when the Thread.Sleep(5000) passes the app hides.

Calling:

                Program.mainAnnounceWindow.setLogTextBox(task);
                Program.mainAnnounceWindow.setLogTrayTip(task);

Has no problem. BaloonTip shows, just the Gui doesn't show up properly. Something i'm doing wrong.

Oh and of course i did some cut/paste so it may miss some stuff. If it's necessary to add something let me know.

With regards,

MadBoy

+1  A: 

Your problem is you are accessing a ui object from a non ui thread, which is not allowed. You need to pass a delegate wrapping the operation you want to perform to the Invoke or BeginInvoke methods on one of the ui controls (perhaps your form object).

Kevin Stafford
I am using public static void windowStateChange(string varState) which uses Invoke to get into GUi thread. isn't it enough?
MadBoy
+1  A: 

The problem is that your main thread is locked up, because you added this:

while (true) { 
    Thread.Sleep(1000); 
} 

This will prevent the window thread from receiving and processing the windows messages (such as show and hide) appropriately.

You'll also want to use mainAnnounceWindow.Show(), not mainAnnounceWindow.ShowDialog(), as this will prevent control from returning properly to the main thread. You should just call Application.Run(mainAnnounceWindow) in your Main routine:

public static void Main()       
{      
    myThreadGuiAnnounce.Start();      
    myThreadTcpClient.Start();      
    myThreadUdpMonitor.Start();      
    myThreadHandler.Start();      

    // Just change your main window's load to hide itself... windowStateChange("Hide");      
    Application.Run(mainAnnounceWindow);
}
Reed Copsey
myThreadGuiAnnounce.Start(); is starting the thread private static void showGuiAnnounce() { mainAnnounceWindow.ShowDialog();} so in fact GUI has it's own thread separate from Main. So Threa.Sleep(1000) just makes Main thread to sleep and do nothing while gui is in another thread? Shouldn't it work ?
MadBoy
That's not going to be safe. The new thread would need to be started STA, and if you use ShowDialog, the window will not be processing messages and be able to hide the way you want. Leave the window in the main thread, and show it normally (non-dialog), and your code will most likely work the wya you want...
Reed Copsey
Indeed. If i don't use separate thread to create mainAnnounceWindow.. and i use Main thread as Gui thread your solution works. But what if i would like to have Main thread left for other things (as the Thread.Sleep was there so the Main thread wouldn't exit.
MadBoy
It's almost ALWAYS better to leave the GUI on the main thread, and push your work onto other threads. This will be more maintainable, now and in the future.
Reed Copsey
OK. Your solution works fine and i guess i can live with GUI being in main thread. What about my 2nd problem that when started the gui becomes visible for blink. I have added: private void Announce_Shown(object sender, System.EventArgs e) { this.Hide(); }to make it hide on startup.. what would be better way to do this?
MadBoy
Put it into Form_Load, and set this.visible = false, instead of doing this on the shown event.
Reed Copsey
this.visible = false in Announce_load has no impact. Gui still shows (totally not just for a brief moment). Also sometimes an error TargetInvocationException is thrown on Application.Run(mainAnnounceWindow); Also i think i will be needing 2nd gui thread for another gui which should be run at same time as the mainAnnounceWindow.
MadBoy
I managed to track down the exception, i still have visibility problem thou :)
MadBoy