ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 Excel Home精品图文教程库
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
300集Office 2010微视频教程 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 3166|回复: 12

《别怕》示例5.5.1(奇怪的跳转)

[复制链接]

TA的精华主题

TA的得分主题

发表于 2012-10-23 17:26 | 显示全部楼层 |阅读模式
本帖最后由 冬日暖暖 于 2012-10-23 17:30 编辑

以下代码包含两个Sub,第一个Sub只是赋值,为的是能使得第二个Sub运作使用。

Option Explicit
Sub Edit()
        Dim var As String
        Let var = "wjh"
        Range("C6").Value = var
End Sub

Private Sub Worksheet_Change(ByVal Target As Range) 【代码20】
    If Application.Intersect(Target, Range("C3:C65536")) Is Nothing Or Target.Count > 1 Then 【代码1】
        Exit Sub 【代码2】
    End If 【代码3】

    Dim i As Integer
    i = 3
    Do While Cells(i, "I").Value <> "" 【代码4】
        If UCase(Target.Value) = Cells(i, "I").Value Then
Rem    Application.EnableEvents = False
            Target.Value = Cells(i, "I").Offset(0, 1).Value 【代码5】
            Target.Offset(0, -1).Value = Date 【代码6】
            Target.Offset(0, 1) = Cells(i, "I").Offset(0, 2).Value 【代码7】
            Target.Offset(0, 2) = Cells(i, "I").Offset(0, 3).Value 【代码8】
            Target.Offset(0, 3).Select  【代码9】
            Application.EnableEvents = True 【代码10】
            Exit Sub 【代码11】
        End If
        i = i + 1
    Loop
End Sub【代码21】

起因: 我想观察Application.EnableEvents = False这句代码在程序中起到的作用。
发现如果注释此代码,不影响程序运行。所以添加了第一个代码,为的是看一下具体步骤是怎样的。


步骤简述:
1.C6输入“wjh”,运行到代码1,跳转至代码3。然后到代码4。
2.满足IF条件,运行代码5。触发Worksheet_Change事件,跳转至代码20。(即第二个Sub开始处)
3.运行到代码1,跳转至代码3。然后到代码4。
4.不满足IF条件,跳至END IF处。
5.循环Do While,步骤代码4→判断IF→不满足跳至END IF,直到结束循环。(备注:循环4遍Do While操作,因为循环到i=8的时候,单元格为空,结束Do While语句)
6.跳至代码21。(即第二个Sub结尾处)
PS: 已经运行到了程序结尾处End Sub处,我以为到这里就结束了,
然后总结此代码中Application.EnableEvents = False这句命令是必不可少的。 但是...

7.跳至代码6。触发Worksheet_Change事件,跳转至代码20。
8.运行到代码1,跳转至代码2(即执行End Sub)。
9.跳至代码7。触发Worksheet_Change事件,跳转至代码20。
10.运行到代码1,跳转至代码2(即执行End Sub)。
11.跳至代码8。触发Worksheet_Change事件,跳转至代码20。
12.运行到代码1,跳转至代码2(即执行End Sub)。
13.跳至代码9。触发Worksheet_Change事件,跳转至代码20。
14.运行到代码1,跳转至代码2(即执行End Sub)。
15.跳至代码10。没有改动单元格,继续执行代码11。
16.最后代码结束在第一个Sub结尾处。(黄色背景色标出)

提问:为什么不在步骤6处结束,而是会继续执行步骤7至步骤16 ?

附件为示例5.5.1的Excel文件。(来源Excel Home)



5.5.1 一举多得,快速录入数据.rar

160.17 KB, 下载次数: 36

示例5.5.1

TA的精华主题

TA的得分主题

发表于 2012-10-24 11:25 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖最后由 hehex 于 2012-10-24 11:28 编辑

因为你注释了Application.EnableEvents = False 这句命令,因此当你输入条件为文具盒时,自动更新的日期啊,商品代码啊,单价啊这些信息也引发了Worksheet_Change 事件,但是这些更新都不在C 列,所以被If 判断给过滤出去直接就Exit sub 了,一共更新了3次,就触发了3次,这就是所谓的跳转。跟踪一遍代码你就会发现,Worksheet_change 事件能执行到End sub 只有一次,就是有效的那次。剩下那3次奇怪的跳转都是无效的,被if 过滤出去exit sub 的,这就是写下Application.EnableEvents = False  的用意,要避免出现的状况。
至于最后为什么是在黄色的End Sub 结束,是因为事件sub 是你用代码激发的,所以最终要返回到最初的进程。

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-10-24 11:46 | 显示全部楼层
hehex 发表于 2012-10-24 11:25
因为你注释了Application.EnableEvents = False 这句命令,因此当你输入条件为文具盒时,自动更新的日期啊, ...

请问,第一次跳转到代码21End Sub处,为什么不结束呢?而是跳转到代码6,以至于触发worksheet_change事件?为什么你说3次奇怪的跳转都是无效的?这里的无效的含义是什么呢?

TA的精华主题

TA的得分主题

