精华内容
下载资源
问答
  • 编程必然要知道事件,比如单击按钮事件,然后编写当事件发生时的处理代码,VB.NET是怎样进行事件处理的呢?可否自定义事件呢?因扩展控件,使其列头增加全选全不选复选框并具备分页功能需要用到自定义事件,详见博文...

    测试环境:windows 7和Microsoft Visual Studio 2015

    编程必然要知道事件,比如单击按钮事件,然后编写当事件发生时的处理代码,VB.NET是怎样进行事件处理的呢?可否自定义事件呢?因扩展控件,使其列头增加全选全不选复选框并具备分页功能需要用到自定义事件,详见博文《VB.NET学习笔记:WinForm自定义DataGridView分页组合控件》、《VB.NET学习笔记:自定义控件之扩展DataGridViewColumnHeaderCell类增加CheckBox全选复选框》,所以拜读了多篇相关事件的博文,结合自己在编程中的使用情况,谈谈我对事件的一些认识。
    一、事件处理——把事件与处理事件的方法关联在一起
    新建一个Windows窗体应用程序项目,在窗体里拉入一个Button控件,将其Name属性值修改为BtnEvent1,Text属性值修改为“按钮1”,然后双击按钮,即可自动生成如下按钮单击事件处理代码:

        Private Sub BtnEvent1_Click(sender As Object, e As EventArgs) Handles BtnEvent1.Click
    
        End Sub
    

    当然也可以在代码窗口里,选择相应的项目名称,及其下相应的控件,就可以选择对应的事件,之后也会自动生成事件处理代码,如下图所示操作。
    添加事件处理过程
    代码比VB6.0里的事件处理代码多了一些内容:
    1、Handles BtnEvent1.Click:表明这个方法应处理BtnEvent1中的Click事件,Handles的目的就是把这个方法与Button类中的Click事件关联起来。
    2、(sender As Object, e As EventArgs) :两个参数sender和e。这是控件类定义的参数,如果参数不对将引发错误。
    参数sender和e到底是个啥呢?我们来测试一下,在事件处理代码中添加如下代码:

        Private Sub BtnEvent1_Click(sender As Object, e As EventArgs) Handles BtnEvent1.Click
            MessageBox.Show(sender.ToString)
            MessageBox.Show(e.ToString)
        End Sub
    

    调试代码,测试结果如图所示:
    sender参数
    e参数
    经在csdn中查询得知:sender参数表示发送者,此处表示按钮。e参数是MouseEventArgs类,继承自EventArgs类,就是将鼠标的动作信息封装在这个类的对象e中。这个对象描述了鼠标点击情况,比如在按钮的那个位置点下了鼠标。我自己也不是很懂,在这里做个记号吧!
    下面代码修改了事件处理的方法名:

    Private Sub Btn1_Click(sender As Object, e As EventArgs) Handles BtnEvent1.Click
            MessageBox.Show(sender.ToString)
            MessageBox.Show(e.ToString)
        End Sub
    

    BtnEvent1_Click修改成了Btn1_Click,如果在VB6.0是没有办法识别这个事件的,单击“按钮1”当然也不会执行此代码。但在vb.net中不影响事件的执行,说明在vb.net中,事件不再依赖方法名。
    我们在窗体中再拉入一个Button控件,将其Name属性值修改为BtnEvent2,Text属性值修改为“按钮2”,然后添加如下按钮单击事件处理代码:

        Private Sub BtnEvent2_Click(sender As Object, e As EventArgs) Handles BtnEvent2.Click
            MessageBox.Show(sender.ToString)
            MessageBox.Show(e.ToString)
        End Sub
    

    BtnEvent2_Click方法里代码与BtnEvent1_Click方法里的代码一模一样,参数也一样,只是关联的事件不同而已,我们可以这两个方法合成一个方法,然后关联到2个事件上:

        Private Sub BtnEvent1_Click(sender As Object, e As EventArgs) Handles BtnEvent1.Click, BtnEvent2.Click
            MessageBox.Show(sender.ToString)
            MessageBox.Show(e.ToString)
        End Sub
    

    Handles关键字可以关联多个事件,用逗号隔开每个事件,每个事件都会导致方法执行。在合并方法时要求每个事件的参数个数及类型必须相同。
    一个方法可以关联多个事件,当然一个事件也是可以引发多个方法。代码如下:

    Private Sub BtnEvent1_Click1(sender As Object, e As EventArgs) Handles BtnEvent1.Click
            Me.Label1.Text = Me.Label1.Text & Environment.NewLine & "方法:BtnEvent1_Click1"
        End Sub
    
        Private Sub BtnEvent1_Click2(sender As Object, e As EventArgs) Handles BtnEvent1.Click
            Me.Label1.Text = Me.Label1.Text & Environment.NewLine & "方法:BtnEvent1_Click2"
        End Sub
    
        Private Sub BtnEvent1_Click3(sender As Object, e As EventArgs) Handles BtnEvent1.Click
            Me.Label1.Text = Me.Label1.Text & Environment.NewLine & "方法:BtnEvent1_Click3"
        End Sub
    

    测试效果如下图所示:
    一个事件引发多个方法
    可见,单击“按钮1”时,每个关联的方法都被执行了。
    除了用Handles关键字关联事件与事件处理方法外,还可以用AddHandler关键字,请看下面代码:

        Private Sub BtnEvent1_Click(sender As Object, e As EventArgs) Handles BtnEvent1.Click
            RemoveHandler TextBox1.TextChanged, AddressOf TextBox1_TextChanged
            Me.TextBox1.Text = sender.ToString
            AddHandler TextBox1.TextChanged, AddressOf TextBox1_TextChanged
        End Sub
    
        Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
            MessageBox.Show("你修改了文本!")
        End Sub
    

    不管是在窗体修改或是编程修改TextBox控件文本都会引发TextChanged事件,上面代码可以先暂停引发该事件,使用到RemoveHandler关键字来把事件跟处理事件的方法分离。

    RemoveHandler TextBox1.TextChanged, AddressOf TextBox1_TextChanged
    

    之后要记得用AddHandler关键字重新将事件TextBox1.TextChanged跟TextBox1_TextChanged方法关联,要不然就真的再不也会引发TextBox控件的TextChanged事件了。

    AddHandler TextBox1.TextChanged, AddressOf TextBox1_TextChanged
    

    学习小结:
    (1)、把事件处理方法和事件关联的方式有两种:
    A、Handles关键字:只要关联某方法后,就会一直关联。我们称其为静态方式。
    B、AddHandler关键字:AddHandler可以在程序运行时动态的添加处理事件的方法,还可以用RemoveHanlder动态地删除处理事件的方法。
    (2)、一个事件可以关联多个方法,一个方法也可以关联多个事件。
    如果事件关联的方法不止一个时,引发事件就会执行全部关联的方法,我们无法干涉和终止,而且也不能预测执行方法的顺序。
    二、自定义事件
    在vb.net中事件可以看成为一种特殊的委托,也就是说可以将sub过程委托给事件,当触发事件时就会执行委托给该事件的sub。
    1、使用自定义事件的步骤
    (1)、声明事件
    事件由委托实现,所以应事先声明一个事件的委托。

        Public Delegate Sub CustomEventHandler(ByVal str As String) '声明一个事件的委托
        Public Event CustomEvent As CustomEventHandler '声明一个自定义事件CustomEvent
    

    也可以用简写,即上述声明等效于:

    Public Event CustomEvent(ByVal str As String) '声明一个自定义事件CustomEvent
    

    注意:声明事件可以带参数,但不能具有返回值、可选参数、ParamArray参数。也就是说不能将function函数委托给事件。
    (2)、触发事件
    使用RaiseEvent关键字来引发事件。
    注意:触发事件时传入的参数必须与声明事件时的参数类型以及个数都要相同。

        Public Sub RaiseCustomEvent(ByVal str As String) '定义触发事件的方法
            RaiseEvent CustomEvent(str) '触发事件
        End Sub
    

    (3)、编写处理事件的Sub方法
    我这里编写的方法名为talk,带一个参数,特别注意的是传入方法的参数必须与声明事件时的参数类型以及个数都要相同。

    Public Sub talk(ByVal str As String)
            Me.TextBox1.Text = str
        End Sub
    

    (4)、把事件和处理事件的方法关联在一起
    可以使用Handles关键字或AddHandler关键字,但使用Handles关键字一定要配合使用WithEvents关键字来声明事件所在的对象,如果不带WithEvents关键字不会报错,但若这个对象真的有事件,也不会引发;当然如果对象本身没有声明的事件,如果强行用WithEvents将会引发语法错误。所以WithEvents和Handles必须成对出现。
    2、WithEvents和Handles关联事件方法
    类代码:

    Public Class ClsCustomEvent '自定义类
        'Public Delegate Sub CustomEventHandler(ByVal str As String) '声明一个事件的委托
        'Public Event CustomEvent As CustomEventHandler '声明一个自定义事件CustomEvent
        Public Event CustomEvent(ByVal str As String) '声明一个自定义事件CustomEvent
        Public Sub RaiseCustomEvent(ByVal str As String) '定义触发事件的方法
            RaiseEvent CustomEvent(str) '触发事件
        End Sub
    End Class
    

    窗体代码:

    Public Class Form1
        Public WithEvents Custom As New ClsCustomEvent() '声明事件对象
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Custom.RaiseCustomEvent("Hello world") '执行ClsCustomEvent类中触发CustomEvent事件的方法
        End Sub
    
        Public Sub talk(ByVal str As String) Handles Custom.CustomEvent '将talk方法委托给ClsCustomEvent类的CustomEvent事件
            Me.TextBox1.Text = str
        End Sub
    End Class
    

    3、AddHandler关联事件方法
    事件所在的类代码:

    Public Class ClsCustomEvent '自定义类
        'Public Delegate Sub CustomEventHandler(ByVal str As String) '声明一个事件的委托
        'Public Event CustomEvent As CustomEventHandler '声明一个自定义事件CustomEvent
        Public Event CustomEvent(ByVal str As String) '声明一个自定义事件CustomEvent
        Public Sub RaiseCustomEvent(ByVal str As String) '定义触发事件的方法
            RaiseEvent CustomEvent(str) '触发事件
        End Sub
    End Class
    

    窗体代码:

    Public Class Form1
        Public Custom As New ClsCustomEvent() '声明事件对象
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            AddHandler Custom.CustomEvent, AddressOf talk '将talk方法委托给ClsCustomEvent类的CustomEvent事件
            Custom.RaiseCustomEvent("Hello world") '执行ClsCustomEvent类中触发CustomEvent事件的方法
        End Sub
    
        Public Sub talk(ByVal str As String)
            Me.TextBox1.Text = str
        End Sub
    End Class
    

    注意: AddHandler与RemoveHandler最好能成对出现,如果不将事件处理程序与事件分离(RemoveHanlder),对象将保留在内存中,即使上面Custom不再指向对象,每个事件仍有对象的一个引用,容易出现内存泄露。而WithEvents会自动处理这些细节。

    本文借鉴与参考了以下博文:
    VB.net学习笔记(七)对象事件的定制
    vb.net中的委托与事件
    学习过程得到网友uruseibest的帮助,表示感谢!

    展开全文
  • 当你们听到VB事件驱动.......你们想到的答案是什么呢?无论你们想到什么样的答案...我在这里都详细加实例解说一下...其实很简单,不管你明不明白这个词的涵义...其实在你打开VB写程序的第一天开始你都在用...转载...
    各位在使用VB的正规或不正规的程序员们...当你们听到VB的事件驱动.......你们想到的答案是什么呢?
    无论你们想到什么样的答案...我在这里都详细加实例解说一下...其实很简单,不管你明不明白这个词的涵义...其实在你打开VB写程序的第一天开始你都在用...

    转载(www.5201413.com)

    -------------------------------------------------------------------------------------------

    VB 程式有两种工作模式—主动模式及事件驱动模式,主动模式与传统的 DOS 程式很像,程式载入系统後就会一直执行,直到结束为止,事件驱动模式则会在程式载入系统後,先暂时停止执行,直到事件发生时,才驱动对应的事件程序而执行某段程式,执行此段程式之後,又会暂停执行,等待下一次事件的发生。

    主动模式的程式在 Windows 3.1 底下是被禁止的,主要的原因是 Windows 3.1 不是真正的多工作业系统,而为了让不同程式能够同时执行,Windows 规定每一个程式必须不定时地呼叫 Yield、GetMessage、PeekMessage 等 API 函数(VB 程式则是呼叫 DoEvents),而当程式呼叫这些函数时,Windows 就会趁此一时机把 CPU 交给其他程式使用,也因此让每一个程式都有机会使用 CPU 而达到多工作业的目的。

    但如果有某一个程式忽略了此一规定,它就会一直霸占着 CPU 不放,而使得整个 Windows 的作业环境像当掉一样,不过这个问题到了 Windows 95 及 NT 之後,已经不再存在,因为 Windows 95 及 NT 具有主动切换各个程式使用 CPU 权利的能力,所以即使某一程式进入了无穷回圈,Windows 依然会在一小段时间後将 CPU 的使用权转移给其他程式,我们可以把 Windows 与程式之间的关系表示成图-1:
     
    尽管主动模式的程式在 Windows 底下不再有问题,但笔者必须强调的是事件驱动模式才是 Windows 程式的主流,为什麽呢?在 Windows 的多工作业环境底下,萤幕的输出、键盘的输入、滑鼠的输入…等,都必须由 Windows 来统筹管理,如果每个程式都想主动控制这些输出与输入装置,势必造成你抢我抢的混乱现象,反观事件驱动模式是以物件为核心,程式的执行是把负责不同工作的物件派到 Windows 的工作环境底下,接着这些物件就会静候 Windows 所产生的事件,然後加以处理,由於事件是由 Windows 统筹管理以及产生的,因此不同程式之间便能够在 Windows 的环境底下共同生存及工作。

    除了从多工作业的角度来看,事件驱动模式的程式也比较容易管理,由於我们会引用不同的物件以处理不同的工作,因此便比较不会把某一项工作的程式写得太大,而造成将来侦错及维护的困难。

    --------------------------------------------------------------------------------
    VB 的事件回溯到 Windows 的讯息
    --------------------------------------------------------------------------------

     
    以上是从 VB 的角度来看事件,尽管笔者说 Windows 会产生事件以驱动物件,但比较正确的说法是 Windows 先产生讯息,经由 VB 的转化才成为事件,然後才驱动物件的,如图-2。

    事件的产生乃源自於 Windows 的讯息

    但是 VB 是如何把讯息转化成事件的呢?而讯息又是什麽东西呢?请回忆我们在 46 期介绍的 hWnd(handle of window),它代表视窗的唯一识别码,由於许多物件都有 hWnd 这个属性,所以我们可以把物件表示成图-3,而视窗是 Windows 传递讯息的对象,这便使得 VB 得以将讯息转化成事件,进而驱动物件中的事件程序。
     
    物件与视窗的关系

    至於 VB 怎样把讯息转化成事件的呢?在视窗的内部,有一样最重要的东西称为「视窗程序」(window procedure),它的用途是接收来自 Windows 的讯息,当 VB 建立物件(视窗)时,也会提供一个视窗程序,用以接受来自 Windows 的讯息、处理讯息、并且会将某些讯息转化成为事件,进而驱动物件的事件程序,只是它始终是隐藏的,所以对大部分的 VB 程式设计人员来说,并不晓得有这一号人物存在。


    将讯息转化成事件的藏镜人是视窗程序

     
    --------------------------------------------------------------------------------
    认识视窗程序
    --------------------------------------------------------------------------------

     

    关於视窗程序,如果表示成 VB 的函数,则如下:

     

    Function WndProcName(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long,
    ByVal lParam As Long) As Long
         Select Case Msg
             Case WM_???? ' 讯息-1
                 ...
             Case WM_???? ' 讯息-2
                 ...
             Case Else
                 ret = DefWindowProc(hWnd, Msg, wParam, lParam)     End Select
        WndProcName = ret
    End Function 

    首先来看视窗程序的四个参数:

    hWnd:handle of window,这个不必再解释了吧!
    Msg:讯息编号,用来表示 Windows 所传递过来的是怎样的讯息。在 Win32api.txt 档案中,我们可以找到许多以 WM_(表示 Window Message) 为开头的常数定义,例如「Public Const WM_LBUTTONDOWN = &H201」,其中常数符号部分(例如 LBUTTONDOWN 表示 Left Button Down)可以让我们望文生义,而常数数值部分(&H201)则是讯息的编号。而视窗程序会利用 If Msg = WM_???? 或 Select Case Msg/Case WM_???? 叙述来判断收到的讯息是否为某一种讯息,进而加以处理。
    wParam 及 lParam:这两个参数称为讯息参数,其意义会随着讯息(参数二 Msg)的不同而有所不同,想了解每一种讯息参数的意义,则必须查阅 SDK(Software Development Kits) 参考手册,不过 SDK 参考手册蛮贵的,但您可以在以下两个地方寻得 SDK 的线上参考手册:(1) VC++ (2) MSDN/CD (Microsoft Developer Network/CD)。
     

    至於视窗程序之中最值得注意的地方是 DefWindowProc API 函数,这是 Windows 为了减少视窗程序的负担所提供的函数,由於 Windows 可能传递给视窗程序的讯息多达数百种,而大部分讯息是不需要特别处理的,此时若呼叫 DefWindowProc,可将讯息交由 API 代为处理。

     
    VB 提供的视窗程序
    --------------------------------------------------------------------------------
     

    如果是自己撰写视窗程序,可以自由地处理收到的讯息,不过在 VB 程式中,视窗程序是由 VB 提供的,以下让笔者先来介绍 VB 所提供的视窗程序是如何处理讯息、以及如何产生事件以驱动物件的。

     

    刚才笔者说过 Windows 的讯息多达数百种,对 VB 所提供的视窗程序而言,也一样不处理那麽多讯息,所以大部分的情况都是呼叫 DefWindowProc 交由 API 代劳。但由於 VB 提供的物件都含有某些事件,因此当 VB 的视窗程序收到讯息时,会判断此一讯息是否应转化成为事件,例如 LBUTTONDOWN 讯息应转化成为 MouseDown 事件,如果是,则判断程式中是否含有该事件的事件程序(例如 Form_MouseDown),如果有,则呼叫此一事件程序,参考图-4 就不难了解其间的运作过程。

     
    笔者觉得视窗程序与事件程序在概念上并没有太大的差别,但不可否认的 VB 的视窗程序会吃掉了许多讯息(也就是说这些讯息并不会产生对应的事件),VB 这麽做也不是没有道理,只保留最为常用的讯息可减少程式设计人员的负担,而实际上,也可以提升程式的执行效能。不过话说回来,因为许多讯息被 VB 吃掉了,这也使得 VB 程式无法完成某些特殊的功能,过去 VB 一直被批评为容易使用但功能不足,这是重要的原因,不过这种现象到了 VB 5.0 以後,有了重大的改变,请继续读下去,稍後笔者就会说明。

     
    --------------------------------------------------------------------------------
    VB 程式具有 Callback 的能力
    --------------------------------------------------------------------------------

     
    想要强化 VB 的视窗程序是可能的,不过这必须先了解 Callback 的工作模式。

     
    Windows 会传递讯息给视窗程序,但何谓传递呢?其实就是 Windows 准备好 hWnd、Msg、wParam、lParam 参数,然後呼叫 VB 所提供的函数(视窗程序),这原本是没什麽特别的事情,但由於平常都是由应用程式(或 VB)来呼叫 Windows 的,而讯息的传递则是由 Windows 倒回来呼叫应用程式,故称为 Callback。


    想要强化 VB 的视窗程序首先必撰写好强化的程序,并且把这个程序设定成 Windows 可以 Callback 的程序,此时最重要的一件事情是告诉 Windows 此一程序的「位址」,但 VB 4.0 以前的版本却无法取得程序的位址,因此举凡与 Callback 有关的程式设计都与 VB 程式无缘,不过到5.0 版以後,VB 提供了 AddressOf 叙述可供程式取得程序的位址,也因此为 VB 程式开启了 Callback 的後门。
     

    Callback 程式范例
    --------------------------------------------------------------------------------
     

    接下来让笔者举个 VB 程式的 Callback 范例,首先请开启 VB 的线上手册(选取 VB 功能表的「说明/线上手册」),然後寻找「AddressOf」关键字,在找到的几个主题中,请选取「AddressOf 运算子范例」,然後依照指示将范例移植到程式中,最後执行程式,结果此一程式会列举系统中的字型,并且显示在表单上的 ListBox 中, 

    VB 的 AddressOf 范例

     

    此一范例的重点在於 EnumFontFamilies API 函数的呼叫(位於 Module1 的 FillListWithFonts 的副程式),EnumFontFamilies 的用途是列举系统的所有字型,但由於系统的字型数目是可变的,因此 Windows 规定呼叫此一函数时必须传入一个 Callback 程序让 Windows 把列举得到的字型一一传递给这个程序,为了将 Callback 程序的位址传给 Windows,我们可以发现 EnumFontFamilies 的参数叁如下:

     
    AddressOf EnumFontFamProc

     

    作用就是取得 EnumFontFamProc 程序的位址。



    --------------------------------------------------------------------------------
    强化 VB 的视窗程序
    --------------------------------------------------------------------------------

     

    由於 VB 的视窗程序是隐藏起来的,所以我们无法改变其中的程式码,但如果已知 hWnd,那麽 Windows 允许我们换掉既有的视窗程序,假设我们想将 Form1 预设的视窗程序改成我们所撰写的视窗程序(假设名称是 WndProc),则呼叫的 API 如下:

     

    ret = SetWindowLong(Form1.hWnd, GWL_WNDPROC, AddressOf WndProc)

     

    如此一来,VB 预设给 Form1 的视窗程序就会失效,而取而代之的是 WndProc 视窗程序,所以接下来就由 WndProc 来接收 Windows 的讯息了。

     

    视窗程序的插队游戏
    --------------------------------------------------------------------------------
     

    利用以上方法来改变 Form 的视窗程序,乍看之下,好像还不错,但实际却很危险,为什麽呢?想想,当我们想换掉某一个视窗程序时,至少要提供与原视窗程序相同的功能,而 VB 的视窗程序到底提供了哪些功能,我们并不清楚,所谓破坏容易建设难啊!解决此一问题,其根本原理请参考图-6,作法上是在原视窗程序前面插入另一个视窗程序,用以撷取我们希望处理的讯息,进而达到强化原视窗程序的效果,至於不处理的讯息,则呼叫 CallWindowProc 将讯息交由原视窗程序来处理,如此一来便不至於破坏原视窗程序的功能。此一作法 Windows 称之为 SubClassing,笔者则称之为视窗程序的插队游戏,要玩插队游戏,还有一些细节要注意。

     

    图-6 在原有的视窗程序之前插入另一个视窗程序

     

    记录原视窗程序的位址
    --------------------------------------------------------------------------------
     

    由於我们必须将不处理的讯息丢回原视窗程序,所以呼叫:

     

    ret = SetWindowLong(Form1.hWnd, GWL_WNDPROC, AddressOf WndProc)

     

    改变 Form1 的视窗程序之前,必须先记录原视窗程序的位址,方法如下:

     

    Dim prevWndProc As Long

    prevWndProc = GetWindowLong(Form1.hWnd, GWL_WNDPROC)

     

    传回值 prevWndProc 即为 Form1 原视窗程序的位址。

     

    插队用视窗程序的写法
    --------------------------------------------------------------------------------
     

    首先假设插队视窗程序的目的只为了插队,但插队之後什麽是也不做,那麽此一视窗程序收到讯息时,就原原本本地呼叫原视窗程序,则 WndProc 视窗程序如下:

     

    Function WndProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

    WndProc = CallWindowProc(prevWndProc, hWnd, Msg, wParam, lParam)

    End Function

     

    比较值得注意的是 CallWindowProc 的第一个参数必须传入原视窗程序的位址 prevWndProc

     

    如果想要在插队的视窗程序中处理某些讯息,则结构如下:

     

    Function WndProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long,
    ByVal lParam As Long) As Long     Select Case Msg
             Case WM_xxxx
                 ... 处理 WM_xxxx 的程式
             Case WM_xxxx
                 ... 处理 WM_xxxx 的程式
             Case Else
            WndProc = CallWindowProc(prevWndProc, hWnd, Msg, wParam, lParam)
         End SelectEnd Function 

    取消插队行为

    --------------------------------------------------------------------------------


    一旦发生插队的行为,在视窗结束以前,一定要取消插队行为,否则程式会当掉,此时须将原视窗程序的位址设定回 Form1,如下:


    ret = SetWindowLong(Form1.hWnd, GWL_WNDPROC, prevWndProc)

     
    实例解析解说


    --------------------------------------------------------------------------------
    Option Explicit
    Public prevWndProc As Long

    Function WndProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

    Debug.Print hWnd, Msg, wParam, lParam

    WndProc = CallWindowProc(prevWndProc, hWnd, Msg, wParam, lParam)

    End Function

     

    在此一 .bas 程式中有几件事值得注意:

     

    Option Explicit 叙述:撰写呼叫 Windows API 的程式时,最好利用此一叙述禁止使用未宣告的变数,因为呼叫 Windows API 时,一旦资料型别不符合就很容易产生错误。
    prevWndProc 变数:用来记录原视窗程序的位址。
    视窗程序内容:在此一视窗程序中,暂时不处理任何讯息,因此直接呼叫 CallWindowProc 将讯息传给原视窗程序,此外也利用 Debug.Print 将视窗程序的几个参数显示出来,藉以了解讯息传递给视窗程序的情况。
     

    Option Explicit
    Private Sub Form_Load()
         Dim ret As Long
         prevWndProc = GetWindowLong(Me.hWnd, GWL_WNDPROC)
         ret = SetWindowLong(Me.hWnd, GWL_WNDPROC, AddressOf WndProc)
    End SubPrivate Sub Form_Unload(Cancel As Integer)
         Dim ret As Long
         ret = SetWindowLong(Me.hWnd, GWL_WNDPROC, prevWndProc)
    End Sub 

    此一 .frm 程式的重点在於 Form_Load 及 Form_Unload 事件程序,其中笔者在 Form_Load 事件程序中(也就是 Form 载入时),将 WndProc.bas 的 WndProc 视窗程序插在原视窗程序之前,而在 Form_Unload 事件程序中则将原视窗程序还原回来。(特别注意:想结束程式,请按下 Form 的关闭钮,不要使用 VB 的结束钮,否则 Form_Unload 将不会被执行到)

     


    --------------------------------------------------------------------------------
    强化视窗程序的实例
    --------------------------------------------------------------------------------

     

    延续前面的 WndProc.vbp 程式,让我们来观察两个强化 VB 原视窗程序的实例。

     

    利用 WM_QUERYOPEN 讯息禁止视窗还原
    --------------------------------------------------------------------------------
     

    WM_QUERYOPEN 讯息发生於视窗由「最小化」还原时,在此笔者想利用此一讯息让视窗一直保留在最小化的状态,首先假设我们不利用此一讯息,而想利用 VB 既有的事件来达成相同的目的,那麽想到的方法是在 Form_Resize 事件程序中撰写以下程式:

     

    Private Sub Form_Resize()
         If WindowState <> vbMinimized Then
             WindowState = vbMinimized
         End If
    End Sub 

    这一段程式确实可以让视窗一直保持在最小化的状态,但使用者将视窗还原时,视窗会被先还原,然後才再缩小,结果可以看到视窗被还原然後又被缩小的过程。如果我们利用 WM_QUERYOPEN 讯息,则 WndProc 视窗程序只要如下修改即可:

     

    Function WndProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long,
    ByVal lParam As Long) As Long     If Msg = WM_QUERYOPEN Then
             ' 吃掉此一讯息,不再传递给原视窗程序
             ' 也就是不让原视窗程序得到视窗还原的讯息
         Else
            WndProc = CallWindowProc(prevWndProc, hWnd, Msg, wParam, lParam)
         End If
    End Function 

    以上程式十分简单,只要在 WM_QUERYOPEN 讯息发生时,不再呼叫原视窗程序,使得原视窗程序不知有 WM_QUERYOPEN 讯息发生,於是便不会由最小化还原回来。

    非显示区的滑鼠事件


    --------------------------------------------------------------------------------

    VB 既有的事件中,只含有视窗「显示区」(client rect)的滑鼠事件,当我们把滑鼠移到非显示区(例如视窗标题区),则收不到 MouseMove 事件,假设我们的需求是:「滑鼠移到视窗上面时,即让视窗变成使用中」,若单纯使用事件程序来撰写程式,则如下:

     
    Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)

    Me.SetFocus

    End Sub

     

    但这个程式有点小瑕疵,那就是滑鼠移到非显示区,而没有移到显示区时,程式并没有作用,为了让程式能够收到滑鼠移到非显示区的讯息,我们可以撰写以下的视窗程序:
     

    Function WndProc(ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long,
    ByVal lParam As Long) As Long     Dim ret As Long
         If Msg = WM_NCMOUSEMOVE Then
             Form1.SetFocus
         End If
        WndProc = CallWindowProc(prevWndProc, hwnd, Msg, wParam, lParam)
    End Function 

    在以上程式中,判断 Msg 是否等於 WM_NCMOUSEMOVE 讯息,便可得知使用者是否将滑鼠移到了非显示区,而由於我们不想改变视窗的行为,所以紧接着又呼叫了 CallWindowProc 将讯息传给原视窗程序。

    转载于:https://www.cnblogs.com/wskaihd/archive/2007/05/23/756819.html

    展开全文
  • 我有一个具有开始按钮的表单(允许用户根据需要反复运行进程),并且我想在表单加载时发送btnStart.Click事件,以便进程自动启动.我有btnStart.Click事件以下函数,但我如何实际告诉Visual Basic’假装有人点击了按钮并...

    我有一个具有开始按钮的表单(允许用户根据需要反复运行进程),并且我想在表单加载时发送btnStart.Click事件,以便进程自动启动.

    我有btnStart.Click事件的以下函数,但我如何实际告诉Visual Basic’假装有人点击了按钮并触发此事件’?

    我试过非常简单,基本上可行.但是,Visual Studio给我一个警告变量’sender’在被赋值之前被使用,所以我猜这不是真正的方法:

    Dim sender As Object

    btnStart_Click(sender, New EventArgs())

    我也尝试过使用RaiseEvent btnStart.Click,但是会出现以下错误:

    ‘btnStart’ is not an event of ‘MyProject.MyFormClass

    Imports System.ComponentModel

    Partial Public Class frmProgress

    Private bw As BackgroundWorker = New BackgroundWorker

    Public Sub New()

    InitializeComponent()

    ' Set up the BackgroundWorker

    bw.WorkerReportsProgress = True

    bw.WorkerSupportsCancellation = True

    AddHandler bw.DoWork, AddressOf bw_DoWork

    AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged

    AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted

    ' Fire the 'btnStart.click' event when the form loads

    Dim sender As Object

    btnStart_Click(sender, New EventArgs())

    End Sub

    Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click

    If Not bw.IsBusy = True Then

    ' Enable the 'More >>' button on the form, as there will now be details for users to view

    Me.btnMore.Enabled = True

    ' Update the form control settings so that they correctly formatted when the processing starts

    set_form_on_start()

    bw.RunWorkerAsync()

    End If

    End Sub

    ' Other functions exist here

    End Class

    展开全文
  • VB中sub和private是什么意思 Private Sub cmdFind_Click()说明发生了按钮的点击事件。 Private Sub是一个过程,并且是模块级的。过程没有返回值public是公有,类的方法或者变量可供其他任何类使用...

    Private Sub cmdFind_Click() 
    说明发生了按钮的点击事件。   Private Sub是一个过程,并且是模块级的。过程没有返回值public是公有,类中的方法或者变量可供其他任何类使用 
    private是私有,类中的方法或者变量不被任何类用,不被子类继承 
    protected是保护,类中的方法或者变量可被子类和同一包下的类使用

    私有函数过程,外部不能直接使用的!Sub 是过程 Private是私有函数过程 外部不能直接使用的

    Sub 语句 
    描述 
    声明 Sub 过程的名称、参数以及构成其主体的代码。 

    语法 
    [Public | Private] Sub name [(arglist)] 
    [statements] 
    [Exit Sub] 
    [statements] 
    End Sub 


    Sub 语句的语法包含以下部分 

    部分 描述 
    Public 表示 Sub 过程可被所有 Script 中的所有其他过程访问。 
    Private 表示 Sub 过程只可被声明该过程的 Script 中的其他过程访问。 
    Name Sub 的名称,遵循标准变量命名约定。 
    Arglist 代表在调用时要传递给 Sub 过程的参数的变量列表。用逗号隔开多个变量。 
    Statements 在 Sub 过程主体内所执行的任何语句组。 


    arglist 参数包含下列语法和部分:[ByVal | ByVal] varname[( )] 
    部分 描述 
    ByVal 表示该参数按值传递。 
    ByRef 表示该参数按引用传递。 
    Varname 代表参数的变量名称,遵循标准变量命名约定。 

    说明 
    如没有显式地指定使用 Public 或 Private,则 Sub 过程默认为公用,即它们对于 Script 中的所有其他过程都是可见的。Sub 过程中局部变量的值在调用过程中不被保留。 

    所有可执行代码必须包含于过程中。不能在另一个 Sub 或 Function 过程中定义一个 Sub 过程。 

    使用 Exit Sub 语句可以立即从 Sub 过程中退出。程序继续执行调用 Sub 过程的语句之后的语句。可以在 Sub 过程中任意位置出现任意个 Exit Sub 语句。 

    与 Function 过程相似之处是:Sub 过程是一个可以获取参数,执行一系列语句以及可改变其参数的值的独立过程。而与 Function 过程不同之处是:Function 过程可以返回值,而 Sub 过程不能用于表达式中。 

    可以使用过程名并跟随相应的参数列表来调用 Sub 过程。关于如何调用 Sub 过程的详细说明信息,请参阅 Call 语句。 

    小心 Sub 过程可以是递归的,即该过程可以调用自己来完成某个给定的任务。但是递归可能会导致堆栈溢出。 

    在 Sub 过程中使用的变量分为两类:一类是在过程内显式声明的,另一类则不是。在过程内显式声明的变量(使用 Dim 或等效方法)总是局部变量。对于那些没有在过程中显式声明的变量也是局部的,除非在该过程外更高级别的位置显式地声明它们。 

    小心 过程可以使用没有在过程内显式声明的变量,但只要有任何 script 级定义的名称与之同名,就会产生名称冲突。如果过程中引用的未声明的变量与其他的过程、常数或变量的名称相同,则会认为过程引用的是Script 级的名称。显式声明变量可以避免这类冲突,使用 Option Explicit 语句可强制显式声明变量。 


    Visual Basic 语言概念 

    Sub 过程 
    Sub 过程是包含在 Sub 语句和 End Sub 语句中的一系列 Visual Basic 语句。每次调用过程时都执行过程中的语句,从 Sub 语句后的第一个可执行语句开始,到遇到的第一个 End Sub、Exit Sub 或 Return 语句结束。 

    Sub 过程执行操作但并不返回值。它能够带参数,如呼叫代码传递给它的常数、变量或表达式。 

    声明 Sub 过程的语法如下所示: 

    [accessibility] Sub subname[(argumentlist)] 
    ' Statements of the Sub procedure go here. 
    End Sub 
    可访问性可以是 Public、Protected、Friend、Protected Friend 或 Private。 

    可以在模块、类和结构中定义 Sub 过程。默认情况下它们是 Public,这意味着可以从应用程序中的任意位置调用它们。 

    参数声明 
    声明过程的每个参数与声明变量的方法一样,都是指定参数名和数据类型。也可以指定传递机制,以及参数是否可选。 

    参数列表中每个参数的语法如下所示: 

    [Optional] [ByVal|ByRef] [ParamArray] argumentname As datatype 
    如果参数是可选的,则还必须在其声明中提供默认值,如下所示: 

    Optional [ByVal|ByRef] argumentname As datatype = defaultvalue 
    调用语法 
    可以使用独立的调用语句来显式调用 Sub 过程。不能在表达式中使用其名称来调用它。调用语句必须提供所有非可选参数的值,并且必须用括号将参数列表括起来。如果未提供任何参数,则也可以选择省略括号。Call 关键字的使用也是可选的。 

    调用 Sub 过程的语法如下所示: 

    [Call] subname[(argumentlist)] 
    下面的 Sub 过程通知计算机操作员应用程序将要执行哪个任务,并且还显示一个时间戳。应用程序不是在每个任务的开头重复此代码,而仅是从不同的位置调用 TellOperator。每次调用都会传递 Task 参数中的字符串以标识开始执行的任务。 

    Sub TellOperator(ByVal Task As String) 
    Dim Stamp As Date ' Stamp is local to TellOperator. 
    Stamp = TimeOfDay() ' Get current time for time stamp. 
    ' Use MessageBox class of System.Windows.Forms namespace. 
    MessageBox.Show("Starting " & Task & " at " & CStr(Stamp)) 
    End Sub 
    典型的 TellOperator 调用如下所示: 

    Call TellOperator("file update") 
    请参见 
    过程 | 事件处理过程 | Function 过程 | Property 过程 | 过程参数 | Sub 语句

    posted on 2012-02-08 17:05 Jack.leung 阅读(...) 评论(...) 编辑 收藏

    转载于:https://www.cnblogs.com/jack-liang/archive/2012/02/08/2342966.html

    展开全文
  • 事件是你的代码兵器库的主要部分,无论你用Visual Basic? 6.0,Visual Basic .NET 2002,Visual Basic .NET 2003,还是Visual Basic 2005。窗体和控件引发事件,同时你的代码处理这些事件。你用Visual Basic写的...
  • vb之窗体关闭事件

    千次阅读 热门讨论 2014-11-25 19:28:59
    QueryUnload 事件  在一个窗体或应用程序...如果没有窗体取消 QueryUnload 事件,该 Unload 事件首先发生在所有其它窗体,然后再发生在 MDI 窗体。当一个子窗体或一个 Form 对象关闭时,在那个窗体的 QueryUnloa
  • 在方法里激发事件响应到VB里,VB接收不到事件ATL,Active Template Library活动模板库,是一种微软程序库,支持利用C++语言编写ASP代码以及其它ActiveX程序。通过活动模板库,可以建立COM组件,然后通过ASP页面的...
  • 如何实现点击VB中DataGrid行,触发事件弹出窗口,并传递所点击行参数: 通过双击事件来实现的: Private Sub DataGrid1_DblClick()  Dim frm1 As New frmNew ''定义一个窗体对象frm1,frmNew是程序的一...
  • 一、什么是类? 用面向对象的编程思想来看,类就是存储数据同时给一组相关代码赋予协调功能的方式。类是面向对象编程的核心。 VB最为人垢病的是它的面向对象特性。... 然后在BaseClass加入以下代码: Public ...
  • VB模拟键盘事件的N种方法 2009-05-16 19:42 键盘是我们使用计算机的一个很重要的输入设备了,即使在鼠标大行其道的今天,很多程序依然离不开键盘来操作。但是有时候,一些重复性的,很繁琐的键盘...
  • VB中的窗口位图

    千次阅读 2012-03-21 22:04:16
    影响VB窗口位图的属性 1)  BackColor属性:该属性确定窗口背景颜色。 2)  Picture属性:该属性用于作为窗口背景的图片,在窗口内部,该属性封装了一个BITMAP的GDI对象。 3)  hDC、Image及AutoReDraw属性: a) ...
  • VB中调用VB.NET包装类

    千次阅读 2006-04-29 16:42:00
    概述许多组织都有一个重要的 Microsoft Visual Basic 6...可以维持 Visual Basic 6 应用程序的现状,而无需向该旧式应用程序加入任何新技术。可以将 Visual Basic 6 应用程序完全迁移到 Visual Basic .NET 。或者,
  • VB中应用DDE

    千次阅读 2014-02-09 10:09:25
    动态数据交换(dde)是windows应用程序间通讯的基本方法之一,在动态数据交换的过程,提供数据和服务的应用程序称为服务器,请求数据或服务的应用程序则称为客户。 dde交谈是由客户程序启动的。如果得到服务器...
  • vb中如何实现键盘钩子? 2009-04-08 17:35 Windows钩子函数的概念和实现方法 首先我们必须大致了解Windows的基本运作机理,Windows作为一个多任务操作系统,它是分有层次概念的,运行在最底下的称为Ring 0层,在这...
  • VB中的类开发

    千次阅读 2005-02-07 18:42:00
    对程序员和编程爱好者来说,VB中类的技术是学习的一个难点,在大型软件的开发过程,模块(Moudle)、控件(Active ocx)、链接库(Active dll)和类(Class moudle)构成了系统化、高效化的软件工程,而类的技术是...
  • VB属性、方法、事件

    千次阅读 2006-08-03 18:42:00
    1、 向类添加属性 向类添加属性可以采用以下两种方式之一:“字段”或“属性过程”。还可以用属性的 Public、ReadOnly 或 WriteOnly 修饰符确定属性如何工作。 字段和属性过程字段其实只是类的公共变量,可以从...
  • VB 的基本数据类型

    万次阅读 2017-06-11 10:12:31
    VB基本数据类型 VB 6.O提供的基本数据类型主要有:字符型数据、数值型数据、货币类型、字节类型、布尔类型、日期类型、通用类型、无符号类型、对象类型等9种数据类型。 1.字符型数据 字符型数据(String)用以定义...
  • VB中的Dictionary对象

    千次阅读 2011-08-31 20:04:55
    然后在Session_onStart事件处理程序里将一系列值用Add方法放入Dictionary,并将对Dictionary对象的引用指定给ASP会话变量MyDictionary: PROGID="Scripting.Dictionary"> </OBJECT> ...
  • VB中Sub过程与Function过程的区别

    千次阅读 2020-01-14 13:39:58
    通用过程与事件过程不同,它不依附于某一对象,也不是有对象的某一事件驱动或由系统自动调用,而是通过被调用语句(如Call语句)调用才起作用。通用过程可以被多次调用,调用该过程的过程称为调用过程。 Sub过程...
  • Partial Classes are a feature of VB.NET that is used almost everywhere, but there's not much written about it. This might be because there are not a lot of obvious "developer" applications ...
  • 细谈VB中的function程序应用问题!

    万次阅读 热门讨论 2012-06-06 21:14:05
    VB中function 程序如何应用?  Function 过程是由用户定义的独立的函数过程,与Sub字程序类似。Function 函数过程可读取参数,修改语句。可作为独立的基本语句调用,也可以在程序或是函数嵌套使用。它有返回值...
  • 正则表达式在VB中的应用

    千次阅读 2008-12-21 20:41:00
    VB正则表达式简介如果原来没有使用过正则表达式,那么可能对这个术语和概念会不太熟悉。不过,它们并不是您想象的那么新奇。请回想一下在硬盘上是如何查找文件的。您肯定会使用 ? 和 * 字符来帮助查找您正寻找的文件...
  • VB基础

    万次阅读 多人点赞 2008-03-06 09:52:00
    VB入门1.1 知识要点1.VB的特点、安装和启动了解VB面向对象、可视化、事件驱动的特点。2.VB集成开发环境初学者主要掌握菜单栏、工具栏、工具箱窗口、属性窗口、代码窗口、工程资源管理器窗口的使用。3.VB对象的...
  • VB中Doevents函数详解

    2012-03-20 11:16:00
    Doevents函数是一个很好用的函数,但很多人...DoEvents函数的功能是:转让控制权,以便让操作系统处理其它的事件。 问:为什么要用doevents? A.在需要用某一循环处理相当耗时或者很快速的代码时,就需要用到它,以便用...
  • 2009年05月07日 星期四 上午 00:20 在程序流程经常要延时一段时间后再继续往下执行,在VB中常用的有以下几种方法(因为Timer控件打乱了程序的流程所以一般不用它):1.使用Windows API函数Sleep新建一个工程,添加一个...
  • 浅谈VB程序设计的数据库应用

    千次阅读 2012-08-19 09:47:16
    浅谈VB程序设计的数据库应用  在Visual Basic程序设计,数据库应用是一个非常重要、实用的内容,在以后的学习我们肯定会大量使用Visual Basic编程开发数据库应用程序。现在我们正学习把Visual Basic ...
  • VB中实现延时(等待)的几种方法

    千次阅读 2011-10-10 12:32:40
    在程序流程经常要延时一段时间后再继续往下执行,在VB中常用的有以下几种方法(因为Timer控件打乱了程序的流程所以一般不用它): 1.使用Windows API函数Sleep 新建一个工程,添加一个TextBox控件和一个

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,714
精华内容 5,885
关键字:

以下不是vb中的事件是