我在上一个问题(如何在用户取消时优雅地退出嵌套子程序的中间?)中使用命令按钮的标题作为状态变量时收到了一些合理的批评反馈.我这样做是因为它很有效,一次只用很少的代码服务两三个目的,但我明白它是如何引起问题的,特别是我最初提出它时略显草率的方式.
我觉得这应该得到自己的讨论,所以这里有相同的想法清理了一点并修改为"正确"(这基本上意味着在一个地方定义字符串,所以你的代码不会开始失败因为你只是改变了命令按钮的文本).我知道我的变量和控件命名约定很差(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 *******
就是这样.是否还有什么真正是不好做这种方式?这只是一个风格问题吗?还有什么东西可以以更令人满意或可维护的方式给我这四件事吗?
用户按下按钮的即时GUI反馈已导致操作
如果不需要采取措施,用户眼睛已经处于如何取消的位置的即时GUI反馈
用户启动/取消操作的一键式方法(减少GUI上的混乱程度)
一个简单的即时命令按钮禁用以防止多个关闭请求
我可以看到一个问题可能是代码和GUI之间的紧密耦合(在几个方面),所以我可以看到这对于大型项目(或至少是大型GUI)来说可能会成为一个大问题.这恰好是一个较小的项目,只有2或3个按钮可以接受这种"治疗".
这种技术的最大问题是它使用字符串作为布尔值.根据定义,布尔变量只能有两个状态,而字符串可以有任意数量的状态.
现在,您通过依赖一组预定义字符串来定义命令按钮文本的允许值,从而减轻了这种固有的危险.这留下了一些较小的问题:
逻辑是低于明确的关于当前和可用状态(实际上有形式的四个可能的状态:未开工,开工,竣工,开工,但消除) - 维修需要按钮文本之间的潜在的相互作用的仔细观察和布尔变量状态,以确定当前状态/应该是什么.单个枚举将使这些国家的明确,使代码更易于阅读和理解,从而简化了维护.
你依靠控件属性(按钮文本)的行为,以保持与暴露的属性值类型(字符串)是一致的.这种假设使得旧的VB6代码迁移到更新的语言/平台绝对地狱.
字符串比较比布尔变量的简单测试慢得多.在这种情况下,这无关紧要.一般来说,避免它同样容易.
你正在使用DoEvents来模拟多线程(与问题没有直接关系......但是,呃).
在处理像这样的[非常旧的]代码[按钮标题作为变量]时,我遇到的最大问题是全球化是一场噩梦......我不得不移动一个旧的vb6应用程序来使用英语和德语...它需要数周,如果不是几个月.
你正在使用goto的.....还需要一些重构来使代码可读?
**编辑以回应评论我只在每个过程顶部的vb6中使用goto; 在错误goto myErrorHandler上.
然后在proc的最底部我会有一个将错误传递给全局处理程序的衬管,以记录错误.
忽略一般的架构/耦合问题,因为你知道这些问题,你的方法的一个问题是因为VB6控件在你设置属性时做了神奇的事情.您可能认为您只是设置了一个属性,但在许多情况下,您也会引发事件.将复选框值设置为true会触发click事件.在选项卡控件上设置tabindex会导致单击事件.有很多情况.
如果我没记错的话,我也认为有些情况下,如果你设置一个属性,然后立即读取它,你将看不到更新.我相信在看到新值之前必须进行屏幕刷新.
我见过太多可怕的VB6代码,它使用控件属性作为存储.如果您发现这种代码,您将会识别它,因为它分散了对Refresh方法,DoEvents的冗余调用,并且您经常会看到UI被挂起.这通常是由无限循环引起的,其中设置了属性,触发了事件,然后设置了另一个属性,最终有人编写了一行代码,再次更新了第一个属性.
如果这些问题不足以吓到你,那就想想这个.我们中的一些人并不那么聪明.我已经在VB6中编写了超过10年的编码,并亲自编写了大约750K LOC,我一直盯着你上面的例子,我发现很难理解它是怎么回事.假设将来需要阅读代码的所有人都会非常愚蠢,通过编写非常简单的代码让我们感到高兴.