微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

VB.Net程序设计:跨线程弹出等待提示窗体,允许多个等待提示窗体同时存在

在实际应用中遇到长时间计算分析,让用户等待的时候,窗体如果没有处理好,一般会出现假死状态。网络上也有很多这方面处理的方法,多线程或者BackgroundWorker组件。而且大部分采用了Singleton单一模式方法

疑问

1.大部分复杂操作是在后台操作(另外一个线程),由主线程的创建一个子线程来操作。能否在子线程操作的过程中弹出一个等待提示窗体吗?由子线程和等待提示窗体自己完成一切的操作。

正常的做法是:在后台执行工作中,发送信息给主线程UI。

疑问1是本文的重点:某个线程A(可以是主线程或者子线程)创建一个包含有等待提示窗体的线程B,由A发送信息给B。那么线程A的父线程或者主进程就不用理会A和B了。

2.如果有多个子线程在处理分析,那么Singleton单一模式一个窗体来显示等待提示信息的话,一个窗体显示不同几组提示信息,就不大好了,或者提示信息的显示就会乱。

为了解决以上的疑问,参考了本论坛和博客园的几位高人的思路,写了一个允许多个等待提示窗体的程序,本人水平有限,拍砖就拍砖吧,望大家指点更好的方法或思路。

参考链接

http://www.cnblogs.com/xiaozhi_5638/p/3457499.html

http://www.jb51.cc/article/p-epbfvvyh-bbo.html

正常做法有两种等待模式:引用第一个链接的插图:


图1 等待模式,后台工作没有完成之前,UI界面不允许操作。


图2,非等待模式,后台工作没完成之前,UI界面可以操作。

本文的疑问的处理方法


图3,简单做法,任务大的话UI线程理论上还是会有阻塞。但可以解决UI空间不足等问题。


图4 推荐做法,不必每次都写更新UI线程的界面,复杂任务执行中自己处理等待提示消息,主UI不必去理会A和B。


如果有多个等待提示窗体,那么就会有多个过程产生多个提示窗体,而且为了让等待提示窗体不阻碍主线程的操作,那么必须将等待提示窗体封装起来。

封装在一个类里面,而且窗体的运行模式应该是在另外一个线程里面。同时等待提示窗体允许创建他的线程发送提示信息给他,等待提示窗体接收到信息后显示出来。

程序主要内容有:

1.一个等待提示窗体:FrmMsgWaitingFor

窗体有几种组合模式:

Public Enum eWaitingFormType
    Message  '只显示提示信息
    Progress  '只显示进度条 由于进度无法预测,认设置Stype属性设为Marquee
    MessageAndProgress  '显示信息和进度条
    MessageAndWorking  '显示信息和加载动画
End Enum

2.一个可以创建等待提示窗体的类似工厂的类:MsgWaitingFactory

此类需要完成的工作:

2.1可以创建出不同的等待提示窗体。

2.2在另外一个线程中创建窗体并显示,让调用本类的线程(可以主线程或者某个子线程)与等待提示窗体线程分离。

2.3主线程可以通过这个类的实例来发送信息给等待提示窗体,也可以通知关闭等待提示窗体。

3.调用方法

调用MsgWaitingFactory方法可以是主线程调用,也可以是子线程调用:也就是上面所说的A和B的调用

Dim msgWaiter As New MsgWaitingFactory(eWaitingFormType.MessageAndWorking)
msgWaiter.Show()
msgWaiter.SendMsgToWaiter("开始复杂计算分析...")
'......
'复杂计算分析过程中
msgWaiter.SendMsgToWaiter("提示信息...")
'......
msgWaiter.SendMsgToWaiter("处理完毕.")
msgWaiter.Close()

当然,因为FrmMsgWaitingFor有简单的用跨线程的安全处理,也可以直接调用这个窗体:

Dim frm As New FrmMsgWaitingFor(eWaitingFormType.MessageAndWorking,Me)
frm.Show()
frm.SetMessage("working...")
frm.SetMessage("update UI...")
frm.SetMessage("Finish.")
frm.Close()

等待提示窗体设计界面如下:


代码:MsgWaitingFactory

Imports System.Threading

Public Class MsgWaitingFactory

    Private _threadWaiter As Thread
    Private _frmWaiter As FrmMsgWaitingFor
    Private _eFormType As eWaitingFormType
    Private _MyParentForm As Form

    '------------------------------------------------------
	'Singleton单一模式
    'Private Shared ReadOnly objLock As New Object
    'Private Shared _curFrm As FrmMsgWaitingFor

    'Public Shared ReadOnly Property CurrentWaitingForForm(e As eWaitingFormType) As FrmMsgWaitingFor
    '    Get
    '        SyncLock objLock
    '            If _curFrm Is nothing Then
    '                _curFrm = New FrmMsgWaitingFor(e)
    '            End If
    '            Return _curFrm
    '        End SyncLock
    '    End Get
    'End Property
    '------------------------------------------------------

    Public Sub New(e As eWaitingFormType)
        _eFormType = e
    End Sub

    '------------------------------------------------------
    Public Sub New(e As eWaitingFormType,ParentForm As Form)
        _eFormType = e
        _MyParentForm = ParentForm
    End Sub
  
    Public Sub SendMsgToWaiter(msg As String)
        If _frmWaiter IsNot nothing Then _frmWaiter.SetMessage(msg)
    End Sub
 
    Public Sub SendMsgToWaiter(msg As String,isTitle As Boolean)
        If _frmWaiter IsNot nothing Then
            If isTitle Then
                _frmWaiter.SetFormText(msg)
            Else
                _frmWaiter.SetMessage(msg)
            End If
        End If
    End Sub

    Public Sub Show()
        If _threadWaiter IsNot nothing Then
            Try
                _threadWaiter.Abort()
                _threadWaiter = nothing
            Catch ex As ThreadStartException

            End Try
        End If
        '---------------------
        _threadWaiter = New Thread(New ThreadStart(Sub()
                                                       _frmWaiter = New FrmMsgWaitingFor(_eFormType)
                                                       '_frmWaiter.MdiParent = _MyParentForm
                                                       Application.Run(_frmWaiter)
                                                   End Sub))
        '---------------------
        _threadWaiter.IsBackground = True
        _threadWaiter.SetApartmentState(ApartmentState.STA)
        _threadWaiter.Start()
    End Sub
    

    Public Sub Close()
        '-----------
        'If _threadWaiter IsNot nothing Then
        '    Try
        '        'Application.OpenForms("FrmMsgWaitingFor").Close()跨线程有异常
        '        'Application.OpenForms("FrmMsgWaitingFor").dispose()
        '        _threadWaiter.Abort()  '终止线程,也有异常。
        '        _threadWaiter.disableComObjectEagerCleanup()
        '    Catch ex As ThreadStartException

        '    End Try
        'End If
        '--此方法让窗体自动执行在其线程上关闭
        If _frmWaiter IsNot nothing Then
            _frmWaiter.SetCloseForm()
        End If

    End Sub

End Class

代码:FrmMsgWaitingFor (部分代码参考 水如烟的)
Public Class FrmMsgWaitingFor

    Protected Delegate Sub SetControlText(ByVal ctr As Control,ByVal s As String)

    Public Sub New(e As eWaitingFormType)
        InitializeComponent()
        Select Case e
            Case eWaitingFormType.Message
                HideWorkingImg()
                Me.ProgressBarMain.Visible = False
            Case (eWaitingFormType.Progress)
                HideWorkingImg()
                Me.LabelMessage.Visible = False
            Case eWaitingFormType.MessageAndProgress
                HideWorkingImg()
            Case eWaitingFormType.MessageAndWorking
                Me.ProgressBarMain.Visible = False
        End Select
    End Sub

    Private Sub HideWorkingImg()
        Me.PicWorking.Visible = False
        Me.PicWorking.Image = nothing
    End Sub

    Protected Sub OnSetControlText(ByVal ctr As Control,ByVal s As String)
        If ctr.Invokerequired Then
            Dim m As New SetControlText(AddressOf OnSetControlText)
            Me.Invoke(m,New Object() {ctr,s})
        Else
            ctr.Text = s
        End If
        Application.DoEvents()
    End Sub

    Public Sub SetFormText(ByVal s As String)
        OnSetControlText(Me,s)
    End Sub

    Public Sub SetMessage(ByVal s As String)
        OnSetControlText(Me.LabelMessage,s)
    End Sub

    Public Sub SetMessage(ByVal format As String,ByVal args() As Object)
        SetMessage(String.Format(format,args))
    End Sub

    Public Sub SetCloseForm()
        If Me.Invokerequired Then
            Dim mi As New MethodInvoker(AddressOf SetCloseForm)
            Me.Invoke(mi)
        Else
            Me.Close()
        End If
    End Sub

End Class

--执行界面:



可以收工了,不过还要以下问题没处理:

1.由于程序调用: Application.Run(_frmWaiter) 是在当前线程上运行标准应用程序消息循环,主界面会暂时失去焦点。应该有办法解决。不过可以同时允许多个等待提示窗体程序,等窗体关闭后,主界面重新得到焦点。

2.进度条没有处理Stype为:Blocks 百分比显示效果

3.其他跨线程等问题。

4.如果有人想到:说到底还不如在后台执行工作中,发送信息给主线程更新UI不就得了,那么你有可能没明白我想表达的意思。

5.其他扩展,等待提示窗体在一定时间后自动关闭,窗体的控制,美化,位置控制等,如在右下角提示

原文地址:https://www.jb51.cc/vb/258274.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