向项目中的所有表单添加事件 [英] Add an event to all Forms in a Project

查看:96
本文介绍了向项目中的所有表单添加事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果要在项目中的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 Scope成员. treescope?view = netframework-4.7.2"rel =" nofollow noreferrer> TreeScope.SubTree .

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,对于每个引发AutomationEventAutomationElement,报告:
-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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