【问题标题】:How to know if any form is created in the application C#?如何知道是否在应用程序 C# 中创建了任何表单?
【发布时间】:2019-05-11 06:45:48
【问题描述】:

我知道有从表单类继承的类的事件处理程序。通过使用这些事件,我们可以了解是否创建了表单。例如表单加载事件处理程序。 我正在从事由太多表格组成的几乎大型项目。在这个项目中,用户可以创建多个工作空间,并在每个工作空间中打开不同的表单。当用户关闭工作区时,所有相关的表单都应该关闭。为了实现此功能,我考虑了一个字典,其中包含工作区 id 的键,其值是相关的打开表单。因此,我应该在用户打开表单时添加这些值。如果我向每个表单加载的事件处理程序添加一行代码,则可以做到这一点,但这需要时间。我想知道我是否可以理解从主窗体打开一个窗体。

ps:我熟悉 Application.Openforms。在这种情况下没有帮助。

【问题讨论】:

  • 显示创建和显示()表单的代码
  • @CaiusJard 在不同的类中,根据用户点击的按钮创建了不同的表单。没有一个类可以创建所有表单
  • UI 自动化,使用 WindowPattern.WindowOpenedEvent 事件:Run event when any Form loads(简单实现)。
  • VB.Net 版本:Add an event to all Forms in a Project(请阅读注释)。
  • 请定义什么是工作区。是 Form 本身创建了需要与之关联的其他表单吗?

标签: c# .net winforms


【解决方案1】:

过去我也遇到过类似的问题,当时我首先想到的是使用 System.Reflection,但这是不可能的,因为您无法在运行时确定创建了哪个实例。

我还没有找到任何“神奇”的解决方案来解决这个问题,但是我找到的最简单和安全的解决方案是扩展Form 基类(到我的自定义SuperForm)并使用这个基类来协调和更新此基类中所有实例的静态列表(通过覆盖 OnLoad()OnClosed() 方法)。
显然所有的申请表都必须继承自SuperForm

另外,我发现在一个庞大的 winforms 应用程序中,让所有表单都继承一个您自己扩展的类是一种很好的做法,因为它可以让您对应用程序有更多的控制权,并且可以让您将来的生活更轻松。

这是一个架构示例:

超窗体类:

public class SuperForm : Form
{
    private bool _isFormActive = false;
    public bool isFormActive
    {
        get
        {
            return this._isFormActive;
        }

        set
        {
            this._isFormActive = value;
        }
    }

    protected override void OnLoad(EventArgs e)
    {
        this.isFormActive = true;
        AppForms.Add(this);
        base.OnLoad(e);
    }

    protected override void OnClosed(EventArgs e)
    {
        this.isFormActive = false;
        AppForms.Remove(this);
        base.OnClosed(e);
    }
}

带有静态列表的静态类来处理应用程序表单实例:

public static class AppForms
{
    private static List<SuperForm> _AppFormsList;
    public static List<SuperForm> AppFormsList
    {
        get
        {
            if (_AppFormsList == null)
            {
                _AppFormsList = new List<SuperForm>();
            }
            return _AppFormsList;
        }
        set
        {
            _AppFormsList = value;
        }
    }

    public static void Add(SuperForm instance)
    {
        AppFormsList.Add(instance);
    }
    public static void Remove(SuperForm instance)
    {
        AppFormsList.Remove(instance);
    }
}

实施:

public partial class Form1 : SuperForm
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {

    }

    private void button1_Click(object sender, EventArgs e)
    {   
        // you dont have to use polymorphism...
        SuperForm f = new Form2();
        f.Show();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        // you dont have to use polymorphism...
        SuperForm f = new Form3();
        f.Show();
    }

    private void button3_Click(object sender, EventArgs e)
    {
        // show all the forms that are active
        foreach (var frm in AppForms.AppFormsList)
        {
            MessageBox.Show(((SuperForm)frm).isFormActive.ToString());
        }
    }
}

【讨论】:

  • 感谢您分享您的解决方案。我曾考虑过您的解决方案,但努力是添加一行代码来加载每个表单的事件处理程序。
  • 欢迎您,如果您能找到您正在寻找的解决方案,请更新。
【解决方案2】:

感谢@Jimi 和@TnTinMn。 最简单的解决方案(需要较少的努力)是使用 WindowOpenedEvent。此事件侦听器识别已打开的窗口。值得一提的是,当在您的操作系统上打开任何窗口时,都会引发此事件。我们可以将打开/创建的窗口的进程 ID 与我们的应用程序进程 ID 进行比较,以了解哪个表单与应用程序相关。请注意 UIAutomationClientUIAutomationType DLL 应该被添加到项目中。这些 DLL 位于: C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\UIAutomationClient.dll

我已将以下代码添加到 Program.cs 文件中。

           MainForm mainForm = new MainForm();
           Automation.AddAutomationEventHandler(
           WindowPattern.WindowOpenedEvent, AutomationElement.RootElement,
           TreeScope.Subtree, (uiElm, evt) =>
           {
               AutomationElement element = uiElm as AutomationElement;
               if (element == null) return;
               try
               {
                   if (element.Current.ProcessId == Process.GetCurrentProcess().Id)
                   {
                       IntPtr elmHandle = (IntPtr)element.Current.NativeWindowHandle;
                       Form form = Application.OpenForms.OfType<Form>()
                           .Where(f => (f.AccessibilityObject as Control.ControlAccessibleObject).Handle == elmHandle)
                           .FirstOrDefault();
                       mainForm.UpdateTabFormsDict(form); // adding a open form to the current tab
                   }
               }
               catch (ElementNotAvailableException)
               { /* May happen when Debugging => ignore or log */
               }
           });

更多信息请见Run event when any Form loads。 WindowPattern 还提供WindowClosedEvent

【讨论】:

  • 您应该订阅辅助线程上的自动化事件。请参阅:UI Automation Threading Issues
  • @TnTinMn 该文档已声明使用 AddAutomationEventHandler 是安全的,就像我们使用的那样。我不明白你的意思是什么。
  • 重新阅读最后一段,其中指出“当订阅可能源自客户端应用程序 UI 的事件时,您必须在非 UI 线程上调用 AddAutomationEventHandler 或相关方法”。您在用于创建mainForm(UI 线程)的同一线程中调用Automation.AddAutomationEventHandler。它可能会起作用。但是,如果他们麻烦您说您应该在辅助线程中执行此操作,那么遵循给出的指导通常是个好主意。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-11
  • 2013-02-26
  • 2015-10-19
  • 2020-04-23
  • 2013-01-04
  • 2014-01-21
  • 1970-01-01
相关资源
最近更新 更多