当用户取消时,如何从嵌套子程序的中间优雅地退出? [英] How to gracefully exit from the middle of a nested subroutine when user cancels?

查看:28
本文介绍了当用户取消时,如何从嵌套子程序的中间优雅地退出?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(我使用的是 VB6,但我想这会出现在大多数其他语言中.)

(I'm using VB6 but I imagine this comes up in most other languages.)

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

I've got a GUI button that calls a routine that takes a minute or two to complete. I want impatient users to be able to click on the button a second time to have it gracefully exit out of the routine at any point.

我使用了一个静态变量来使这个工作得很好(见下面的代码),但我正在清理项目,我想把 For/Next 循环放到它自己的函数中,因为它在几个不同的地方都需要在项目中.

I used a static variable to make this work pretty well (see code below), but I'm cleaning up the project and I want to put the For/Next loop into its own function, since it's required in several different places in the project.

但是这样做会破坏嵌入在 for/next 中的静态标志,因此我需要进行一些更改.在我对公共(全局)变量做一些愚蠢的事情之前,我想我会问其他(更聪明,也许实际上受过 CS 教育)的人在面对这个问题时做了什么.

But doing that would break my static flag embedded in the for/next, so I need to make some changes. Before I do something hare-brained with public (global) variables, I thought I'd ask what other (smarter, perhaps actually CS educated) people have done when faced with this problem.

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

So basically my question is how do I replicate this:

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 循环移动到它自己的函数时?

When I move the for/next loop to its own function?

我想我会将 ExitThisSub 更改为公共变量 QuitDoingSoManyLongCalculations,它将以相同的方式退出新的 for/next sub 和 DoSomething_Click.

I'm thinking I'll change ExitThisSub to a public variable QuitDoingSoManyLongCalculations that will exit the new for/next sub and then the DoSomething_Click in the same way.

但是当我使用全局变量时,我总是觉得自己很业余(我就是这样)——有没有更优雅的解决方案?

But I always feel like an amateur (which I am) when I use global variables - is there a more elegant solution?

推荐答案

好吧,您可以在表单中将模块级别的变量声明为私有.这不是全局变量,而是模块级变量.然后你可以把它传递给你创建的函数并在函数中检查它.

Well you could declare the variable at module level in the forms as private. That is not a global but a module level variable. Then you could pass it to the function you create and check it in the function.

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

But be careful with the DoEvents. It basically means allow the windows message loop to process messages. This means that not only can the user click your button again, they can close the form and do other things. So when you are in this loop you'll need to set a module level variable anyway as you'll need to check for it in a QueryUnload of the form and in any event handlers.

您还可以使用控件本身的 Tag 属性来存储各种标志.但我不认为那更优雅.

You can also use the Tag property of the control itself to store a flag of sorts. But I don't consider that more elegant.

我也更喜欢使用两个不同的按钮.只需隐藏一个并显示另一个.这样你的取消代码和运行代码就在不同的事件处理程序中分开了.

I also prefer to use two different buttons. Just hide one and show the other. That way your cancel code and run code are separated in different event handlers.

为了扩展我的回答,这里有一些处理卸载方面的示例代码.在这里,如果您通过 x 停止,它会提示您.如果你通过任务管理器杀死,它会优雅地死亡.

To expand on my answer here is some sample code that handles the unloading aspect. Here if you stop via the x it prompts you. If you kill via task manager, it dies gracefully.

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

这篇关于当用户取消时,如何从嵌套子程序的中间优雅地退出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