vb.net winforms events ui-automation

vb.net - Agregar un evento a todos los formularios en un proyecto



winforms events (2)

Si quiero mostrar el tamaño de cada Form en mi Proyecto en el Título del Form , ¿cuál será el mejor enfoque?
No quiero poner manualmente un controlador de eventos en cada Form .
Quiero que el proceso sea automático.
Algo así como un evento Load() sobrecargado que agrega un controlador en el evento de cambio de tamaño.


Aquí hay un intento de implementar una solución de Automation para el problema.

El problema:
Adjunte uno o más controladores de eventos a cada Form existente en un proyecto de WinForms (o un subconjunto de ellos), sin editar / modificar el código existente de estas clases.

Una posible solución proviene de la clase de Automation , que proporciona medios para detectar cuándo se abre una nueva ventana e informa del evento a los suscriptores de su propio Automation.AddAutomationEventHandler , cuando el EventId de su AutomationEvent se establece en un patrón WindowPattern .
El miembro AutomationElement debe establecer en AutomationElement.RootElement y el miembro Scope en TreeScope.SubTree .

Automation , para cada AutomationElement que genera AutomationEvent , informa:
- Element.Name (correspondiente al título de Windows)
- la Process ID
- la Window Handle la Window Handle (como un valor entero)

Estos valores son suficientes para identificar una ventana que pertenece al proceso actual; El manejador de la ventana permite identificar la instancia del Form abierto, probando la colección Application.OpenForms() .

Cuando se selecciona el formulario, se puede adjuntar un nuevo Event Handler a un Event de su elección.

Al expandir este concepto, es posible crear una Lista de Eventos predefinida y una Lista de Formularios para adjuntar estos eventos.
Posiblemente, con un archivo de clase para incluir en un proyecto cuando sea necesario.

Como nota, algunos eventos no serán significativos en este escenario, ya que la Automation informa la apertura de una ventana cuando ya se muestra, por lo tanto, los eventos Load() y Shown() pertenecen al pasado.

He probado esto con un par de eventos ( Form.Resize() y Form.Activate() ), pero en el código aquí estoy usando .Resize() para simplificar.

Esta es una representación gráfica del proceso.
Al iniciar la aplicación, el .Resize() eventos no se adjunta al evento .Resize() .
Es solo porque un campo Boolean está configurado en False .
Al hacer clic en un botón, el campo Boolean se establece en True , lo que permite el registro del controlador de eventos.
Cuando se registra el evento .Resize() , todos los Window Title Forms informarán el tamaño actual de la ventana.


Entorno de prueba :
Visual Studio 2017 pro 15.7.5
.Net FrameWork 4.7.1

Espacios de nombres importados:
System.Windows.Automation

Asambleas de referencia :
UIAutomationClient
UIAutomationTypes

Código de 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

Nota:
Este código anterior se coloca dentro del Formulario de inicio de la aplicación (para pruebas), pero es preferible tener un Module para incluir en el Proyecto cuando sea necesario, sin tocar el código actual.

Para que esto funcione, agregue un nuevo Module (denominado Program ) que contenga un Public Sub Main() , y cambie las propiedades del Proyecto para iniciar la aplicación desde Sub Main lugar de un Form .
Elimine la marca de verificación en "Usar marco de aplicación" y elija "Sub Main" del Combo "Objeto de inicio".

Todo el código se puede transferir al proceso Sub Main con un par de modificaciones:

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


Puedes usar la automatización como se sugiere en @Jimi.

Puede usar My.Application.OpenForms para recorrer en iteración todos los formularios abiertos, pero no ayudará cuando se abra un nuevo formulario.

Puede crear alguna clase ReportSizeForm que herede System.Forms.Form. Y cambie la herencia de sus formularios de System.Windows.Forms.Form regular a su ReportSizeForm.