在单独的线程上运行表单,等待主UI线程完成

时间:2013-03-22 08:52:11

标签: c# .net vb.net thread-safety

在.NET 4.0中我正在处理一个加载时间很长(大约20秒)的应用程序,所以我想在一个表单上显示一个swish滚动选框,它在加载时会出现在应用程序的顶部。

由于主UI线程正在进行数据UI元素的所有加载,因此我无法在单独的线程上执行该操作,因此我最终尝试在新线程上运行该表单。我最终在表单中坚持使用这个代码,让它显示在一个新的线程上,如下所示:

Public Class frmWait

Public Property Message As String
    Get
        Return Me.lblMessage.Text
    End Get
    Set(value As String)
        If Not String.IsNullOrEmpty(value) Then
            Me.lblMessage.Text = value
        Else
            Me.lblMessage.Text = DefaultMessage
        End If
    End Set
End Property

Private OwnerThread As Thread
Private OwnerForm As Form
Private Const DefaultMessage As String = "しばらくお待ちください..."

Public Sub New(ByVal ParentForm As Form)

    InitializeComponent()

    Me.Message = DefaultMessage
    Me.OwnerForm = ParentForm

End Sub

Public Sub New(ByVal Message As String, ByVal ParentForm As Form)

    Call InitializeComponent()

    Me.Message = Message
    Me.OwnerForm = ParentForm

End Sub

Public Sub ShowOnThread()

    ' Position the form in the center of the owner
    With Me.OwnerForm
        Dim ownerCenter As New Point(.Location.X + CInt(.Width / 2), .Location.Y + CInt(.Height / 2))
        Me.Location = New Point(ownerCenter.X - CInt(Me.Width / 2), ownerCenter.Y - CInt(Me.Height / 2))
    End With

    Me.OwnerThread = New Thread(New ThreadStart(AddressOf Me.ShowDialog))
    Call Me.OwnerThread.Start()

End Sub

Public Shadows Sub Close()

    If Me.OwnerThread IsNot Nothing AndAlso Me.OwnerThread.IsAlive Then
        Call Me.OwnerThread.Abort()
    Else
        Call MyBase.Close()
    End If

End Sub

End Class

这可能非常笨拙,但我在应用程序的不同位置显示它,所以这似乎是最有效的代码方式...

它实际上运作得很好,但我不时会遇到问题,需要一些帮助才能解决这些问题。

  • 有时当表单关闭时,我会收到有关线程以不安全的方式中止的错误。

  • 目前我将表单手动放置在表单中心,我希望它覆盖表单。理想情况下,我希望能够在其上调用.ShowDialog(ParentForm),但当然由于从一种形式到另一种形式的跨线程访问而引发异常。

任何有关如何解决此问题的建议都将非常受欢迎。 因为我对线程几乎一无所知,所以我可能把它编成像猴子一样,所以如果有更好的方法来完成它,我非常想知道它。

我列出的代码是在VB.NET中,但是C#中的答案代码也很好(对于任何过分热心的重复者)

更新:

我现在意识到我应该在我的问题中提供更多细节......等待表单实际上不是我显示应用程序的第一种形式。首先是登录屏幕。当用户通过身份验证时,登录表单将启动应用程序的主界面,这是实际需要很长时间才能加载的表单。

我在登录表单和主界面之间显示等待表单。我还使用此表单来覆盖用户在主界面上启动的任何长时间运行的任务。

4 个答案:

答案 0 :(得分:1)

您应该将加载代码移动到单独的线程/后台工作程序,而不是尝试在单独的线程上显示对话框。

然后只需启动线程并在form_load上显示进度条并在线程完成时隐藏进度条:

Dim _progressForm As frmLoading

    Private Sub frmMain_Load(sender As System.Object, e As System.EventArgs) Handles Me.Load
        'start loading on a separate thread
        BackgroundWorker1.RunWorkerAsync()
        'show a marquee animation while loading
        _progressForm = New frmLoading
        _progressForm.ShowDialog(Me)
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        'simulate a long load
        For i As Integer = 1 To 10
            System.Threading.Thread.Sleep(1000)
        Next
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        _progressForm.Close()
    End Sub

答案 1 :(得分:1)

VisualStudio可以为您完成此操作。当然,您当然可以自己编写,但如果您查看Application下的项目选项,则会设置Splash Screen。您可以将任何表单(启动表单除外)指定为启动画面。它在自己的线程上运行,您可以在启动期间对其进行调整以推进进度条等。

MSDN : Splash Screen

答案 2 :(得分:0)

看一下TaskFactory.New

运行不同线程的线程代码以保持UI响应

http://msdn.microsoft.com/en-us/library/ee782519.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-3

答案 3 :(得分:0)

您可以使用ApplicationContext类来允许首先运行启动画面,然后在准备好时在主窗体中交换。在Application.Run的调用中使用它代替主窗体:

Application.Run(new MyApplicationCtx())

也为你搜索了一篇文章:

http://www.codeproject.com/Articles/5756/Use-the-ApplicationContext-Class-to-Fully-Encapsul