多线程任务表单调用问题,有更好的方法吗?
Multithreaded Task Form Invoke Issue, Is there a Better way to do this?
我有一个我编写的应用程序,它执行一些繁重的任务,我在其中显示了一个任务表单。此任务表单显示当前进度,以及在繁重任务线程中设置的状态文本。我现在遇到的问题是我的 Invoke 调用(UpdateStatus 方法)在表单实际有时间显示自身之前被调用,并开始抛出异常。
这是我的表格:
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();
}
}
注意:我确实删除了一些与我的问题none相关的代码
问题出在这个例子中:
// 显示任务对话框
TaskForm.Show(this, "My Window Title", "Header Text", false);
TaskForm.UpdateStatus("Status Message"); // Exception Thrown HERE!!
如果 ShowForm() 方法中没有 Thread.Sleep(),我会得到可怕的 Invoke 异常 (InvalidOperationException => "Invoke or BeginInvoke cannot be called on a control until the window handle has been created.")。有没有更好的方法来显示此表单以防止这些问题?我感到内疚不得不使用 Thread.Sleep() 来等待 GUI 像它应该的那样弹出。
可以通过IsHandleCreated
属性[msdn]判断window句柄是否已经创建。所以不需要 sleep
时间,我们不能保证它是恒定的。
所以你可以使用类似的东西,
TaskForm.Show(this, "My Window Title", "Header Text", false);
while(!this.IsHandleCreated); // Loop till handle created
TaskForm.UpdateStatus("Status Message");
我已经使用过这个并且它对我有用,我也欢迎任何关于这个解决方案的评论。
我有一个我编写的应用程序,它执行一些繁重的任务,我在其中显示了一个任务表单。此任务表单显示当前进度,以及在繁重任务线程中设置的状态文本。我现在遇到的问题是我的 Invoke 调用(UpdateStatus 方法)在表单实际有时间显示自身之前被调用,并开始抛出异常。
这是我的表格:
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();
}
}
注意:我确实删除了一些与我的问题none相关的代码
问题出在这个例子中:
// 显示任务对话框
TaskForm.Show(this, "My Window Title", "Header Text", false);
TaskForm.UpdateStatus("Status Message"); // Exception Thrown HERE!!
如果 ShowForm() 方法中没有 Thread.Sleep(),我会得到可怕的 Invoke 异常 (InvalidOperationException => "Invoke or BeginInvoke cannot be called on a control until the window handle has been created.")。有没有更好的方法来显示此表单以防止这些问题?我感到内疚不得不使用 Thread.Sleep() 来等待 GUI 像它应该的那样弹出。
可以通过IsHandleCreated
属性[msdn]判断window句柄是否已经创建。所以不需要 sleep
时间,我们不能保证它是恒定的。
所以你可以使用类似的东西,
TaskForm.Show(this, "My Window Title", "Header Text", false);
while(!this.IsHandleCreated); // Loop till handle created
TaskForm.UpdateStatus("Status Message");
我已经使用过这个并且它对我有用,我也欢迎任何关于这个解决方案的评论。