简体   繁体   English

Winforms:每当在我的应用程序中打开表单时,是否有一种通知方法?

[英]Winforms: Is there a way to be informed whenever a form gets opened in my application?

I have a "Switch to window" button on my main form that I'd like to be enabled only when other windows (in my app) are open. 我在主窗体上有一个“切换到窗口”按钮,只有在其他窗口(在我的应用程序中)打开时,我才希望启用它。 Is there some sort of event that is raised whenever a form is opened or closed that my main form can hook? 每当打开或关闭表单时,都会发生某种主窗体可以挂起的事件吗? (For example, perhaps some sort of way to track when Application.OpenForms has changed?) (例如,也许某种方式可以跟踪Application.OpenForms何时更改?)

(I understand that if this functionality were in a menu item, I could simply do the Application.OpenForms check when the menu was clicked, but this button is not in a menu.) (我知道,如果此功能在菜单项中,则可以简单地在单击菜单时执行Application.OpenForms检查,但是此按钮不在菜单中。)

You could define your own type FooForm which inherits from Form . 您可以定义自己的类型FooForm ,该类型从Form继承。 In the constructor of FooForm , fire a statically defined FormOpened event. FooForm的构造函数中,触发一个静态定义的FormOpened事件。

Then, all your forms would have a base type of FooForm instead of Form . 然后,您所有的表单都将具有FooForm的基本类型,而不是Form The event will be fired each time one of your forms is opened. 每次打开您的表单之一时,都会触发该事件。

I ended up building a window manager class and when a new form is created, it adds itself to the Window Manager's collection. 我最终建立了一个窗口管理器类,并在创建新表单时将其自身添加到窗口管理器的集合中。 You could encapsulate this functionality in a base Form class so you don't have to remember to do it. 您可以将此功能封装在一个基本Form类中,这样就不必记住要这样做了。 Then you can create events in the window manager class to notify you of things like this. 然后,您可以在窗口管理器类中创建事件,以将此类事件通知您。 You also get the benefit of being able to query the collection of windows in the manager class. 您还可以获得能够在manager类中查询窗口集合的好处。 I then used this class to be able to consolidate the functionality that builds a menu of open windows into a utility class. 然后,我使用此类来将将打开的窗口的菜单构建为实用程序类的功能进行合并。

You could use a MessageFilter and monitor for WM_SHOWWINDOW and WM_CLOSE messages. 您可以使用MessageFilter并监视WM_SHOWWINDOW和WM_CLOSE消息。

UPDATE: You can also use a windows hook similar to what's at https://blogs.msdn.microsoft.com/calvin_hsia/2016/11/30/its-easy-to-use-windows-hooks-even-from-c/ 更新:您还可以使用类似于https://blogs.msdn.microsoft.com/calvin_hsia/2016/11/30/its-easy-to-use-windows-hooks-even-from-c的Windows钩子/

Below is code that will write to the Console whenever a Form is opened or closed. 下面是在打开或关闭表单时将写入控制台的代码。


