0

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

由于主 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

4 回答 4

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
于 2013-03-22T10:03:02.797 回答
1

VisualStudio 可以为您做到这一点。当然,您当然可以编写自己的,但如果您查看项目选项下ApplicationSplash Screen. 您可以将任何表单(启动表单除外)指定为初始屏幕。它在自己的线程上运行,您可以在启动期间编组调用它以推进进度条等。

MSDN:启动画面

于 2013-03-22T10:30:01.580 回答
0

看看 TaskFactory.New

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

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

于 2013-03-22T11:01:38.130 回答
0

您可以使用ApplicationContext该类来允许先运行初始屏幕,然后在准备好时切换到主窗体。在对 Application.Run 的调用中使用它代替主窗体:

Application.Run(new MyApplicationCtx())

刚刚也为您搜索了一篇文章:

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

于 2013-03-22T12:12:26.957 回答