0

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

基本上,我有许多带有单个基类的用户控件,它们抓取我的主窗体窗口的一个实例,因此用户控件可以访问主窗体属性并可以调用主窗体上的方法。这是一个片段(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 变量可能会或可能不会填充非空值。

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

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

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

this.frmParent = new frmMain();

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

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

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

4

2 回答 2

2

通过让用户控件知道它所在的表单,你犯了一个相当严重的 OOP 罪。它应该是一个不关心其容器的独立类,您使用事件让容器知道容器可能感兴趣的类中发生的任何事情。 Winforms 中的任何标准控件。例如,TextBox 从不关心它放在什么样的表单上。

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

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

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

于 2011-09-05T22:41:28.797 回答
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-event 中处理它,所以它似乎不太可能。您可以通过将逻辑移至 OnParentChanged 事件来验证这一理论。

顺便说一句,您的工作似乎很脏,它没有给您对主窗体的引用,它创建了一个新的主窗体实例(从未显示)。

于 2011-09-05T22:42:14.770 回答