Multithreaded Task Form Invoke Issue, Is there a Better way to do this?

耗尽温柔 提交于 2021-02-08 04:49:34

问题


I have an application that i wrote that does a few heavy tasks, in which i display a Task Form. This task form shows the current progress, as well as Status text that is set within the heavy tasks thread. The issue I'm coming across right now is that My Invoke call (The UpdateStatus method) is getting called before the form actually has time to display itself, and starts throwing exceptions.

Here is my form:

public partial class TaskForm : Form
{
    const int WM_SYSCOMMAND = 0x0112;
    const int SC_MOVE = 0xF010;

    /// <summary>
    /// The task dialog's instance
    /// </summary>
    private static TaskForm Instance;

    /// <summary>
    /// Private constructor... Use the Show() method rather
    /// </summary>
    private TaskForm()
    {
        InitializeComponent();
    }

    public static void Show(Form Parent, string WindowTitle, string InstructionText, string SubMessage, bool Cancelable, ProgressBarStyle Style, int ProgressBarSteps)
    {
        // Make sure we dont have an already active form
        if (Instance != null && !Instance.IsDisposed)
            throw new Exception("Task Form is already being displayed!");

        // Create new instance
        Instance = new TaskForm();

        // === Instance form is setup here === //
        // Setup progress bar...
        // Hide Instruction panel if Instruction Text is empty...
        // Hide Cancel Button if we cant cancel....
        // Set window position to center parent

        // Run form in a new thread
        Thread thread = new Thread(new ThreadStart(ShowForm));
        thread.IsBackground = true;
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        Thread.Sleep(500); // Wait for Run to work or the Invoke exceptions are thrown
    }

    public static void CloseForm()
    {
        // No exception here
        if (Instance == null || Instance.IsDisposed)
            return;

        try
        {
            Instance.Invoke((Action)delegate()
            {
                Instance.Close();
                Application.ExitThread();
            });
        }
        catch { }
    }

    protected static void ShowForm()
    {
        Application.Run(Instance);
    }

    public static void UpdateStatus(string Message)
    {
        if (Instance == null || Instance.IsDisposed)
            throw new Exception("Invalid Operation. Please use the Show method before calling any operational methods");

        Instance.Invoke((Action)delegate()
        {
            Instance.labelContent.Text = Message;
        });
    }

    new public void Show()
    {
        base.Show();
    }
}

NOTE: I did cut out some code that is none related to my question

The issue lays in this example:

// Show task dialog

TaskForm.Show(this, "My Window Title", "Header Text", false);
TaskForm.UpdateStatus("Status Message"); // Exception Thrown HERE!!

Without the Thread.Sleep() in the ShowForm() method, i get the dreaded Invoke exception (InvalidOperationException => "Invoke or BeginInvoke cannot be called on a control until the window handle has been created."). Is there a better way to display this form to prevent these issues? I feel guilty having to use a Thread.Sleep() to wait for the GUI to popup like its suppossed to.


回答1:


You can use IsHandleCreated Property [msdn] to determine whether the window handle has been created. So no need of a sleep time which we cannot guarantee to be constant.

so you can use something like,

TaskForm.Show(this, "My Window Title", "Header Text", false);
while(!this.IsHandleCreated); // Loop till handle created
TaskForm.UpdateStatus("Status Message");

I have use this and it worked for me, I welcome any comments regarding this solution too.



来源:https://stackoverflow.com/questions/28229128/multithreaded-task-form-invoke-issue-is-there-a-better-way-to-do-this

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!