我在上一个问题(如何在用户取消时优雅地退出嵌套子程序的中间?)中使用命令按钮的标题作为状态变量时收到了一些合理的批评反馈.我这样做是因为它很有效,一次只用很少的代码服务两三个目的,但我明白它是如何引起问题的,特别是我最初提出它时略显草率的方式.
我觉得这应该得到自己的讨论,所以这里有相同的想法清理了一点并修改为"正确"(这基本上意味着在一个地方定义字符串,所以你的代码不会开始失败因为你只是改变了命令按钮的文本).我知道我的变量和控件命名约定很差(OK,不存在),所以请提前道歉.但是我想继续关注标题作为状态变量的讨论.
所以我们走了:
' Global variables for this form
Dim DoTheThingCaption(1) As String
Dim UserCancel, FunctionCompleted As Boolean
Private Sub Form_Initialize()
' Define the possible captions (is there a #define equivalent for strings?)
DoTheThingCaption(0) = "Click to Start Doing the Thing"
DoTheThingCaption(1) = "Click to Stop Doing the Thing"
' Set the caption state when form initializes
DoTheThing.Caption = DoTheThingCaption(0)
End Sub
Private Sub DoTheThing_Click() ' Command Button
If DoTheThing.Caption = DoTheThingCaption(0) Then
UserCancel = False ' this is the first time we've entered this sub
Else ' We've re-entered this routine (user clicked on button again
' while this routine was already running), so we want to abort
UserCancel = True ' Set this so we'll see it when we exit this re-entry
DoTheThing.Enabled = False 'Prevent additional clicks
Exit Sub
End If
' Indicate that we're now Doing the Thing and how to cancel
DoTheThing.Caption = DoTheThingCaption(1)
For i = 0 To ReallyBigNumber
Call DoSomethingSomewhatTimeConsuming
If UserCancel = True Then Exit For ' Exit For Loop if requested
DoEvents ' Allows program to see GUI events
Next
' We've either finished or been canceled, either way
' we want to change caption back
DoTheThing.Caption = DoTheThingCaption(0)
If UserCancel = True Then GoTo Cleanup
'If we get to here we've finished successfully
FunctionCompleted = True
Exit Sub '******* We exit sub here if we didn't get canceled *******
Cleanup:
'We can only get to here if user canceled before function completed
FunctionCompleted = False
UserCancel = False ' clear this so we can reenter later
DoTheThing.Enabled = True 'Prevent additional clicks
End Sub '******* We exit sub here if we did get canceled *******
Run Code Online (Sandbox Code Playgroud)
就是这样.是否还有什么真正是不好做这种方式?这只是一个风格问题吗?还有什么东西可以以更令人满意或可维护的方式给我这四件事吗?
我可以看到一个问题可能是代码和GUI之间的紧密耦合(在几个方面),所以我可以看到这对于大型项目(或至少是大型GUI)来说可能会成为一个大问题.这恰好是一个较小的项目,只有2或3个按钮可以接受这种"治疗".
这种技术的最大问题是它使用字符串作为布尔值.根据定义,布尔变量只能有两个状态,而字符串可以有任意数量的状态.
现在,您通过依赖一组预定义字符串来定义命令按钮文本的允许值,从而减轻了这种固有的危险.这留下了一些较小的问题:
在处理像这样的[非常旧的]代码[按钮标题作为变量]时,我遇到的最大问题是全球化是一场噩梦......我不得不移动一个旧的vb6应用程序来使用英语和德语...它需要数周,如果不是几个月.
你正在使用goto的.....还需要一些重构来使代码可读?
**编辑以回应评论我只在每个过程顶部的vb6中使用goto; 在错误goto myErrorHandler上.
然后在proc的最底部我会有一个将错误传递给全局处理程序的衬管,以记录错误.
忽略一般的架构/耦合问题,因为你知道这些问题,你的方法的一个问题是因为VB6控件在你设置属性时做了神奇的事情.您可能认为您只是设置了一个属性,但在许多情况下,您也会引发事件.将复选框值设置为true会触发click事件.在选项卡控件上设置tabindex会导致单击事件.有很多情况.
如果我没记错的话,我也认为有些情况下,如果你设置一个属性,然后立即读取它,你将看不到更新.我相信在看到新值之前必须进行屏幕刷新.
我见过太多可怕的VB6代码,它使用控件属性作为存储.如果您发现这种代码,您将会识别它,因为它分散了对Refresh方法,DoEvents的冗余调用,并且您经常会看到UI被挂起.这通常是由无限循环引起的,其中设置了属性,触发了事件,然后设置了另一个属性,最终有人编写了一行代码,再次更新了第一个属性.
如果这些问题不足以吓到你,那就想想这个.我们中的一些人并不那么聪明.我已经在VB6中编写了超过10年的编码,并亲自编写了大约750K LOC,我一直盯着你上面的例子,我发现很难理解它是怎么回事.假设将来需要阅读代码的所有人都会非常愚蠢,通过编写非常简单的代码让我们感到高兴.
我认为最好将标题文本与处理状态分离。goto 也使得阅读变得困难。这是我的重构版本......
Private Const Caption_Start As String = "Click to Start Doing the Thing"
Private Const Caption_Stop As String = "Click to Stop Doing the Thing"
Private Enum eStates
State_Initialized
State_Running
State_Canceled
State_Completed
End Enum
Private Current_State As eStates
Private Sub Form_Initialize()
DoTheThing.Caption = Caption_Start
Current_State = State_Initialized
End Sub
Private Sub DoTheThing_Click()
If Current_State = State_Running Then
'currently running - so set state to canceled, reset caption'
'and disable button until loop can respond to the cancel'
Current_State = State_Canceled
DoTheThing.Caption = Caption_Start
DoTheThing.Enabled = False
Else
'not running - so set state and caption'
Current_State = State_Running
DoTheThing.Caption = Caption_Stop
'do the work'
For i = 0 To ReallyBigNumber
Call DoSomethingSomewhatTimeConsuming
'at intervals check the state for cancel'
If Current_State = State_Canceled Then
're-enable button and bail out of the loop'
DoTheThing.Enabled = True
Exit For
End If
DoEvents
Next
'did we make it to the end without being canceled?'
If Current_State <> State_Canceled Then
Current_State = State_Completed
DoTheThing.Caption = Caption_Start
End If
End If
End Sub
Run Code Online (Sandbox Code Playgroud)