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.
Select File, New, Project from the main menu in Visual Studio . NET, and then pick a Visual Basic Windows Application to create. A form will be created with a default name of Form1. Add a second form by right-clicking the project and selecting Add, Add Windows Form from the menu that appears.
Right-click on the project and choose Add > Form (Windows Forms). In the Name box, type a name for your form, such as MyNewForm. Visual Studio will provide a default and unique name that you may use.
Here is an attempt to implement an Automation solution to the problem.
The problem:
Attach one or more Event Handlers to each existing Form in a Project (or a subset of them), without editing/modifying these classes existing code.
A possible solution comes from UIAutomation, 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, for each AutomationElement that raises the AutomationEvent, reports:
Element.Name (corresponding to the Windows Title)Process ID
Window Handle (as an Integer value)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.
When the Form is singled out, a new Event Handler can be attached to an Event of choice.
By expanding this concept, it's possible to create a predefined 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.
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.
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.
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' Title will report the current size of the Window.

Test environment:Visual Studio 2017 pro 15.7.5.Net FrameWork 4.7.1
Imported Namespaces:System.Windows.Automation
Reference Assemblies:UIAutomationClientUIAutomationTypes
MainForm code:
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
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.
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 check mark on Use Application Framework and choose Sub Main from the Startup object Combo.
All the code can be transferred 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With