Vb6 当用户取消时,如何优雅地从嵌套子程序的中间退出?

Vb6 当用户取消时,如何优雅地从嵌套子程序的中间退出?,vb6,Vb6,(我正在使用VB6,但我想这在大多数其他语言中都会出现。) 我有一个GUI按钮,它调用一个需要一两分钟才能完成的例程。我希望不耐烦的用户能够第二次点击按钮,让它在任何时候优雅地退出例程 我使用了一个静态变量使其工作得很好(参见下面的代码),但我正在清理项目,我想将For/Next循环放入它自己的函数中,因为它在项目中的几个不同位置都是必需的 但是这样做会破坏for/next中嵌入的静态标志,所以我需要做一些更改。在我对公共(全球)变量做一些胡思乱想的事情之前,我想我应该问问其他(更聪明的,也许实

(我正在使用VB6,但我想这在大多数其他语言中都会出现。)

我有一个GUI按钮,它调用一个需要一两分钟才能完成的例程。我希望不耐烦的用户能够第二次点击按钮,让它在任何时候优雅地退出例程

我使用了一个静态变量使其工作得很好(参见下面的代码),但我正在清理项目,我想将For/Next循环放入它自己的函数中,因为它在项目中的几个不同位置都是必需的

但是这样做会破坏for/next中嵌入的静态标志,所以我需要做一些更改。在我对公共(全球)变量做一些胡思乱想的事情之前,我想我应该问问其他(更聪明的,也许实际上受过计算机科学教育的)人在面对这个问题时做了什么

所以基本上我的问题是如何复制这个:

Private Sub DoSomething_Click()

  Static ExitThisSub As Boolean ' Needed for graceful exit

  If DoSomething.Caption = "Click To Stop Doing Something" Then
    ExitThisSub = False ' this is the first time we've entered this sub
  Else ' We've re-entered this routine (user clicked on button to stop it)
    ExitThisSub = True ' Set this so we'll see it when we exit this re-entry
    Exit Sub '
  End If


  DoSomething.Caption = "Click To Stop Doing Something"

  For i = 0 To ReallyBigNumber
    Call DoingSomethingSomewhatTimeConsuming
    If ExitThisSub = True Then GoTo ExitThisSubNow
    DoEvents
  Next

  ' The next line was missing from my original example,
  ' prompting appropriate comments
  DoSomething.Caption = "Click To Do Something"

  Exit Sub

ExitThisSubNow:

  ExitThisSub = False ' clear this so we can reenter later
  DoSomething.Caption = "Click To Do Something"

End Sub
当我将for/next循环移到它自己的函数时

我想我会将ExitThisSub更改为一个公共变量,退出一些长时间的计算,从而退出新的for/next sub,然后以相同的方式单击DoSomething


但当我使用全局变量时,我总是觉得自己是个业余爱好者(我也是),有没有更优雅的解决方案?

您需要某种共享变量,以便for循环和按钮可以通信。我将for循环(及其相关代码)放在命令对象中。我的VB已经生锈了,但我认为您可以使用自己的“全局”变量和函数声明模块。您可以将所有代码移动到一个模块中,只需像现在一样检查全局变量


我对您发布的代码示例的主要关注与用户取消无关,而是与其他所有内容有关:您通过读取按钮文本来检查您的运行状态,而不是通过另一种方式进行检查(设置按钮文本是因为运行状态,它应该存储在一个变量中);您使用GOTO来退出for循环,而不是中断(VB有中断吗?),并且您将清理代码放在正常流程之外,而在我看来,无论用户是否取消,它都可以运行。

一个可能的替代方法是将繁重的工作功能卸载到新线程。然后,如果用户想要取消,您可以直接杀死该线程,也可以向该线程发送消息


如上所述,通过按钮名进行切换是一个非常常见的技巧,如果只有几个按钮状态,则非常安全。

我一直使用全局布尔变量,如bUserPressedCancel,以及循环中的DoEvents。优雅,斯梅莱根,它的作品


我同意Shinny先生的观点,即根据标题的价值进行测试不是一个好主意。如果在设计器中更改按钮上的文本,将破坏代码。最好不要依赖文本的措辞来运行代码。

您可以在表单的模块级将变量声明为private。 这不是一个全局变量,而是一个模块级变量。 然后您可以将它传递给您创建的函数,并在函数中检查它

但是要小心这些事件。它基本上意味着允许windows消息循环处理消息。这意味着用户不仅可以再次单击您的按钮,还可以关闭表单并执行其他操作。所以,当您在这个循环中时,您无论如何都需要设置一个模块级变量,因为您需要在表单的QueryLoad和任何事件处理程序中检查它

您还可以使用控件本身的Tag属性来存储排序标志。但我不认为它更优雅。

我也喜欢使用两个不同的按钮。把一个藏起来,把另一个拿出来。这样,取消代码和运行代码在不同的事件处理程序中分离

下面是一些处理卸载方面的示例代码来扩展我的答案。 在这里,如果您通过x停止,它会提示您。如果你通过任务管理器杀人,它会优雅地死去

Option Explicit

Private Enum StopFlag
   NotSet = 0
   StopNow = 1
   StopExit = 2
End Enum

Private m_lngStopFlag As StopFlag
Private m_blnProcessing As Boolean

Private Sub cmdGo_Click()

   Dim lngIndex As Long
   Dim strTemp As String

   m_lngStopFlag = StopFlag.NotSet
   m_blnProcessing = True

   cmdStop.Visible = True
   cmdGo.Visible = False

   For lngIndex = 1 To 99999999

      ' check stop flag
      Select Case m_lngStopFlag

         Case StopFlag.StopNow

            MsgBox "Stopping - Last Number Was " & strTemp
            Exit For

         Case StopFlag.StopExit

            m_blnProcessing = False
            End

      End Select

      ' do your processing
      strTemp = CStr(lngIndex)

      ' let message loop process messages
      DoEvents

   Next lngIndex

   m_lngStopFlag = StopFlag.NotSet
   m_blnProcessing = False
   cmdGo.Visible = True
   cmdStop.Visible = False

End Sub

Private Sub cmdStop_Click()

   m_lngStopFlag = StopFlag.StopNow

End Sub

Private Sub Form_Load()

   m_blnProcessing = False

End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)

   Select Case UnloadMode

      Case vbFormControlMenu, vbFormCode

         If m_blnProcessing Then

            Cancel = True

            If MsgBox("Unload Attempted - Cancel Running Process?", vbOKCancel + vbDefaultButton1 + vbQuestion, "Test") = vbOK Then

               m_lngStopFlag = StopFlag.StopExit

            End If

         End If

      Case Else

         m_lngStopFlag = StopFlag.StopExit
         Cancel = True

   End Select

