WinForms 的应用程序事件处理程序






4.77/5 (15投票s)
2004年11月12日
6分钟阅读

158872

1883
在本文中,您将学习如何在 .NET 中为任何 WinForms 应用程序创建一个应用程序事件处理程序组件 (AEHC)。
目录
引言
在本文中,您将学习如何在 .NET 中为任何 WinForms 应用程序创建一个应用程序事件处理程序组件 (AEHC)。AEH 是您 WinForms 应用程序的事件处理程序。该组件的主要功能是在每个窗体加载到内存后引发一个事件,以便我们可以处理其事件以执行任何自定义代码处理,并在处理后将控件返回给窗体。
让我们考虑一个场景。在为应用程序实现安全性时,我们可能需要根据访问表单的用户角色来禁用或启用每个表单上的某些控件。为此,我们将调用一个自定义方法(例如,AuthorizeMe()
),以从应用程序的每个表单中执行自定义身份验证和授权。假设,在任何情况下,我们都忘记从某个表单中调用其方法,结果将是该表单没有应用安全性。
现在,有了 AEHC,我们只需在应用程序中从 AEHC 调用一次 AuthorizeMe()
方法,然后 AEHC 将负责在每次窗体加载后执行 AuthorizeMe()
。
什么是应用程序事件处理程序组件?
在 ASP.NET 中,我们可以创建自定义 HTTP 模块,以便在全局级别为 Web 应用程序中的每个网页添加自定义处理。使用 HTTP 模块的一个好处是您无需从每个网页调用它们;它们将自动为每个网页执行。在应用程序事件处理程序中,您将实现与 HTTP 模块类似的功能。但是,AEHC 将特定于每个应用程序,而 HTTP 模块可以用于整个计算机。
应用程序事件处理程序组件会在应用程序的整个生命周期中跟踪被加载和卸载的窗体,并在应用程序级别生成 FormLoaded()
和 FormUnloaded()
等事件。就像 HTTP 模块一样,您也可以编写自己的模块来在这些事件中操作窗体的属性(参见图 1)。
图 1 应用程序事件处理程序组件生命周期
Dim WithEvents myHandler As New AEHC.ApplicationEventHandler
Public frCol As ArrayList
Public Sub main()
'Attaches the Form Filter to the Application
myHandler.Init()
Application.Run(New Form1)
End Sub
Private Sub myHandler_FormLoaded(ByRef sender As System.Windows.Forms.Form, _
ByVal e As AEHC.AppEventHandlerArgs) Handles myHandler.FormLoaded
sender.Text = Now.ToLocalTime
End Sub
Private Sub myHandler_FormUnloaded(ByVal e As AEHC.AppEventHandlerArgs) _
Handles myHandler.FormUnloaded
MsgBox(e.FormsCollection.Count & " in memory")
End Sub
应用程序事件处理程序组件的架构
应用程序事件处理程序组件建立在 WinForms 发布的窗口消息之上。AEHC 的核心部分是一个 FormFilter
,它是一个通过实现 IMessageFilter
创建的自定义消息过滤器类。
让我们深入了解细节,看看应用程序事件处理程序组件是如何工作的(参见图 2)。当您加载 WinForms 应用程序时,它会为其大多数事件生成消息。您可以使用自定义消息过滤器来过滤消息队列。
让我们创建一个 Forms Collection 来保存应用程序中加载的所有窗体。获取应用程序中的活动窗体。检查该窗体是否存在于 Forms Collection 中,如果不存在则将其添加到集合中。
现在向 Form
Closed
事件添加一个处理程序,您将使用它在窗体接收到 Close
事件后将其从集合中删除。然后,引发 FormLoaded()
以将控件传递给用户。用户可以处理该事件并执行一些自定义处理,例如自定义身份验证、在加载的窗体中添加新控件,更改窗体的外观。
图 2 应用程序事件处理程序组件的工作流程
创建应用程序事件处理程序组件
应用程序事件处理程序由两个类组成:
FormMessageFilter
类:此类负责过滤由窗体或控件分派的消息。它实现IMessageFilter
以设计自定义消息过滤器(IMessageFilter
接口用于在消息分派给窗体或控件之前捕获消息)。声明两个事件
FormFound()
、FormUnload()
和一个ArrayList
来存储 Forms 集合。在
IMessageFilter
的PreFilterMessage
方法中,将消息的Msg
属性与消息代码(参见表 1)进行比较,并捕获事件。返回False
以分派消息,反之亦然。将
MSG
属性与&HF
进行比较以过滤窗口绘制消息。查找Form.ActiveForm
属性以识别窗体,并将Form
添加到 Forms Collection 中。向窗体附加一个事件处理程序,以便在窗体关闭后将其从集合中删除。Message 代码 目的 WM_PAINT
0x000F 当系统或其他应用程序请求绘制应用程序窗口的某一部分时发送 WM_KEYDOWN
0x0100 当按下非系统键时,发布给具有键盘焦点的窗口 WM_KEYUP
0x0101 当释放非系统键时,发布给具有键盘焦点的窗口 WM_MOUSEMOVE
0x0200 当光标移动时发布到窗口 WM_LBUTTONDOWN
0x0201 当用户在窗口客户区内按下鼠标左键时发布 WM_LBUTTONUP
0x0202 当用户在窗口客户区内释放鼠标左键时发布 WM_LBUTTONDBLCLK
0x0203 当用户在窗口客户区内双击鼠标左键时发布 WM_RBUTTONDOWN
0x0204 当用户在窗口客户区内按下鼠标右键时发布 WM_RBUTTONUP
0x0205 当用户在窗口客户区内释放鼠标左键时发布 WM_RBUTTONDBLCLK
0x0206 当用户在窗口客户区内双击鼠标右键时发布 注意:有关更多消息代码,请参阅 *WINUSER.H* 表 1:用于自定义过滤器类的消息代码
Friend Shared frmCollection As New ArrayList Public Event FormFound(ByRef sender As Form) Public Event FormUnload() 'Method for filtering the message Public Function PreFilterMessage(ByRef m As _ System.Windows.Forms.Message) As Boolean Implements _ System.Windows.Forms.IMessageFilter.PreFilterMessage 'Blocks messages related to Window Paint event (WM_PAINT 0x000F) If (m.Msg = &H200) Then If Not (Form.ActiveForm) Is Nothing Then Dim curForm As Form = Form.ActiveForm 'Search the forms in the Forms Collection to prevent 'duplication of forms in the collection If Not (Search(curForm, frmCollection)) Then frmCollection.Add(curForm) AddHandler curForm.Closed, AddressOf MyForm_Closed RaiseEvent FormFound(curForm) End If End If End If 'Must return false to dispatch the Message Return False End Function 'Event Handler for Form Close event Private Sub MyForm_Closed(ByVal sender As Object, _ ByVal e As System.EventArgs) Dim k, j As Integer 'Removes the forms which are disposed '(Form.Dispose) rather being closed(Form.Close) For i As Integer = 0 To frmCollection.Count - 1 j = i - k If j > (frmCollection.Count - 1) Then Exit For 'Checks the IsDisposed Property of the Form 'in the Forms Collection If frmCollection(j).IsDisposed = True Then frmCollection.Remove(frmCollection(j)) k = k + 1 End If Next 'Removes the form which raised the close event If Not (sender) Is Nothing Then frmCollection.Remove(sender) RaiseEvent FormUnload() End Sub 'Search Method searches fr(form) in the forms collection (frmArray) Private Function Search(ByVal fr As Form, _ ByVal frmArray As ArrayList) As Boolean Dim fr1 As Form For Each fr1 In frmArray If fr.Equals(fr1) Then Return True Next Return False End Function End Class
- 应用程序事件处理程序:将
FormMessageFilter
添加到应用程序的消息泵中,以过滤掉窗体绘制消息。以及一个共享的只读FormsCollection
属性,用于在运行时访问窗体集合。Public Class ApplicationEventHandler Public Event FormLoaded(ByRef sender As Form) Public Event FormUnloaded() 'Use this property to access your loaded forms Public Shared ReadOnly Property FormsCollection() As ArrayList Get Return FormMessageFilter.frmCollection End Get End Property 'Attaches FormFilter to the application Public Sub Init() Dim MyFilter As New FormMessageFilter AddHandler MyFilter.FormFound, AddressOf pFormLoaded AddHandler MyFilter.FormUnload, AddressOf pFormUnLoaded Application.AddMessageFilter(MyFilter) End Sub 'Called when any form is loaded in the application Private Sub pFormLoaded(ByRef CurForm As Form) RaiseEvent FormLoaded(CurForm) End Sub 'Called when any form is unloaded in the application Private Sub pFormUnLoaded() RaiseEvent FormUnloaded() End Sub End Class
您已成功创建了应用程序事件处理程序组件。
使用应用程序事件处理程序组件
现在一切都说完了,您已经完成了应用程序事件处理程序组件的创建。让我们学习如何在您的 WinForms 项目中使用它。
在 VB.NET 中创建一个新的 WinForms 应用程序。要将 AEHC 与 VB.NET WinForms 应用程序一起使用,您需要添加一个模块并编写一个 Sub main
方法。
添加对 AEHC 的引用,并在模块中使用 WithEvents
实例化它,当您想要捕获 FormLoaded()
和 FormUnloaded()
事件时。
通过调用 Init()
方法初始化实例。Init()
将为应用程序注册 AEHC 并开始监视消息队列。
在 FormLoaded()
事件中,添加用于向加载的窗体添加 Label
控件的代码;对于 FormUnloaded()
事件,只需添加一个消息框,并使用 AEHC 的 FormsCollection
属性通过 myHandler.FormsCollection.Count
获取加载窗体的数量。
Dim WithEvents myHandler As New AEHC.ApplicationEventHandler
Public Sub main()
‘Initialize the component
myHandler.Init()
Application.Run(New Form1)
End Sub
Private Sub myHandler_FormLoaded(ByRef sender As _
System.Windows.Forms.Form) Handles myHandler.FormLoaded
Dim label1 As System.Windows.Forms.Label
label1 = New System.Windows.Forms.Label
sender.SuspendLayout()
'Initialize the Label
label1.Font = New System.Drawing.Font("Arial", 14.25!, _
System.Drawing.FontStyle.Bold, _
System.Drawing.GraphicsUnit.Point, CType(0, Byte))
label1.Location = New System.Drawing.Point(8, 8)
label1.Name = "Label1"
label1.Size = New System.Drawing.Size(208, 23)
label1.TabIndex = 0
label1.Text = " Application Event Handler Component Magic "
'
sender.Controls.Add(label1)
sender.ResumeLayout(False)
End Sub
Private Sub myHandler_FormUnloaded() Handles myHandler.FormUnloaded
MsgBox(myHandler.FormsCollection.Count)
End Sub
运行您的项目,看看您的应用程序事件处理程序组件的魔力。您将在窗体上看到一个带有文本“Application Event Handler Component Magic”的标签。为了进行更多测试,您也可以在窗体上放置一个按钮并创建窗体的新实例,您将获得惊人的功能,例如所有显示的窗体都带有标签控件。
结论
应用程序事件处理程序组件对于 WinForms 开发人员来说非常强大,他们可以广泛地将其用于 WinForms 的 UI 定制以及其他任务。他们还可以利用 FormCollection
属性来操作应用程序中的窗体。
消息过滤器是为您的 WinForms 应用程序中的每个窗体添加自定义处理的最简单方法之一。事实上,通过使用相同的机制,您可以在 WinForms 应用程序中模仿 ASP.NET 的一些更出色的功能。在未来的专栏中,我将进一步探讨在 WinForms 中实现 ASP.NET 功能,例如缓存等。