Winforms设计器是如何实例化我的窗体的?

5
我正在开发自己的WinForms设计器。它必须能够加载现有的自定义表单类型。我遇到的问题之一是没有默认构造函数的表单:我的代码当前在将其加载到设计器之前实例化表单,这需要一个默认构造函数。
另一方面,VS2008能够加载这样的表单。我相信它实际上并没有实例化我的表单(如这个问题中所述):甚至不执行默认构造函数。而且它也没有真正执行InitializeComponent()。我只是在那个函数中添加了一个消息框,但它并没有显示出来。
看起来它动态模仿自定义表单类型,并只执行它认为相关的InitializeComponent中的部分代码。
有人知道在哪里可以找到关于VS设计师如何工作的更多信息吗?
谢谢您。
注意:我找到了这个相关问题,但没有令人满意的答案。
编辑:附加信息:Steve向我指出了CodeDom,这非常有趣。但我的问题是,我需要加载到设计器中的类型已经编译好了,而不是作为源代码可用。我找不到任何方法将CodeDom反序列化应用于编译代码。

1
您可能会在自定义现有的Windows表单设计元素方面运气更好,而不是重新发明它们。它们处理各种情况,您在用户抱怨缺少它们之前可能都没有想过。 - John Saunders
1
约翰,这正是我的观点。你在哪里看到我重新发明了什么? - Serge Wautier
2个回答

14

在这里发现了这篇文章:

当你在VS中打开一个新的Windows应用程序项目时,你会看到一个名为Form1的空白窗体。现在,由于您还没有构建该项目,因此设计师如何能创建Form1的实例并显示它呢?其实,设计师并没有真正实例化Form1。它正在创建Form1的基类System.Windows.Forms.Form的实例。只要你有面向对象编程的基本知识,你会觉得这很自然。当你设计Form1时,你从基类Form开始,并对其进行自定义。这正是设计师帮助你完成的工作。

现在假设你添加了一堆控件到Form中并关闭了设计器。当你重新打开设计器时,这些控件仍然存在。但是,基类Form上并没有这些控件,所以如果设计器没有运行Form1的构造函数,它又是如何显示这些控件的呢?设计器通过反序列化InitializeComponent中的代码来实现这一点。设计器支持的每种语言都有一个CodeDomProvider,它负责提供一个解析器来解析InitializeComponent中的代码,并创建其的CodeDom表示。然后,设计器调用一组CodeDomSerializers将其反序列化为它可以添加到设计时窗体中的实际控件(或更广义地说,组件)。现在,我在上述说明中略过了许多细节,但是这里的重点是Form1的构造函数和InitializeComponent从未真正被调用。相反,设计器解析InitializeComponent中的语句以查找要实例化并添加到窗体的控件。


以上是Visual Studio中Windows Forms设计器加载表单的方法。如果您正在寻找一种创建没有默认构造函数但仍可访问包含组件/控件实例的方式,我不知道解决方案。唯一我知道可以绕过缺少默认构造函数的方法是使用FormatterServices.GetUninitializedObject,但请注意...

因为对象的新实例被初始化为零,并且没有运行构造函数,所以该对象可能不代表该对象认为有效的状态。

我也有一个需要实例化已编译表单的应用程序,但是我一直使用的是Activator.CreateInstance,并要求其他开发人员至少包括一个私有默认构造函数,如果他们想在我的应用程序中使用它们的表单。由于我们拥有整个代码库,而且每个人都知道这个要求,这不是问题,并且对我们很有帮助。


Steve,非常有趣的链接。谢谢!它基本上确认了我想象中的一般机制,但我不知道CodeDom。我会立即深入研究它的;-) - Serge Wautier
当我尝试加载已编译的自定义表单类型时,CodeDoms似乎让我陷入了死胡同 :-( - Serge Wautier
"没有构造函数被运行" -- 另外需要注意的是,这不适用于 UserControl。如果我在 UserControl 的构造函数中放置一个无限循环,并尝试将其拖到窗体中,它会锁定 Visual Studio。还有一些其他例外情况在链接的 MSDN 博客页面的评论中提到。" - jrh

1
作为对Steve答案的补充,如果您向项目添加一个新的Windows窗体,但将其设置为抽象的,您仍然可以在设计器中打开它。但是,如果您添加另一个窗体,并使其派生自第一个(抽象)窗体,则在尝试在设计器中打开该窗体时会出现错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接