Если я хочу отображать размер каждого Form
в моем проекте в заголовке Form
, что будет лучшим подходом?
Я не хочу вручную помещать обработчик событий в каждый Form
.
Я хочу, чтобы процесс быть автоматическим.
Что-то вроде перегруженного события Load()
, которое добавляет обработчик события изменения размера.
Добавить событие во все формы в проекте
Ответы (2)
Вот попытка реализовать Автоматизация решения проблемы.
Проблема:
Прикрепите один или несколько обработчиков событий к каждому существующему Form
в проекте WinForms
(или к их подмножеству), не редактируя/модифицируя существующий код этих классов.
Возможное решение исходит от класса Automation
, который предоставляет средства для обнаружения открытия нового окна и сообщает об этом событии своим собственным подписчикам Automation.AddAutomationEventHandler, when the EventId
of its AutomationEvent имеет значение WindowPattern.
Шаблон AutomationElement должно быть задано значение AutomationElement.RootElement и член Scope
в TreeScope.SubTree.
Automation
, для каждого AutomationElement
, вызывающего AutomationEvent
, сообщает:
- Element.Name
(соответствует заголовку Windows)
- Process ID
- Window Handle
(в виде целого числа)
Этих значений вполне достаточно, чтобы идентифицировать окно, принадлежащее текущему процессу; дескриптор окна позволяет идентифицировать открытый экземпляр Form
, тестируя коллекция Application.OpenForms() .
Когда форма выделена, новый Event Handler
может быть присоединен к выбранному Event
.
Расширяя эту концепцию, можно создать предопределенный список событий и список форм для прикрепления этих событий.
Возможно, с файлом класса для включения в проект при необходимости.
Обратите внимание, что некоторые события не будут иметь значения в этом сценарии, поскольку Automation
сообщает об открытии окна, когда оно уже показано, поэтому события Load()
и Shown()
относятся к прошлому.
Я проверил это с парой событий (
Form.Resize()
и Form.Activate()
), но в этом коде для простоты я использую только .Resize()
.Это графическое представление процесса.
При запуске приложения обработчик событий не привязан к событию .Resize()
.
Это просто потому, что для поля Boolean
задано значение False
.
При нажатии кнопки Boolean
в поле установлено значение True
, что позволяет зарегистрировать обработчик событий.
Когда зарегистрировано событие .Resize()
, все Forms
Window Title
будут сообщать о текущем размере окна.
Тестовая среда: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
Примечание.
Этот предыдущий код помещается в стартовую форму приложения (для тестирования), но может быть предпочтительнее иметь Module
для включения в проект при необходимости, не затрагивая текущий код.
Чтобы это заработало, добавьте новый Module
(с именем Program
), который содержит Public Sub Main()
, и измените свойства проекта, чтобы приложение запускалось с Sub Main
вместо Form
.
Снимите флажок "Использовать Application Framework" и выберите " Sub Main" из комбо "Startup object".
Весь код можно перенести в прок Sub Main
с парой модификаций:
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
FormClosed
событие как Что ж?
- person Visual Vincent; 25.07.2018
NullRef
вы получаете ее новый экземпляр), оставляя возможность обратной совместимости для поклонников.
- person Jimi; 25.07.2018
VB.Net
для WinForms
. Так что это своего рода эксперимент (посмотрите, что я написал @Visual Vincent). Он успешно работает в паре программ, написанных на разных языках. Если я найду что-то примечательное, я сообщу вам.
- person Jimi; 25.07.2018
Module
(с Sub Main
) для включения в проект вместо редактирования формы запуска. Может быть полезно.
- person Jimi; 25.07.2018
Вы можете использовать автоматизацию, как предложил @Jimi.
Вы можете использовать My.Application.OpenForms для перебора всех открытых форм, но это не поможет при открытии новой формы.
Вы можете создать некоторый класс ReportSizeForm, который наследует System.Forms.Form. И измените наследование ваших форм с обычного System.Windows.Forms.Form на ваш ReportSizeForm.
Automation.AddAutomationEventHandler
сWindowPattern.WindowOpenedEvent
возникает при открытии окна (любого окна). Вы можете изменить заголовок формы, когда обнаружите, что она была активирована. Однако автоматизация немного деликатна в обращении. - person Jimi   schedule 24.07.2018Shown
. - person Visual Vincent   schedule 24.07.2018System.Windows.Forms.Form
, вам просто нужно изменить их, чтобы вместо этого наследоватьYourBaseForm
. - person Visual Vincent   schedule 24.07.2018Inherits System.Windows.Forms.Form
наInherits YourBaseForm
одним щелчком мыши. Visual Studio также поддерживает это через поле поиска (CTRL + F
в редакторе кода), но не позволяет фильтровать файлы для поиска. - person Visual Vincent   schedule 24.07.2018