向项目中的所有表单添加事件
Add an event to all Forms in a Project
如果我想在 Form
的标题中显示项目中每个 Form
的大小,最好的方法是什么?
我不想在每个 Form
.
中手动放置一个事件处理程序
我希望该过程是自动的。
类似于在调整大小事件上添加处理程序的重载 Load()
事件。
您可以按照@Jimi 的建议使用自动化。
您可以使用 My.Application.OpenForms 遍历所有打开的表单,但在打开新表单时没有帮助。
您可以创建一些继承 System.Forms.Form 的 ReportSizeForm class。并将表单的继承从常规 System.Windows.Forms.Form 更改为 ReportSizeForm。
这是对问题实施 Automation 解决方案的尝试。
问题:
将一个或多个事件处理程序附加到项目中的每个现有表单(或其子集),而无需 editing/modifying 这些 class 现有代码。
一个可能的解决方案来自 UIAutomation,它提供了检测何时打开新 Window 并向其自己的 Automation.AddAutomationEventHandler, when the EventId
of its AutomationEvent is set to a WindowPattern 模式的订阅者报告事件的方法。
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
(整数值)
这些值足以识别属于当前进程的Window; Window 句柄允许识别打开的 Form
实例,测试 Application.OpenForms() collection.
当表格被选中时,一个新的Event Handler
可以附加到一个选择的Event
。
通过扩展这个概念,可以创建一个预定义的事件列表和一个表单列表来附加这些事件。
有可能,在需要时将 class 文件包含在项目中。
请注意,某些事件在这种情况下将没有意义,因为 Automation
报告 Window 已经显示时的打开,因此 Load()
和Shown()
事件属于过去。
我已经用几个事件(Form.Resize()
和 Form.Activate()
)对此进行了测试,但在此处的代码中,为了简单起见,我只使用了 .Resize()
。
这是该过程的图形表示。
启动应用程序,事件处理程序未附加到 .Resize()
事件。
这只是因为 Boolean
字段设置为 False
.
单击按钮,Boolean
字段设置为 True
,启用事件处理程序的注册。
当.Resize()
事件被注册时,所有Forms的Title都会报告Window.
的当前大小
测试环境:
Visual Studio 2017 pro 15.7.5
.Net FrameWork 4.7.1
导入的命名空间:
System.Windows.Automation
参考程序集:
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
注:
之前的代码放在应用程序启动表单中(用于测试),但最好在需要时将模块包含在项目中,而不触及当前代码。
要使其正常工作,请添加一个包含 Public Sub Main()
的新模块(名为 Program
),然后更改项目属性以从 Sub Main()
而不是从 Sub Main()
启动应用程序表格.
删除 Use Application Framework
上的复选标记,然后从 Startup object
组合中选择 Sub Main
。
所有代码都可以通过一些修改转移到 Sub Main
proc:
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
如果我想在 Form
的标题中显示项目中每个 Form
的大小,最好的方法是什么?
我不想在每个 Form
.
中手动放置一个事件处理程序
我希望该过程是自动的。
类似于在调整大小事件上添加处理程序的重载 Load()
事件。
您可以按照@Jimi 的建议使用自动化。
您可以使用 My.Application.OpenForms 遍历所有打开的表单,但在打开新表单时没有帮助。
您可以创建一些继承 System.Forms.Form 的 ReportSizeForm class。并将表单的继承从常规 System.Windows.Forms.Form 更改为 ReportSizeForm。
这是对问题实施 Automation 解决方案的尝试。
问题:
将一个或多个事件处理程序附加到项目中的每个现有表单(或其子集),而无需 editing/modifying 这些 class 现有代码。
一个可能的解决方案来自 UIAutomation,它提供了检测何时打开新 Window 并向其自己的 Automation.AddAutomationEventHandler, when the EventId
of its AutomationEvent is set to a WindowPattern 模式的订阅者报告事件的方法。
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
(整数值)
这些值足以识别属于当前进程的Window; Window 句柄允许识别打开的 Form
实例,测试 Application.OpenForms() collection.
当表格被选中时,一个新的Event Handler
可以附加到一个选择的Event
。
通过扩展这个概念,可以创建一个预定义的事件列表和一个表单列表来附加这些事件。
有可能,在需要时将 class 文件包含在项目中。
请注意,某些事件在这种情况下将没有意义,因为 Automation
报告 Window 已经显示时的打开,因此 Load()
和Shown()
事件属于过去。
我已经用几个事件(Form.Resize()
和 Form.Activate()
)对此进行了测试,但在此处的代码中,为了简单起见,我只使用了 .Resize()
。
这是该过程的图形表示。
启动应用程序,事件处理程序未附加到 .Resize()
事件。
这只是因为 Boolean
字段设置为 False
.
单击按钮,Boolean
字段设置为 True
,启用事件处理程序的注册。
当.Resize()
事件被注册时,所有Forms的Title都会报告Window.
测试环境:
Visual Studio 2017 pro 15.7.5
.Net FrameWork 4.7.1
导入的命名空间:
System.Windows.Automation
参考程序集:
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
注:
之前的代码放在应用程序启动表单中(用于测试),但最好在需要时将模块包含在项目中,而不触及当前代码。
要使其正常工作,请添加一个包含 Public Sub Main()
的新模块(名为 Program
),然后更改项目属性以从 Sub Main()
而不是从 Sub Main()
启动应用程序表格.
删除 Use Application Framework
上的复选标记,然后从 Startup object
组合中选择 Sub Main
。
所有代码都可以通过一些修改转移到 Sub Main
proc:
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