using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    class Form1 : Form
    {
        public Form1()
        {
            var newWindowBtn = new Button { Text = "New Window" };
            newWindowBtn.Click += (s, e) => new Form { Text = Guid.NewGuid().ToString() }.Show(this);
            Controls.Add(newWindowBtn);
        }
    }

    static class NativeMethods
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct CWPSTRUCT
        {
            public IntPtr lparam;
            public IntPtr wparam;
            public int message;
            public IntPtr hwnd;
        }

        public delegate IntPtr CBTProc(int code, IntPtr wParam, IntPtr lParam);

        public enum HookType
        {
            WH_CALLWNDPROC = 4,
        }

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool UnhookWindowsHookEx(IntPtr hookPtr);

        [DllImport("kernel32.dll")]
        public static extern uint GetCurrentThreadId();

        [DllImport("user32.dll")]
        public static extern IntPtr CallNextHookEx(IntPtr hookPtr, int nCode, IntPtr wordParam, IntPtr longParam);

        [DllImport("user32.dll")]
        public static extern IntPtr SetWindowsHookEx(HookType hookType, CBTProc hookProc, IntPtr instancePtr, uint threadID);
    }

    static class Program
    {
        [STAThread]
        static void Main()
        {
            var hook = NativeMethods.SetWindowsHookEx(NativeMethods.HookType.WH_CALLWNDPROC, Callback, IntPtr.Zero, NativeMethods.GetCurrentThreadId());
            try
            {
                Application.Run(new Form1());
            }
            finally
            {
                NativeMethods.UnhookWindowsHookEx(hook);
            }
        }

        static IntPtr Callback(int code, IntPtr wParam, IntPtr lParam)
        {
            var msg = Marshal.PtrToStructure(lParam);
            if (msg.message == 0x0018)//WM_SHOWWINDOW
            {
                var form = Control.FromHandle(msg.hwnd) as Form;
                if (form != null)
                {
                    Console.WriteLine($"Opened form [{form.Handle}|{form.Text}]");
                }
            }
            if (msg.message == 0x0010)//WM_CLOSE
            {
                var form = Control.FromHandle(msg.hwnd) as Form;
                if (form != null)
                {
                    Console.WriteLine($"Closed form [{form.Handle}|{form.Text}]");
                }
            }
            return NativeMethods.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
        }
    }
}

A simple global form counter: 一个简单的全局表格计数器:

public static class AppForms
{
    public static int OpenForms { get; private set; }
    public static event EventHandler FormShown;
    public static event EventHandler FormClosed;
    public static void Watch(Form form)
    {
        form.Shown += (sender, e) => { OpenForms++; FormShown?.Invoke(sender, e); };
        form.Closed += (sender, e) => { OpenForms--; FormClosed?.Invoke(sender, e); };
    }
}

The next step is to make a project-width search for InitializeComponent and add one line of code bellow it: 下一步是对InitializeComponent进行项目宽度的搜索,并在其后添加一行代码:

InitializeComponent();
AppForms.Watch(this);

Finally subscribe to the global events FormShown and FormClosed in your main form's constructor. 最后,在主窗体的构造函数中订阅全局事件FormShownFormClosed

AppForms.FormShown += (sender, e) =>
{
    this.Text = $"OpenForms: {AppForms.OpenForms}";
};
AppForms.FormClosed += (sender, e) =>
{
    this.Text = $"OpenForms: {AppForms.OpenForms}";
};

You don't need to worry about your forms not being garbage collected because of the event subscriptions. 您不必担心由于事件订阅而无法对表单进行垃圾回收。 The class AppForms is a subscriber, and subscribers don't keep references of publishers. AppForms类是订阅者,订阅者不保留发布者的引用。 Only the publishers keep references of subscribers . 只有发布者保留订户的引用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在运行时调整WinForms应用程序主窗体的起始大小和位置的最安全方法是什么? - What's the safest way to adjust my WinForms application main form starting size and position at run time? 有没有办法让我的“文件关联”不被应用程序打开? - Is there a way to let my “File association” not to be opened by the application? WinForms应用程序中的表单invalidate() - Form invalidate() in WinForms Application 获取winforms应用程序名称的正确方法是什么? - what is the right way of getting my winforms application's name? Winforms Form应用程序中的MVP - MVP within Winforms Form application 通过两个打开的表单最大化应用程序 - Maximize application with two opened form C# - 如何在我的 winforms 应用程序中从另一个 class 引用我当前的 Form1 实例? - C# - How to reference my current instance of Form1 from another class in my winforms application? 可以在winforms应用程序中形成包含另一种形式? - can form Contain another form in winforms application? WinForms:有什么方法可以在Application.Run(new Form1())之前获取窗口句柄? - WinForms: Is any way to get window handle before Application.Run(new Form1())? 空字符串在Winforms中从一种形式传递到另一种形式 - Empty string gets passed from one Form to another in Winforms
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM