向项目中的所有表单添加事件 [英] Add an event to all Forms in a Project
问题描述
如果要在项目中的Form
标题中显示每个Form
的大小,什么是最佳方法?
我不想在每个Form
中手动放置事件处理程序.
我希望过程是自动的.
有点像重载的Load()
事件,该事件在resize事件上添加了处理程序.
If I want to display the size of every Form
in my Project in the Form
's Title what will be the best approach?
I don't want to manually put a event handler in every Form
.
I want the process to be automatic.
Something like a overloaded Load()
event that adds a handler on the resize event.
推荐答案
Here is an attempt to implement an Automation solution to the problem.
问题:
将一个或多个事件处理程序附加到WinForms
项目(或其子集)中的每个现有Form
,而无需编辑/修改这些类的现有代码.
The problem:
Attach one or more Event Handlers to each existing Form
in a WinForms
Project (or a subset of them), without editing/modifying these classes existing code.
可能的解决方案来自Automation
类,该类提供了检测何时打开新窗口并将事件报告给其自己的 AutomationEvent 设置为 AutomationElement 成员必须设置为 AutomationElement.RootElement 和
A possible solution comes from the Automation
class, which provides means to detect when a new Window is opened and reports the event to the subscribers of its own Automation.AddAutomationEventHandler, when the EventId
of its AutomationEvent is set to a WindowPattern pattern.
The AutomationElement member must be set to AutomationElement.RootElement and the Scope
member to TreeScope.SubTree.
Automation
,对于每个引发AutomationEvent
的AutomationElement
,报告:
-Element.Name
(与Windows标题对应)
-Process ID
-Window Handle
(作为整数值)
Automation
, for each AutomationElement
that raises the AutomationEvent
, reports:
- the Element.Name
(corresponding to the Windows Title)
- the Process ID
- the Window Handle
(as an Integer value)
这些值足以识别属于当前进程的Window;窗口句柄允许标识打开的Form
实例,测试
These values are quite enough to identify a Window that belongs to the current process; the Window handle allows to identify the opened Form
instance, testing the Application.OpenForms() collection .
当表单被选出来时,新的Event Handler
可以附加到选择的Event
上.
When the Form is singled out, a new Event Handler
can be attached to an Event
of choice.
通过扩展此概念,可以创建预定义的事件列表和将这些事件附加到的表单列表.
可能的话,可以在需要时将一个类文件包含在Project中.
By expanding this concept, it is possible to create a pre-defined List of Events and a List of Forms to attach these events to.
Possibly, with a class file to include in a Project when required.
请注意,在这种情况下,某些事件将是没有意义的,因为Automation
报告已经显示的窗口已打开,因此Load()
和Shown()
事件属于过去. br>
As a note, some events will not be meaningful in this scenario, because the Automation
reports the opening of a Window when it is already shown, thus the Load()
and Shown()
events belong to the past.
我已经通过几个事件(
Form.Resize()
和Form.Activate()
)对它进行了测试,但是在这里的代码中,为了简单起见,我仅使用.Resize()
.I've tested this with a couple of events (
Form.Resize()
and Form.Activate()
), but in the code here I'm using just .Resize()
for simplicity.这是过程的图形表示.
启动应用程序时,事件处理程序未附加到.Resize()
事件.
仅仅是因为Boolean
字段设置为False
.
单击按钮,将Boolean
字段设置为True
,从而启用事件处理程序的注册.
注册.Resize()
事件时,所有Forms
Window Title
都将报告窗口的当前大小.
This is a graphics representation of the process.
Starting the application, the Event Handler is not attached to the .Resize()
event.
It's just because a Boolean
fields is set to False
.
Clicking a Button, the Boolean
field is set to True
, enabling the registration of the Event Handler.
When the .Resize()
event is registered, all Forms
Window Title
will report the current size of the Window.
测试环境:
Visual Studio 2017 pro 15.7.5
.Net FrameWork 4.7.1
Test environment:
Visual Studio 2017 pro 15.7.5
.Net FrameWork 4.7.1
导入的命名空间:
System.Windows.Automation
Imported Namespaces:
System.Windows.Automation
参考程序集:
UIAutomationClient
UIAutomationTypes
Reference Assemblies:
UIAutomationClient
UIAutomationTypes
MainForm
代码:
Imports System.Diagnostics
Imports System.Windows
Imports System.Windows.Automation
Public Class MainForm
Friend GlobalHandlerEnabled As Boolean = False
Protected Friend FormsHandler As List(Of Form) = New List(Of Form)
Protected Friend ResizeHandler As EventHandler
Public Sub New()
InitializeComponent()
ResizeHandler =
Sub(obj, args)
Dim CurrentForm As Form = TryCast(obj, Form)
CurrentForm.Text = CurrentForm.Text.Split({" ("}, StringSplitOptions.None)(0) &
$" ({CurrentForm.Width}, {CurrentForm.Height})"
End Sub
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent,
AutomationElement.RootElement,
TreeScope.Subtree,
Sub(UIElm, evt)
If Not GlobalHandlerEnabled Then Return
Dim element As AutomationElement = TryCast(UIElm, AutomationElement)
If element Is Nothing Then Return
Dim NativeHandle As IntPtr = CType(element.Current.NativeWindowHandle, IntPtr)
Dim ProcessId As Integer = element.Current.ProcessId
If ProcessId = Process.GetCurrentProcess().Id Then
Dim CurrentForm As Form = Nothing
Invoke(New MethodInvoker(
Sub()
CurrentForm = Application.OpenForms.
OfType(Of Form)().
FirstOrDefault(Function(f) f.Handle = NativeHandle)
End Sub))
If CurrentForm IsNot Nothing Then
Dim FormName As String = FormsHandler.FirstOrDefault(Function(f) f?.Name = CurrentForm.Name)?.Name
If Not String.IsNullOrEmpty(FormName) Then
RemoveHandler CurrentForm.Resize, ResizeHandler
FormsHandler.Remove(FormsHandler.Where(Function(fn) fn.Name = FormName).First())
End If
Invoke(New MethodInvoker(
Sub()
CurrentForm.Text = CurrentForm.Text & $" ({CurrentForm.Width}, {CurrentForm.Height})"
End Sub))
AddHandler CurrentForm.Resize, ResizeHandler
FormsHandler.Add(CurrentForm)
End If
End If
End Sub)
End Sub
Private Sub btnOpenForm_Click(sender As Object, e As EventArgs) Handles btnOpenForm.Click
Form2.Show(Me)
End Sub
Private Sub btnEnableHandlers_Click(sender As Object, e As EventArgs) Handles btnEnableHandlers.Click
GlobalHandlerEnabled = True
Me.Hide()
Me.Show()
End Sub
Private Sub btnDisableHandlers_Click(sender As Object, e As EventArgs) Handles btnDisableHandlers.Click
GlobalHandlerEnabled = False
If FormsHandler IsNot Nothing Then
For Each Item As Form In FormsHandler
RemoveHandler Item.Resize, ResizeHandler
Item = Nothing
Next
End If
FormsHandler = New List(Of Form)
Me.Text = Me.Text.Split({" ("}, StringSplitOptions.RemoveEmptyEntries)(0)
End Sub
End Class
注意:
先前的代码位于应用程序的开始表单"中(用于测试),但最好在需要时在项目中包含一个Module
,而无需触摸当前代码.
Note:
This previous code is placed inside the app Starting Form (for testing), but it might be preferable to have a Module
to include in the Project when needed, without touching the current code.
要使其正常工作,请添加一个包含Public Sub Main()
的新Module
(名称为Program
),并更改Project属性以从Sub Main
而不是Form
启动应用程序. >
删除使用应用程序框架"上的复选标记,然后从启动对象"组合中选择子主要".
To get this to work, add a new Module
(named Program
) which contains a Public Sub Main()
, and change the Project properties to start the application from Sub Main
instead of a Form
.
Remove the checkmark on "Use Application Framework" and choose "Sub Main" from the "Startup object" Combo.
所有代码都可以通过以下修改转移到Sub Main
proc中:
All the code can be transfered to the Sub Main
proc with a couple of modifications:
Imports System
Imports System.Diagnostics
Imports System.Windows
Imports System.Windows.Forms
Imports System.Windows.Automation
Module Program
Friend GlobalHandlerEnabled As Boolean = True
Friend FormsHandler As List(Of Form) = New List(Of Form)
Friend ResizeHandler As EventHandler
Public Sub Main()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Dim MyMainForm As MainForm = New MainForm()
ResizeHandler =
Sub(obj, args)
Dim CurrentForm As Form = TryCast(obj, Form)
CurrentForm.Text = CurrentForm.Text.Split({" ("}, StringSplitOptions.None)(0) &
$" ({CurrentForm.Width}, {CurrentForm.Height})"
End Sub
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent,
AutomationElement.RootElement,
TreeScope.Subtree,
Sub(UIElm, evt)
If Not GlobalHandlerEnabled Then Return
Dim element As AutomationElement = TryCast(UIElm, AutomationElement)
If element Is Nothing Then Return
Dim NativeHandle As IntPtr = CType(element.Current.NativeWindowHandle, IntPtr)
Dim ProcessId As Integer = element.Current.ProcessId
If ProcessId = Process.GetCurrentProcess().Id Then
Dim CurrentForm As Form = Nothing
If Not MyMainForm.IsHandleCreated Then Return
MyMainForm.Invoke(New MethodInvoker(
Sub()
CurrentForm = Application.OpenForms.
OfType(Of Form)().
FirstOrDefault(Function(f) f.Handle = NativeHandle)
End Sub))
If CurrentForm IsNot Nothing Then
Dim FormName As String = FormsHandler.FirstOrDefault(Function(f) f?.Name = CurrentForm.Name)?.Name
If Not String.IsNullOrEmpty(FormName) Then
RemoveHandler CurrentForm.Resize, ResizeHandler
FormsHandler.Remove(FormsHandler.Where(Function(fn) fn.Name = FormName).First())
End If
AddHandler CurrentForm.Resize, ResizeHandler
FormsHandler.Add(CurrentForm)
CurrentForm.Invoke(New MethodInvoker(
Sub()
CurrentForm.Text = CurrentForm.Text & $" ({CurrentForm.Width}, {CurrentForm.Height})"
End Sub))
End If
End If
End Sub)
Application.Run(MyMainForm)
End Sub
End Module
这篇关于向项目中的所有表单添加事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!