在实例化WinForm用户控件时不总是调用基类

本文关键字:调用 基类 实例化 WinForm 用户 控件 | 更新日期: 2023-09-27 18:07:30

我有一个奇怪的问题,有一个解决方案,但我想保持代码尽可能相似。问题集中在用户控件基类中的一个特定变量上,该变量可能为空,也可能不为空,而且它永远不应该为空。

基本上我有一个单一的基类,抓住我的主窗体窗口的一个实例的用户控件的数量,所以用户控件可以访问主窗体属性,并可以调用主窗体上的方法。下面是一个代码片段(this.frmParent是一个public成员):

    private void ucBase_Load( object sender, EventArgs e )
    {
        // Establish the link to the main form.
        this.frmParent = FindForm() as frmMain;
    }

然后每个用户控件共享这个基类:

public partial class ucLiberty : ucBase

然后在主表单中,我将像这样调用用户控件:

                ucLiberty Liberty = new ucLiberty();
                IQDevicePath = Liberty.GetIQDrivePath();

由于某种原因,当我实例化用户控件(在本例中它在主表单中)时,基类中的frmParent变量可能会也可能不会使用非空值填充。

我注意到用户控件中的加载事件没有触发。我发现了一个叫做CreateControl()的方法,它应该强制创建控件,然后我的加载事件开始触发,但是当我在调试器中跟踪执行时,我到达了试图填充frmParent的基类,FindForm()并不总是返回一个非空值。

我有其他用户控件,我没有这个问题,它们之间的区别是,一些用户控件有子控件,一些没有子控件。没有子控件的那个有这个问题。

我的解决方法是监视FindForm()在哪个用户控件中失败,并在该用户控件的load事件中,通过调用主表单的构造函数来赋值,如下所示:

this.frmParent = new frmMain();

然而,我仍然需要调用CreateControl()来触发加载事件,并且我不喜欢要求未来的维护者必须具有不同行为指令的显式知识的想法。换句话说,我希望我的用户控件都以相同的方式工作,以保持维护简单。

我已经把我的代码分开了,不能弄清楚为什么有时用户控件的加载事件可能触发或可能不触发,以及为什么在用户控件基类中调用FindForm()失败。

谁有解决这些问题的办法?谢谢。

在实例化WinForm用户控件时不总是调用基类

让用户控件知道它所在的表单,这是在犯相当严重的OOP错误。假设是一个独立的类,不关心它的容器,您使用事件让容器知道容器可能感兴趣的类中发生的任何事情。Winforms中任何标准控件都严格遵循的设计原则。例如,TextBox从不关心它放在什么样的表单上。

这是理论,实践往往不那么干净。你遇到的问题是,OnLoad方法(又名Load事件)触发一个不同的原因。它在创建本机Windows句柄时运行。通常在创建窗体时发生,由Show()方法调用触发。它是之后的表单的initializecomponent()方法。

如果你在用户控件的构造函数中有任何代码要求Handle属性有一个值,那么Winforms就会强制为你的控件创建Windows句柄并触发Load事件。太早了,在表单的InitializeComponent()方法有机会调用它的Controls.Add()方法之前。Parent属性还没有引用表单。

使用调试器很容易进行诊断。在用户控件的OnLoad方法上设置断点。堆栈跟踪将直接带您到触发句柄创建的语句。

你有多少主表单实例?如果你只有一个——而且永远只有一个,你可以把它作为一个单例来访问。

public class frmMain : Form
{
     private static frmMain s_Singleton;
     public static frmMain Singleton
     {
          get
          {
              if (s_Singleton == null) s_Singleton = new frmMain();
              return s_Singleton;
          }
     }
}

所以不要直接调用构造函数,而是调用frmMain。引用的单例(即使在(特别是在)您的Program.cs中,表单很可能是最初构造的)。此外,您有一个对主表单的全局可访问引用,可通过在用户控件

中调用frmMain.Singleton来获得。

至于为什么你的ucBase_Load不加载的原因,我没有根据的猜测是,你也在具体的用户控件中处理事件,它以某种方式阻止基处理程序的触发。如果是这种情况,请在具体用户控件的事件处理程序中添加base.OnLoad()

至于为什么FindForm不起作用,可能是因为在用户控件的构造函数完成之前调用了该方法。这似乎不太可能,但在没有看到代码的情况下很难确定。这可能是问题的原因是控件的父等是在构造函数中设置的。但由于您是在Load-event中处理它,因此似乎不太可能。您可以通过将逻辑移动到OnParentChanged事件来验证这个理论。

顺便说一下,你的工作周围似乎非常脏,它没有给你一个引用到你的主表单,它创建了一个新的主表单实例(这是从未显示)。