发表于 2012-10-24 12:26 | 显示全部楼层
冬日暖暖 发表于 2012-10-24 11:46
请问,第一次跳转到代码21End Sub处,为什么不结束呢?而是跳转到代码6,以至于触发worksheet_change事件 ...

首先请你确认一件事,确认Application.EnableEvents = False 是被注释的。
如果你把这句话前面的注释点掉,那么所有的所谓奇怪的跳转都不会发生,程序也如你所说的在代码21处跳到黄色标记处然后结束,你不妨这样试验一下。

然后试解答你的第2个问题,为什么在代码21处程序没有结束。分析事件代码,首先从头看,进入if 判断,为假,这里我们先向下看,另一个if 是确保你输入的助记符号"wjh"之类的是正确的存在于代码表里的并且自动切换为大写,当为真时,就是代码5这一句是向D 列相同行的单元格输入代码表内的名称“文具盒”。这句话触发Worksheet_change 事件没有,如果 Application.EnableEvents = False没有执行,这句话就已经触发了事件。okay, 继续分析下一句代码6,在B 列相关单元格输入当前日期,同理也触发了事件。代码7,代码8我想就不用逐句分析了,都触发了事件。这就是在第2个End sub 之后要触发4次(上贴我还看错了)Worksheet_Change 事件,这点明白了吗?

最后你的第3个问题,为什么这些都是无效的。回头看Worksheet_change 事件中代码1 部分,
If Application.Intersect(Target, Range("C3:C65536")) Is Nothing Or Target.Count > 1 Then
分析这条语句是什么意思呢,第一个条件是判断发生Change 事件的Target 对象不是在C 列,第2个条件是判断选中输入内容的单元格多于1个,Or 判断,就是这2个条件任意一个为真,那么if 判断为真,就执行下面的语句:exit sub 直接退出事件过程,说明是无效的单元格变动和本程序不符。

OKay, 我们回头看看上面说过的程序在End sub 之后的4次事件激活情况,一次是在D 列,一次在B 列,另外两次不细说了,反正都不在C 列,所以都被事件开头的if 判断给过滤掉了,直接退出事件过程了。因此说它们是无效的。
            



            

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-10-24 13:28 | 显示全部楼层
本帖最后由 冬日暖暖 于 2012-10-24 13:29 编辑
hehex 发表于 2012-10-24 12:26
首先请你确认一件事,确认Application.EnableEvents = False 是被注释的。
如果你把这句话前面的注释点掉 ...


首先我确认Application.EnableEvents = False 是被注释的。
目的是观察为什么注释掉这句代码之后,程序依然得到正确的结果。

另外,针对您回到我的第2个问题。
然后试解答你的第2个问题,为什么在代码21处程序没有结束。分析事件代码,首先从头看,进入if 判断,为假,这里我们先向下看,另一个if 是确保你输入的助记符号"wjh"之类的是正确的存在于代码表里的并且自动切换为大写,当为真时,就是代码5这一句是向D 列相同行的单元格输入代码表内的名称“文具盒”。这句话触发Worksheet_change 事件没有,如果 Application.EnableEvents = False没有执行,这句话就已经触发了事件。【备注:我理解这里触发了事件。】okay, 继续分析下一句代码6备注:我理解代码6以及接下来的步骤。我不理解的是,为什么会执行到代码6,而不是结束。

TA的精华主题

TA的得分主题

发表于 2012-10-24 16:25 | 显示全部楼层
我不理解的是,为什么会执行到代码6,而不是结束。
我有点绕糊涂了,不太明白你的问题到底是什么了。
针对这句红色的话,请你再次详细看代码,代码6  Target.Offset(0, -1).Value = Date  这句话永远执行在事件End Sub 前面,为什么会在这里结束呢?

7.跳至代码6。触发Worksheet_Change事件,跳转至代码20。

你指的是这句?请你重新跟踪一遍代码,这句话一定不对的,不可能跳转到Target.Offset(0, -1).Value = Date  这里。
单步跟踪程序 应该直接跳到事件开始位置,就是你指的代码20。

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-10-24 17:24 | 显示全部楼层
本帖最后由 冬日暖暖 于 2012-10-24 17:25 编辑
hehex 发表于 2012-10-24 16:25
我不理解的是,为什么会执行到代码6,而不是结束。
我有点绕糊涂了,不太明白你的问题到底是什么了。
针对 ...


回答您的第1个问题:代码6是在IF判断语句中,当不满足条件时候,不会被执行到的吧。
回答您的第2个问题:我也很奇怪为什么在调转到End Sub(代码21)之后,会直接跳转到代码6。但是我能确定的确是这样的,我是按照程序逐步进行写的帖子。(或者能麻烦你下载一下压缩包吗?这个我还真不知道要怎么再具体描述了。)

再次简述一下具体步骤(忽略前面部分):
1.程序执行到代码5处,判断C6录入的数据"wjh",符合要求执行了代码5。将C6的答案更新为“文具盒”。触发时间调到代码20(即事件开始的位置)。
2.判断IF语句,因为改变在C列,所以跳到End If(代码3)。
3.进入Do While循环。然后判断IF语句,由于不满足条件(即判断的区别没有“文具盒”这个值)。所以循环了几次之后(从IF 直接到 End If),直到Do While的判断句不成立(即出现了空值),完成循环,调到End Sub(代码21)处。【再次重申我的问题:程序走到这里为什么不结束。而是直接跳转到了代码6。】

我的压缩包.rar

18.36 KB, 下载次数: 11

示例5.5.1

TA的精华主题

TA的得分主题

发表于 2012-10-24 18:05 | 显示全部楼层
本帖最后由 hehex 于 2012-10-24 22:29 编辑

确实是这样,看来我前面对事件的激活理解是有误差的。
初步跟踪程序后发现,excel 中当WorkSheet_change 事件激活时,语句是会先执行事件激活程序

现在时间不够了,我有事情,等等晚上或者明天有时间再好好跟踪一下程序的运行,详细研究一下。

TA的精华主题

TA的得分主题

发表于 2012-10-24 22:25 | 显示全部楼层
本帖最后由 hehex 于 2012-10-24 22:27 编辑

经过反复单步追踪代码,我基本上懂得激发事件的过程了:
Option Explicit
Sub Edit()
        Dim var As String
        Let var = "wjh"
        Range("C6").Value = var
End Sub

Private Sub Worksheet_Change(ByVal Target As Range) 【代码20】
'=〉这里添加一句代码
    debug.print Target.value ' 意思是在立即窗口打印引发事件的Target 对象的值
    If Application.Intersect(Target, Range("C3:C65536")) Is Nothing Or Target.Count > 1 Then 【代码1】
        Exit Sub 【代码2】
    End If 【代码3】

    Dim i As Integer
    i = 3
    Do While Cells(i, "I").Value <> "" 【代码4】
        If UCase(Target.Value) = Cells(i, "I").Value Then
Rem    Application.EnableEvents = False
            Target.Value = Cells(i, "I").Offset(0, 1).Value 【代码5】
            Target.Offset(0, -1).Value = Date 【代码6】
            Target.Offset(0, 1) = Cells(i, "I").Offset(0, 2).Value 【代码7】
            Target.Offset(0, 2) = Cells(i, "I").Offset(0, 3).Value 【代码8】
            Target.Offset(0, 3).Select  【代码9】
            Application.EnableEvents = True 【代码10】
            Exit Sub 【代码11】
        End If
        i = i + 1
    Loop
End Sub【代码21】

Okay, 我们来单步执行这段代码,从Sub edit() 开始,第一次激活事件的时候,你会发现 Target.value="wjh",这点也是我们需要的,就是所谓的助记符。这次激活事件我们称为事件A,一路往下执行,
到代码5这里,因为Rem    Application.EnableEvents = False, 所以代码5激活了事件。这次激活我们称为事件B激活事件后,立刻返回了事件sub 的开头就是
Private Sub Worksheet_Change(ByVal Target As Range) 【代码20】 这里,跟踪Target.value 里面的值是"文具盒",因为是更新的值在C 列,符合条件所以事件开头的if 判断不会拦截。
所以代码继续执行到do while 循环里的第2个if 判断,由于"文具盒" 是全称不是助记符,因此每次if 判断都是False, 直接执行End if 直到循环结束到事件结束。事件B 结束了
戏骨来了,那么你的问题是为什么程序没有到此结束呢,因为事件A还没有结束,还要继续执行,所以执行代码6,代码6再次激活了事件,我们称为事件C,激活事件C 的Target.value 是当前日期,这个不在C 列在B 列,因此被事件的第一个IF 所拦截执行Exit Sub 事件C结束了,但是事件A 还没有结束,还要继续执行下一句就是代码7,代码7激活了事件D,代码8激活事件E 这2个都和事件C 是完全一样的,当事件E 结束的时候,事件A 还没有结束,但是后面的语句不会再激活事件了,就一路执行下去直到事件A 结束,再返回Sub edit() 中,赋值的下一条语句直到程序结束。

由上面分析可以知道,VBA 的事件激活是先激活事件程序,在激活事件结束之后再返回激活点的下一条语句继续执行。这个有点像堆栈,后进先出。
感谢楼主的深入探索精神,由此也纠正了我以前的错误认识。{:soso_e117:}{:soso_e120:}{:soso_e182:}
            

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-10-25 09:26 | 显示全部楼层
hehex 发表于 2012-10-24 22:25
经过反复单步追踪代码,我基本上懂得激发事件的过程了:
Option Explicit
Sub Edit()

非常感谢你的解释,我大体明白了。
原来奇怪的跳转是因为第一个Sub Edit()并没有结束。也解释了,为什么最后是最后代码结束是在第一个Sub处。

不过有一处我不大明白。事件A没有结束,所以执行代码6。那么为什么是代码6?
难道是因为,程序始终没有执行到代码6,所以才会在跳出End Sub之后直接执行没有执行过的第一句代码处吗?
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-5-2 10:31 , Processed in 0.042145 second(s), 16 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表