写了八年VB才明白,事件驱动比什么框架都香

写了八年VB才明白,事件驱动比什么框架都香 写了八年VB才明白事件驱动比什么框架都香写了这么多年VB最让我感慨的一句话就是VB不是过时了是你没真正懂它。很多人一提VB就摇头觉得这是上个世纪的东西。但说实话在中小项目、内部管理系统、工业控制这些领域VB的事件驱动模型至今没有对手。今天不扯虚的就围绕事件驱动编程这个核心把我这些年踩过的坑、总结出来的实战经验一次性全掏出来。看完这篇你会重新理解VB为什么能活到今天。一、事件驱动编程到底是什么为什么VB把它做到了极致很多人刚学编程的时候写的都是顺序执行的代码第一行干什么第二行干什么按顺序往下走。这种方式在写小脚本的时候没问题但一旦做窗体程序顺序执行就完全不够用了。事件驱动编程的核心逻辑是程序不主动跑等用户来触发。 用户点了按钮按钮的Click事件就响了用户改了文本框Change事件就响了用户关了窗体Unload事件就响了。整个程序的运行节奏完全由用户的操作来决定。VB之所以在这方面做得好是因为它从一开始就是为事件驱动设计的。每个控件都自带一套事件你只需要在对应的事件里写代码不用自己去写消息循环、不用自己去轮询输入。这套机制看起来简单但真正要用好里面的门道非常多。二、窗体加载事件里藏着的大坑1、Form_Load不是万能的很多人习惯把所有初始化代码都往Form_Load里塞加载数据、设置控件属性、读取配置文件……结果程序一启动就卡住了因为所有操作都在主线程里堵着。Form_Load的执行时机是在窗体显示之前但它仍然在UI线程上。如果你在这里面做了耗时操作比如读取大文件、连接数据库窗体就会假死——看着像是没反应其实程序在后台拼命干活。正确的做法是Form_Load只做轻量级初始化耗时操作放到Form_Activate或者Timer里异步执行。举个例子vbPrivate Sub Form_Load() 只做最基本的界面初始化Me.Caption 员工管理系统 v2.0Me.StartUpPosition 1 居中显示 耗时操作不放这里LoadEmployeeDataEnd SubPrivate Sub Form_Activate() 窗体已经显示了这时候再加载数据用户体验好很多Timer1.Interval 100Timer1.Enabled TrueEnd SubPrivate Sub Timer1_Timer()Timer1.Enabled FalseDim conn As ADODB.ConnectionSet conn New ADODB.Connectionconn.Open ProviderMicrosoft.Jet.OLEDB.4.0;Data Sourcedata.mdbDim rs As ADODB.RecordsetSet rs New ADODB.Recordsetrs.Open SELECT * FROM Employees, conn, 1, 1Do While Not rs.EOFList1.AddItem rs(Name).Valuers.MoveNextLooprs.Closeconn.CloseSet rs NothingSet conn NothingEnd Sub这段代码把数据库读取放到了Timer的第一次触发里窗体先闪出来数据随后加载用户感知到的就是秒开。2、Form_Initialize和Form_Load的区别很多人分不清这两个事件。简单说Initialize先执行Load后执行。 Initialize的时候控件还没创建完你在这里访问控件属性会报错。Load的时候控件已经全部就绪了可以安全操作。所以如果你需要在初始化阶段就设置某些全局变量用Initialize如果需要操作控件老老实实等Load。三、按钮事件里最容易犯的三个错误1、所有逻辑堆在一个Click事件里这是新手最常见的问题。一个保存按钮的Click事件里写了验证、写库、发通知、刷新列表……几百行代码全挤在一起。后期要改一个功能得在几百行里翻半天。正确的做法是把每个步骤拆成独立的子过程vbPrivate Sub cmdSave_Click()If Not ValidateInput() Then Exit SubIf Not SaveToDatabase() Then Exit SubRefreshListMsgBox 保存成功, vbInformationEnd SubPrivate Function ValidateInput() As BooleanIf Trim(txtName.Text) ThenMsgBox 姓名不能为空, vbExclamationtxtName.SetFocusValidateInput FalseExit FunctionEnd IfIf Not IsNumeric(txtAge.Text) ThenMsgBox 年龄必须是数字, vbExclamationtxtAge.SetFocusValidateInput FalseExit FunctionEnd IfValidateInput TrueEnd FunctionPrivate Function SaveToDatabase() As BooleanOn Error GoTo ErrHandlerDim conn As ADODB.ConnectionSet conn New ADODB.Connectionconn.Open ProviderMicrosoft.Jet.OLEDB.4.0;Data Sourcedata.mdbDim sql As Stringsql INSERT INTO Employees (Name, Age, Dept) VALUES ( _Replace(txtName.Text, , ) , txtAge.Text , _cboDept.Text )conn.Execute sqlconn.CloseSet conn NothingSaveToDatabase TrueExit FunctionErrHandler:MsgBox 保存失败 Err.Description, vbCriticalSaveToDatabase FalseEnd Function这样一拆每个函数职责单一出了问题一眼就能定位。2、忘了处理取消操作很多保存对话框用户点了取消或者右上角的关闭按钮程序照样执行保存逻辑。原因就是没判断DialogResult或者没处理QueryUnload事件。vbPrivate Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)If Me.Dirty Then Dirty是自定义属性表示数据有修改Dim result As VbMsgBoxResultresult MsgBox(数据已修改是否保存, vbYesNoCancel vbQuestion, 提示)If result vbCancel ThenCancel True 取消关闭ElseIf result vbYes ThencmdSave_ClickEnd IfEnd IfEnd Sub3、按钮事件里直接写SQL拼接这是安全隐患最大的写法。上面的代码里我用了Replace来处理单引号但这远远不够。真正的项目里一定要用参数化查询或者存储过程否则SQL注入一打一个准。四、文本框事件的实战应用1、Change事件比KeyPress事件好用十倍很多人喜欢用KeyPress来做输入限制比如只允许输入数字vbPrivate Sub txtAge_KeyPress(KeyAscii As Integer)If KeyAscii 48 Or KeyAscii 57 ThenKeyAscii 0End IfEnd Sub这段代码能用但有个问题粘贴进来的内容它管不了。用户直接CtrlV粘贴一段文字KeyPress根本不会触发。更靠谱的做法是用Change事件做后置校验vbPrivate Sub txtAge_Change()Dim cleanVal As StringcleanVal Dim i As IntegerFor i 1 To Len(Me.txtAge.Text)Dim ch As Stringch Mid(Me.txtAge.Text, i, 1)If ch Like [0-9] ThencleanVal cleanVal chEnd IfNext iMe.txtAge.Text cleanValEnd Sub不管用户是键盘输入还是粘贴这段代码都能把非数字字符过滤掉。2、LostFocus事件做字段级校验Change事件是每输入一个字符就触发适合做实时过滤。LostFocus事件是控件失去焦点时触发适合做整体校验。比如一个邮箱输入框vbPrivate Sub txtEmail_LostFocus()Dim email As Stringemail Trim(Me.txtEmail.Text)If email And Not email Like **.* ThenMsgBox 邮箱格式不正确, vbExclamationMe.txtEmail.SetFocusMe.txtEmail.SelStart 0Me.txtEmail.SelLength Len(email)End IfEnd Sub用户填完邮箱点击其他控件时自动校验格式不对就弹提示并让光标回到输入框体验比点提交才报错好太多了。五、列表控件事件的高效处理1、ListView的ItemClick事件比双击好用很多人用ListView都是双击打开详情但双击的问题是用户想选中一行但不小心双击了就直接跳转了体验很差。更好的做法是单击选中双击才打开。vbPrivate Sub lvwData_ItemClick(ByVal Item As MSComctlLib.ListItem) 单击只高亮不做其他操作lvwData.SelectedItem ItemEnd SubPrivate Sub lvwData_DblClick()If lvwData.SelectedItem Is Nothing Then Exit SubDim empID As LongempID CLng(lvwData.SelectedItem.Tag)frmDetail.EmployeeID empIDfrmDetail.Show vbModalEnd Sub把ID存在Tag属性里双击时取出来传给详情窗体整个流程非常干净。2、ComboBox的DropDown事件做动态加载当ComboBox的数据来自数据库且数据量很大时不要在Form_Load里全部加载那样启动会很慢。用DropDown事件在用户点击下拉的瞬间才去查数据vbPrivate Sub cboCity_DropDown()If Me.cboCity.ListCount 0 ThenDim conn As ADODB.ConnectionSet conn New ADODB.Connectionconn.Open ProviderMicrosoft.Jet.OLEDB.4.0;Data Sourcedata.mdbDim rs As ADODB.RecordsetSet rs conn.Execute(SELECT DISTINCT City FROM Employees ORDER BY City)Do While Not rs.EOFMe.cboCity.AddItem rs(City).Valuers.MoveNextLooprs.Closeconn.CloseEnd IfEnd Sub用户不点下拉就不查库启动速度提升非常明显。六、窗体级别事件的高级用法1、用Resize事件做自适应布局很多程序在不同分辨率下界面会乱掉原因是控件位置写死了。用Resize事件可以让窗体自适应vbPrivate Sub Form_Resize()If Me.WindowState 1 Then 最小化时不处理cmdSave.Move Me.ScaleWidth - cmdSave.Width - 120, cmdSave.Top, 100, 300lvwData.Move 120, 400, Me.ScaleWidth - 240, Me.ScaleHeight - 500End IfEnd Sub窗口一拉大控件自动跟着动不用手动拖。2、用Unload事件做清理工作窗体关闭时一定要释放对象否则内存泄漏是迟早的事vbPrivate Sub Form_Unload(Cancel As Integer)On Error Resume NextIf Not rs Is Nothing ThenIf rs.State 1 Then rs.CloseSet rs NothingEnd IfIf Not conn Is Nothing ThenIf conn.State 1 Then conn.CloseSet conn NothingEnd IfSet Timer1 NothingEnd Sub七、写在最后事件驱动编程说白了就一句话你的代码不是自己跑的是被用户牵着走的。 理解了这一点你写VB的思路就完全不一样了。不再是我要让程序做什么而是用户做了什么之后程序该怎么响应。这个思维转变比学任何新技术都重要。VB的事件模型虽然老但它背后的设计思想放到今天依然是主流。React的事件处理、Android的onClick、iOS的IBAction本质上都是事件驱动。所以别再说VB过时了。它不是过时是你没学到家。注意本文所介绍的软件及功能均基于公开信息整理仅供用户参考。在使用任何软件时请务必遵守相关法律法规及软件使用协议。同时本文不涉及任何商业推广或引流行为仅为用户提供一个了解和使用该工具的渠道。你在生活中时遇到了哪些问题你是如何解决的欢迎在评论区分享你的经验和心得希望这篇文章能够满足您的需求如果您有任何修改意见或需要进一步的帮助请随时告诉我感谢各位支持可以关注我的个人主页找到你所需要的宝贝。博文入口山峰哥-CSDN博客 复制到【浏览器】打开即可,宝贝入口夸克网盘分享 宝贝夸克网盘分享作者郑重声明本文内容为本人原创文章纯净无利益纠葛如有不妥之处请及时联系修改或删除。诚邀各位读者秉持理性态度交流共筑和谐讨论氛围