简体   繁体   English

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

[英]Running a form on a separate thread waiting for main UI thread to complete

In .NET 4.0 I'm dealing with an app which has a long loading time (about 20 seconds), so I wanted to display a swish scrolling marquee on a form that comes on top of the application whilst it is loading. 在.NET 4.0中,我正在处理一个具有较长加载时间(大约20秒)的应用程序,因此我想在应用程序顶部的表单上显示一个swish滚动选框,同时加载它。

Since the main UI thread is doing all the loading of data UI elements, I couldn't get that to execute on a separate thread, so I ended up trying to run the form on a new thread. 由于主UI线程正在进行数据UI元素的所有加载,因此我无法在单独的线程上执行该操作,因此我最终尝试在新线程上运行该表单。 I ended up sticking this code in the form itself, to have it show itself on a new thread, like this: 我最终在表单中坚持使用这个代码,让它在一个新线程上展示自己,如下所示:

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

This is probably quite clumsy, but I am showing it in different places in the application, so this seemed the most code-efficient way of doing this... 这可能是非常笨拙的,但我在应用程序的不同位置显示它,所以这似乎是最有效的代码方式...

It actually works quite well, but I am encountering problems from time to time with this and need some help on how to address these issues. 它实际上工作得很好,但我不时会遇到问题,需要一些帮助来解决这些问题。

  • Sometimes when the form gets closed I get an error about the thread being aborted in an unsafe manner. 有时当表单被关闭时,我得到一个关于线程以不安全的方式被中止的错误。

  • At the moment I position the form manually in the centre of the form I want it to cover form. 此刻,我将表单手动放置在表单的中心,我希望它覆盖表单。 Ideally I'd like to be able to call .ShowDialog(ParentForm) on it, but of course that raises an exception because of cross-thread access from one form to the other. 理想情况下,我希望能够在其上调用.ShowDialog(ParentForm) ,但当然由于从一种形式到另一种形式的跨线程访问而引发异常。

Any suggestions on how to resolve this would be most appreciated. 任何关于如何解决这个问题的建议都将非常感激。 Because I know virtually nothing about threading I probably coded this like a monkey, so if there is a better method to get this done, I would very much like to know about it. 因为我对线程几乎一无所知,所以我可能把它编成像猴子一样,所以如果有更好的方法来完成这项工作,我非常想知道它。

The code I list is in VB.NET, but answer code in C# is fine too (for any overzealous retaggers) 我列出的代码是在VB.NET中,但C#中的答案代码也很好(对于任何过分热心的重复者)

UPDATE: 更新:

I realise now that I should have given a lot more details in my question... The wait form is actually not the first form I am displaying the app. 我现在意识到我应该在我的问题中提供更多细节......等待表单实际上不是我显示应用程序的第一种形式。 There first is a login screen. 首先是登录屏幕。 When the user is authenticated, the login form launches the main interface of the app, which is the form which actually takes a long time to load. 当用户通过身份验证时,登录表单将启动应用程序的主界面,这是实际需要很长时间才能加载的表单。

I am displaying the wait form in between the login form and the main interface. 我在登录表单和主界面之间显示等待表单。 I also use this form to cover for any long running tasks launched on the main interface by the user. 我还使用此表单来涵盖用户在主界面上启动的任何长时间运行的任务。

Instead of trying to show a dialog on a separate thread you should be moving your loading code to a separate thread / background worker. 您应该将加载代码移动到单独的线程/后台工作程序,而不是尝试在单独的线程上显示对话框。

Then just start your threads and show the progressbar on form_load and hide the progressbar when the thread completes: 然后只需启动线程并在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

VisualStudio can do this for you. VisualStudio可以为您完成此操作。 You can certainly write your own, of course, but if you look at your project options under Application there is a setting for Splash Screen . 当然,您当然可以自己编写,但如果您查看Application下的项目选项,则会有Splash Screen的设置。 You can assign any form (other than the startup form) to be your splash screen. 您可以将任何表单(启动表单除外)指定为启动画面。 This runs on its own thread and you can marshall calls to it during startup to advance a progress bar, etc. 它在自己的线程上运行,您可以在启动期间对其进行调整以推进进度条等。

MSDN : Splash Screen MSDN:启动画面

Have a look at TaskFactory.New 看看TaskFactory.New

Run your threaded code of a different thread to keep the UI responsive 运行不同线程的线程代码以保持UI响应

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

You could use the ApplicationContext class to allow the splash screen to be run first and then swap in the Main form when it is ready. 您可以使用ApplicationContext类来允许首先运行启动屏幕,然后在准备好时在主窗体中交换。 You use it in place of your main form in the call to Application.Run: 您在Application.Run调用中使用它来代替主窗体:

Application.Run(new MyApplicationCtx())

Just googled up a article for you as well: 刚刚为你搜索了一篇文章:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM