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

时间:2011-09-05 21:50:55

标签: c# user-controls base-class onload-event intermittent

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

基本上我有一些带有单个基类的用户控件,它抓取了我的主窗体窗口的实例,因此用户控件可以访问主窗体属性并可以调用主窗体上的方法。这是一个片段(this.frmParent是公共成员):

    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变量可能会也可能不会填充非null值。

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

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

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

this.frmParent = new frmMain();

但是,我仍然需要调用CreateControl()才能触发load事件,我不喜欢要求未来的维护者必须明确了解不同的行为命令。换句话说,我希望我的用户控件以相同的方式工作,以保持简单维护。

我已将我的代码分开,并且无法弄清楚为什么有时用户控件的加载事件可能会或可能不会触发,以及为什么用户控件基类中对FindForm()的调用失败。

有没有人对如何解决这些问题有任何想法?谢谢。

2 个答案:

答案 0 :(得分:2)

通过让用户控制知道它所放置的表单,你犯了一个相当严重的OOP罪。 假设是一个不关心其容器的独立类,您使用事件让容器知道容器可能感兴趣的类中发生的任何事情。 Winforms中任何标准控件都遵循的原则。例如,TextBox从不关心它被丢弃的形式。

这就是理论,实践并不常常那么干净。您遇到的问题是OnLoad方法(也称为Load事件)因其他原因而触发。它在创建本机Windows句柄时运行。当表单的窗口被创建时,由Show()方法调用触发,发生正常表单的IntializeComponent()方法之后。

如果您的用户控件的构造函数中有任何代码要求Handle属性具有值,则Winforms会强制并为您的控件创建Windows句柄并触发Load事件。很快,在表单的InitializeComponent()方法有机会调用其Controls.Add()方法之前。 Parent属性尚未引用该表单。在FindForm上的Kaboom()。

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

答案 1 :(得分:0)

您有多少个主要表单实例?如果您只有一个 - 并且只有一个,您可以将其作为单身人士访问。

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.Singleton作为参考(甚至在(最特别是!)你的Program.cs中,其中最初可能构造表单)。此外,您还可以通过在用户控件中调用frmMain.Singleton来获取对主表单的全局可访问引用。

至于你的ucBase_Load没有加载的原因,我没有受过教育的猜测是你也在具体的用户控件中处理事件,并且它以某种方式阻止了基本处理程序的触发。如果是这种情况,请在具体用户控件的事件处理程序中添加base.OnLoad()

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

顺便说一句,你的工作似乎非常脏,它没有给你一个对你的主表单的引用,它创建了一个新的主表单实例(这是从未表现过。)