End Sub

在您需要进行本地化或任何其他需要独立于逻辑更改UI的事情之前,该功能一直有效。我将使用Tag属性或私有模块级变量。然后你可以独立于逻辑改变标题。

如果是我,我会使用两个按钮——一个去,一个停。单击“转到”时,“停止”按钮可见。STOP的Click事件只是隐藏了自己——就是这样

然后,您的循环可以简单地检查停止按钮是否仍然可见。如果不是,那意味着它被点击了,你应该跳出去


您的控件是具有窗体范围的静态对象…

如果您不想进行纯计算,VB6中的线程处理非常复杂。感谢您在按钮名称切换方面为我提供支持!:-)我知道使用按钮文本会带来一些风险,但是用“正确”的方式来做会增加更多的代码。我认为这两个方面最好的是在代码中定义按钮文本并在整个过程中使用它,所以如果它发生变化;它到处都在变化。我知道没有中断命令,但你让我查找它,我应该使用“退出”,谢谢。清理代码在“Exit Sub”之后,因此它不会意外运行。我这样做是因为我看到其他人就是这样做的-从来没有想过它是否理想。你也可以使用控件的tag属性。Exit For和Exit do是VB退出循环的两种方式。@Fred:在你的示例中,看起来清理代码应该在循环结束后运行,不管发生什么,否则按钮文本将不会重置。使用常量来存储按钮文本会有所帮助,但我仍然认为这种模式从根本上是错误的。