创建无模式表单 - 从顶部开始

时间:2016-08-10 04:21:21

标签: .net vb.net winforms

[警告 - 这是一个针对年轻玩家的陷阱,你通常不会把你的任何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

现在这将显示在顶部,但是当主线程正在访问数据库时,它显示为(没有响应)。

2 个答案:

答案 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()