gpt4 book ai didi

vb.net - 创建一个在另一个线程上运行的进度条,同时在主线程中保持计算

转载 作者:行者123 更新时间:2023-12-04 04:14:35 25 4
gpt4 key购买 nike

前言:我知道这是一种不寻常/不正确的方法。我可以使用“真正的” ShowDialog()、后台工作线程/线程等来完成此操作。我不是在寻求帮助这样做;我正在尝试具体做我在这里描述的事情,即使它很丑陋。如果由于 X 原因这是不可能的,请告诉我。

我为一些长期运行的操作创建了一个精美的进度对话框。我需要在新线程上显示此对话框,同时在调用(大多数情况下为 UI)线程上继续处理。

这有 3 个实际要求:

  • 防止用户与调用表单交互(类似于 ShowDialog(this))
  • 将进度对话框保持在主窗口上方(现在可能会落后)
  • 允许主线程继续处理

  • 我所拥有的看起来像这样(到目前为止,就运行而言,工作正常,除了上述那些问题):
    Using ... ShowNewProgressDialogOnNewThread() ...
    Logic
    UpdateProgress() //static
    Logic
    UpdateProgress() //static, uses Invoke() to call dialog
    ...
    End Using // destroys the form, etc

    我尝试了几种方法来做到这一点:
  • 显示对话框 () 上 后台工作人员 /主题
  • Action.BeginInvoke() 调用函数
  • ProgressForm.BeginInvoke( ... 调用 ShowDialog 的方法... )
  • 将主窗体包装在一个实现 IWin32Window 的类中,这样它就可以被跨线程调用并传递给 ShowDialog() - 这个在稍后的某个地方失败了,但至少会导致 ShowDialog() 不会立即 barf。

  • 关于如何进行这项工作的任何线索或智慧?

    解决方案(暂时)
  • 对 EnableWindow 的调用正是我想要的。
  • 我根本没有遇到任何崩溃
  • 改为使用 ManualResetEvent
  • 我设置了 TopMost,因为我不能总是保证表单会以其他方式结束。也许有更好的方法。
  • 我的进度表就像一个闪屏(没有大小调整,没有工具栏等),这也许是没有崩溃的原因(在回答中提到)
  • 这是another thread on the EnableWindow topic (没有引用此修复程序,不过)
  • 最佳答案

    让进度窗口始终显示在(死)表单顶部是一项艰巨的要求。这通常通过使用 Form.Show(owner) 重载来处理。它会在您的情况下造成麻烦,WF 不会欣赏属于另一个线程的所有者表单。这可以通过 P/调用 SetWindowLong() 来设置所有者来解决。

    但是现在出现了一个新问题,当它尝试向其所有者发送消息时,进度窗口就会立即停止运行。有点令人惊讶的是,当您使用 Invoke() 而不是 BeginInvoke() 来更新进度时,这个问题就会消失。有点,您仍然可以通过将鼠标移到残疾所有者的边界上来解决问题。实际上,您必须使用 TopMost 来确定 Z 顺序。更现实的是,Windows 只是不支持您尝试执行的操作。您知道真正的解决方法,它是您问题的首要问题。

    这是一些要进行实验的代码。它假设您的进度表称为 dlgProgress:

    Imports System.Threading

    Public Class ShowProgress
    Implements IDisposable
    Private Delegate Sub UpdateProgressDelegate(ByVal pct As Integer)
    Private mOwnerHandle As IntPtr
    Private mOwnerRect As Rectangle
    Private mProgress As dlgProgress
    Private mInterlock As ManualResetEvent

    Public Sub New(ByVal owner As Form)
    Debug.Assert(owner.Created)
    mOwnerHandle = owner.Handle
    mOwnerRect = owner.Bounds
    mInterlock = New ManualResetEvent(False)
    Dim t As Thread = New Thread(AddressOf dlgStart)
    t.SetApartmentState(ApartmentState.STA)
    t.Start()
    mInterlock.WaitOne()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
    mProgress.BeginInvoke(New MethodInvoker(AddressOf dlgClose))
    End Sub

    Public Sub UpdateProgress(ByVal pct As Integer)
    mProgress.Invoke(New UpdateProgressDelegate(AddressOf dlgUpdate), pct)
    End Sub

    Private Sub dlgStart()
    mProgress = New dlgProgress
    mProgress.StartPosition = FormStartPosition.Manual
    mProgress.ShowInTaskbar = False
    AddHandler mProgress.Load, AddressOf dlgLoad
    AddHandler mProgress.FormClosing, AddressOf dlgClosing
    EnableWindow(mOwnerHandle, False)
    SetWindowLong(mProgress.Handle, -8, mOwnerHandle)
    Application.Run(mProgress)
    End Sub

    Private Sub dlgLoad(ByVal sender As Object, ByVal e As EventArgs)
    mProgress.Location = New Point( _
    mOwnerRect.Left + (mOwnerRect.Width - mProgress.Width) \ 2, _
    mOwnerRect.Top + (mOwnerRect.Height - mProgress.Height) \ 2)
    mInterlock.Set()
    End Sub

    Private Sub dlgUpdate(ByVal pct As Integer)
    mProgress.ProgressBar1.Value = pct
    End Sub

    Private Sub dlgClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs)
    EnableWindow(mOwnerHandle, True)
    End Sub

    Private Sub dlgClose()
    mProgress.Close()
    mProgress = Nothing
    End Sub

    '--- P/Invoke
    Public Shared Function SetWindowLong(ByVal hWnd As IntPtr, ByVal nIndex As Integer, ByVal dwNewLong As IntPtr) As IntPtr
    If IntPtr.Size = 4 Then
    Return SetWindowLongPtr32(hWnd, nIndex, dwNewLong)
    Else
    Return SetWindowLongPtr64(hWnd, nIndex, dwNewLong)
    End If
    End Function

    Private Declare Function EnableWindow Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal enabled As Boolean) As Boolean
    Private Declare Function SetWindowLongPtr32 Lib "user32.dll" Alias "SetWindowLongW" (ByVal hWnd As IntPtr, ByVal nIndex As Integer, ByVal dwNewLong As IntPtr) As IntPtr
    Private Declare Function SetWindowLongPtr64 Lib "user32.dll" Alias "SetWindowLongW" (ByVal hWnd As IntPtr, ByVal nIndex As Integer, ByVal dwNewLong As IntPtr) As IntPtr

    End Class

    示例用法:
      Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Using dlg As New ShowProgress(Me)
    For ix As Integer = 1 To 100
    dlg.UpdateProgress(ix)
    System.Threading.Thread.Sleep(50)
    Next
    End Using
    End Sub

    关于vb.net - 创建一个在另一个线程上运行的进度条,同时在主线程中保持计算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1725810/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com