简体   繁体   中英

Update a Modeless Dialog from the Main Window with Windows Forms

I'm working on a C#/.NET 3.5/Windows Forms program. I would like to create a Modeless Dialog box and then update it with information generated from the main form. I've found several examples of the other direction - info from a dialog box updates the main window, but I'm having trouble going in the reverse direction. The GUI in the dialog box just doesn't function properly. The mouse cursor is an hourglass. I sometimes see updates, but I often see a solid white box, remnants of other windows that were once on top of the dialog box, or it freezes.

Eventually the Dialog box will display more complicated information, but for now I'm just trying to duplicate a counter that appears as a label on the Main Window. To launch the dialog, I do the following:

    bool secondWindowOpen = false;
    Thread secondWindowThread;
    MyPopupWindow secondWindow;

    delegate void TextBoxDelegate(string message);

    private void buttonPop_Click(object sender, EventArgs e)
    {
        // If the second window is not open, then open it
        if (!secondWindowOpen)
        {
            secondWindowOpen = true;

            secondWindow = new MyPopupWindow();
            secondWindowThread = new Thread(secondWindow.MyMethod);
            secondWindowThread.Start();

        }
        else // Close the second window
        {
            secondWindowOpen = false;
            secondWindow.ShouldStop = true;

            secondWindowThread.Join();                
        }
    }

I update the counter with the following code:

if (secondWindow != null)
{
    secondWindow.CounterText = args.FrameNumber.ToString();
}

The code for controlling the Modeless Dialog box looks like the following. I'll admit the code for setting the text field is probably wrong, but it's the best thing I have working at the moment (Invoke calls were giving me trouble).

public partial class MyPopupWindow : Form
{
    public MyPopupWindow()
    {
        InitializeComponent();
    }

    public bool ShouldStop
    {
        get { return shouldStop; }
        set { shouldStop = value; }
    }
    private bool shouldStop = false;

    public void MyMethod()
    {            
        this.Show();
        this.Refresh();

        while (!shouldStop)
        {
            Thread.Sleep(100);
            labelCounter.Text = CounterText;
            Refresh();
        }

        this.Close();
    }

    public string CounterText;
}

My big questions are the following: What is the best way to put a Modeless Dialog Box into a second thread when it just needs to update its own GUI elements? What is the best way to update those GUI elements from the Main Window/Thread? Thanks!

The way forms work is that at the core if the program is a loop that processes messages and passes them out to the forms to be processed. If your form never returns control to your message loop, your program stops working properly. You've tried to solve this by adding more threads, but that way leads to pain of its own.

You can't do this in a thread as all operations applied to your form MUST be done on the UI thread. You could pass back calls like Invalidate to the UI thread using BeginInvoke, but that gets really messy - it's much better to write cooperative windows that run on the one UI thread than to fire off lots of threads that then try to prod the UI thread into doing things for them.

To do this you would create the form and then return control to your message handling loop. At a future time you would close the window from an event like a button click. If you need to update the window as result of any events, you would call invalidate in your event handler and then return.

Based on Jason's suggestions, I ended up simplifying things and getting it to actually work.

First, no more starting a new thread when I display the second window. Instead, I do the following:

bool secondWindowOpen = false;
MyPopupWindow secondWindow;

private void buttonPop_Click(object sender, EventArgs e)
{
    // If the second window is not open, then open it
    if (!secondWindowOpen)
    {
        secondWindowOpen = true;
        secondWindow = new MyPopupWindow();
        secondWindow.Show();                
    }
    else // Close the second window
    {
        secondWindowOpen = false;
        secondWindow.Close();
    }
}

Then I simplified the code in the second window to the following:

public partial class MyPopupWindow : Form
{
    public MyPopupWindow()
    {
        InitializeComponent();
    }

    public string CounterText
    {
        set { labelCounter.Text = value; }
    }
}

First of all, please apologize for my English because I'm french.

Then, if i understood your problem, you would like to update the controls of a second Form from a first one.

please, use the Tag object of the Forms.

  1. Create a class to stock the Data you want to transmit between the forms
  2. Create an instance of the second Form from the first one as this :

    Form2 f = new Form2();
    f.Tag = MyDataObject();

  3. display the Form like

    f.Show() => Modeless way or f.ShowDialog => Modal way

  4. In the constructor of your Form2 you update your controls from your Tag object as this :

    public Form2()
    {
    MyDataObject do = (MyDataObject)this.Tag;
    MyLabel.Text = do.LabelText;
    }

  5. If you want to do it from the Form2 to the Main Form, do the same :

    Into the Form2

    this.Tag = MyDataObject;
    this.Close();

Into the Main Form :

`MyDataObject do = (MyDataObject)f2.Tag;`

I know the topic is old but hope this helps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM