[警告 - 这是一个针对年轻玩家的陷阱,你通常不会把你的任何UI东西放在单独的线程中运行,因为正确的方法是将你的长处理工作放到后台线程中代替。但是,由于遗留系统的限制,此解决方案是必需的。]
我们有一个相当复杂的应用程序,并希望有一个通用的无模式形式,弹出所有其他(模态和无模式)形式,带有标题(每次要求显示时设置为新)和旋转gif向用户显示正在进行后台处理。这可以从多个地方开始调用,并且所有重叠,最后一次调用应该设置标题/注释。
然而,原始代码并不总是显示表单,并且从未在当前模式表单上方显示:
Private Sub StartLongRunningProcess(comment As String, title As String)
LongRunningProcess.MdiParent = MainForm
'...set title and comment here....
LongRunningProcess.Show()
LongRunningProcess.BringToFront()
DoCenter(LongRunningProcess, MainForm)
MainForm.Refresh()
End Sub
Public Sub HideLongRunningProcess()
LongRunningProcess.Hide()
End Sub
我尝试通过以下方式改进:
使用我开始使用的普通非Mdi无模式格式:
_longRunningProcess = New LongRunningProcess(frmPC9main)
.TopMost = True
.Activate()
代替.BringToFront()
_longRunningProcess
,例如建议的here 几乎有效,除非它有时会显示消息“(Not Responding)”让用户认为强制退出应用程序...当他们有长时间运行的进程时,我们试图避免的确切事情。顺便说一句,我不能把我长时间运行的进程放到后台线程中,因为它们太多了,无论如何都不可能 - 所有它们都需要访问主线程。 这真的应该是一个基本的事情,我错过了什么?
另请注意:除了显示/隐藏表单外,我不需要与其进行任何其他交互。
[编辑]我一直在尝试的线程是:
Dim _longRunningProcessThread As Thread
Private _longRunningProcess As LongRunningProcess = Nothing
Public Sub ShowLongRunningProcess(Optional ByVal comment As String = "", Optional ByVal title As String = "Processing in background")
MainForm.CheckForIllegalCrossThreadCalls = False
If _longRunningProcess Is Nothing Then
_longRunningProcessThread = New Thread(New ThreadStart(Sub() StartLongRunningProcess()))
_longRunningProcessThread.IsBackground = True
_longRunningProcessThread.Start()
Else
_longRunningProcess.setCaption(comment)
_longRunningProcess.setTitle(title)
_longRunningProcess.Show()
_longRunningProcess.Activate()
DoCenter(_longRunningProcess, _mainForm)
_mainForm.Refresh()
_longRunningProcess.Refresh()
Application.DoEvents()
End If
MainForm.CheckForIllegalCrossThreadCalls = True
End Sub
Private Sub StartLongRunningProcess()
If _longRunningProcess Is Nothing Then
_longRunningProcess = New LongRunningProcess(_mainForm)
End If
End Sub
Public Sub HideLongRunningProcess()
MainForm.CheckForIllegalCrossThreadCalls = False
_longRunningProcess.Hide()
MainForm.CheckForIllegalCrossThreadCalls = True
End Sub
现在这将显示在顶部,但是当主线程正在访问数据库时,它显示为(没有响应)。
答案 0 :(得分:1)
这是在单独的线程上运行的代码版本。您在上面提供的代码没有在第二个线程上运行消息循环。同时设置CheckForIllegalCrossThreadCalls = false对于你想要实现的目标来说是一个非常糟糕的主意。
用法:
ProgressForm.Initialise()
ProgressForm.ShowProgressForm("hello", "hello")
Dim modalForm As New Form With {.Text = "MyModalForm"}
modalForm.ShowDialog()
ProgressForm.HideProgressForm()
ProgressForm.TearDown()
这是ProgressForm的定义。
Public Class ProgressForm
Inherits Form
Private Shared sForm As ProgressForm
Private Shared sThread As Thread
Public Sub New()
Me.TopMost = True
Me.BackColor = Color.Green
Me.Text = "Progress"
End Sub
Public Shared Sub Initialise()
'Create the form
sThread = New Thread(AddressOf ThreadFunc)
sThread.Start()
While sForm Is Nothing OrElse sForm.InvokeRequired = False
Thread.Sleep(0)
End While
End Sub
Public Shared Sub TearDown()
sForm.BeginInvoke(Sub() Application.ExitThread())
End Sub
Private Shared Sub ThreadFunc()
sForm = New ProgressForm
'Dim handle = sForm.Handle
Application.Run(sForm)
End Sub
Public Shared Sub ShowProgressForm(caption As String, title As String)
If sForm.InvokeRequired Then
sForm.BeginInvoke(Sub() ShowProgressForm(caption, title))
Else
sForm.Text = title
'TODO: Caption
sForm.Visible = True
sForm.TopMost = True
End If
End Sub
Public Shared Sub HideProgressForm()
If sForm.InvokeRequired Then
sForm.BeginInvoke(Sub() HideProgressForm())
Else
sForm.Visible = False
End If
End Sub
Private Sub ProgressForm_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If e.CloseReason = CloseReason.UserClosing Then
Me.Visible = False
e.Cancel = True
End If
End Sub
End Class
答案 1 :(得分:0)
.TopMost = true
会将其提升到所有其他窗口之上。但是,当您打开模式对话框时,它将窃取事件循环,因此您将无法与该表单进行交互。如果您的表单不需要交互,虽然这种方法应该有效。
这是一个在不同线程上运行表单的快速示例(尽管您不应该使用线程池来执行此类任务)
Threading.ThreadPool.QueueUserWorkItem(Sub()
Dim form As New Form With {.TopMost = True, .Text = "Progress", .BackColor = Color.Red}
Application.Run(form)
End Sub)
Dim modalForm As New Form With {.Text = "MyModalForm"}
modalForm.ShowDialog()