问题
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