在第二个线程上创建表单时遇到问题

时间:2011-01-21 04:01:05

标签: c# .net winforms multithreading

我正在通过C#.NET为另一个应用程序编写插件。我的插件必须执行的一些过程相当耗时,所以我想利用多个线程,这样我就可以向用户显示当前任务如果进展而不是整个事情只是挂起的进度条。

通常,在主线程中创建这样的UI,并创建辅助线程来完成工作,例如通过BackGroundWorker类。但是,在我的情况下,工作必须在主线程中完成,因为我正在编写插件的应用程序不满意除了为插件创建的线程之外的线程 - 访问它。

所以我正在创建第二个线程来创建我的UI(一个WinForms表单),然后传回主线程来做任何实际的工作。

我能够在主线程中创建我的Form很好,但是当我尝试在第二个线程中实例化我的表单时,我得到一个InvalidOperationException。这发生在设计器文件中,表单中列表视图中列的名称属性已设置。

以下是例外的详细信息。

System.InvalidOperationException was caught
  Message=ColumnInfo cannot be set.
  Source=System.Windows.Forms
  StackTrace:
       at System.Windows.Forms.ListView.SetColumnInfo(Int32 mask, ColumnHeader ch)
       at System.Windows.Forms.ColumnHeader.set_Text(String value)
       at QA.Revit.RevitQAForm.InitializeComponent() in C:\Documents and Settings\eric.anastas\My Documents\_SVN WC\QA Tool\RevitModelCheckerPlugIn\RevitQAForm.Designer.cs:line 758
       at QA.Revit.RevitQAForm..ctor() in C:\Documents and Settings\eric.anastas\My Documents\_SVN WC\QA Tool\RevitModelCheckerPlugIn\RevitQAForm.cs:line 34
       at QA.Revit.RevitQAToolApp.FormMethod() in C:\Documents and Settings\eric.anastas\My Documents\_SVN WC\QA Tool\RevitModelCheckerPlugIn\RevitModelCheckerCmd.cs:line 99
  InnerException: 

更新

我似乎已经通过将辅助UI线程的ApartmentState更改为STA来实现此功能。虽然我完全不熟悉这种多线程的东西,但不知道ApartmentState或STA的意思。

这是我的代码。

//property used to store a reference to the form
internal RevitQAForm RevitQAForm { get; set; }

//monitor object that when pulsed shows the form
public static readonly Object showFormLock = new object(); 


//this method is called by the parent app when it starts
public Autodesk.Revit.UI.Result OnStartup(Autodesk.Revit.UI.UIControlledApplication application)
{
    //this creates the form UI Thread
    _formThread = new System.Threading.Thread(new System.Threading.ThreadStart(FormMethod));
    _formThread.Name = "Form Thread";
    _formThread.SetApartmentState(System.Threading.ApartmentState.STA);
    _formThread.Start();

    //returns that the plug-in startup succeeded
    return Autodesk.Revit.UI.Result.Succeeded;
}

//the method is started on the second thread
private void FormMethod()
{
    try
    {
        //creates the form
        RevitQAForm = new RevitQAForm();

        lock (showFormLock)
        {
            while (true)
            { 
                //waits for a pulse
                System.Threading.Monitor.Wait(showFormLock);
                RevitQAForm.ShowDialog();
            }
        }
    }
    catch (System.Threading.ThreadAbortException)
    {
        //disposes the form if the thread is aborted
        RevitQAForm.Dispose();
    }
}

//this is called when the user request the form be shown
public void ShowForm()
{
    lock (showFormLock)
    {
        System.Threading.Monitor.Pulse(showFormLock);
    }
}

//this is called when the program closes
public Autodesk.Revit.UI.Result OnShutdown(Autodesk.Revit.UI.UIControlledApplication application)
{
    //aborts the form thread
    formThread.Abort();
    return Autodesk.Revit.UI.Result.Succeeded;
}

就像我说的那样现在似乎有效。我可以使用我的插件启动应用程序并重复显示表单。我关闭程序时也会处理该表单。

但是现在我正在试图找出这种形式如何与主线程进行通信。表单将需要能够触发主线程开始处理,然后主线程需要能够定期将其进度报告回表单线程。在任何时候,表单线程应该能够告诉主线程取消处理。最后,主线程需要在处理完成时通知表单。

任何人都有关于我如何做到这一点的任何提示?

3 个答案:

答案 0 :(得分:1)

这不起作用。所有表单都需要在Windows中使用底层消息泵,为此,他们需要在原始线程上。

答案 1 :(得分:0)

尝试调用使用

的方法
System.Windows.Forms.ListView.SetColumnInfo(Int32 mask, ColumnHeader ch)

使用方法Invoke

答案 2 :(得分:0)

要在主线程中触发处理,您可以使用任何WaitHandle派生类,例如说ManualResetEvent / AutoResetEvent - 本质上,主线程将等待等待句柄并且表单线程可以发出信号事件开始处理。

要将进度从主线程传递回UI /表单线程,您可以使用事件或委托。最简单的方法是声明进程更新委托,使用某种形式的方法实例化它。然后主线程可以调用它 - 这将基本上在您的表单类(主线程)中运行该方法。在此方法中,您必须使用表单的Invoke方法对表单的线程进行编组调整。