排除在设计阶段的开发过程中遇到的故障
在为您的 Windows 窗体组件和控件创建自定义设计时体验时,可能会发生以下常见问题:
无法编译
在设计时无法调试
编译器错误:“未能找到类型或命名空间名称‘<类型名>’。”
设计时错误:“创建组件‘component name’失败。”
调试错误:“非法的线程间操作: 从不是创建控件‘control name’的线程访问它。”
设计时错误:“该文件内的类不是从可进行可视化设计的类继承,因此无法为该文件打开设计器。”
删除组件后标志符号仍存在
自定义行为掩盖了默认设计器行为
以意外的方式引发了设计器事件
序列化集合失败
设计器获取 UndoEngine 引用失败
设计环境无法识别对组件属性的更改
更改组件或设计器后刷新设计环境
关于新生成的 Windows 窗体的 FxCop 警告:DoNotInitializeUnnecessarily
分部类和 Windows 窗体设计器
早期的自定义控件将在设计器中导致意外的行为
寄宿设计器中的智能标记将引发异常
组件图标不出现在工具箱中
无法编译
在设计时开发的大部分时间内,您必须添加对设计时程序集 System.Design.dll 的引用。此程序集不包含在 .NET Framework 4 Client Profile 中。若要添加对 System.Design.dll 的引用,必须将项目的目标框架更改为**“.NET Framework 4”**。
在设计时无法调试
有两种方法可以调试您的设计时代码:
在您的代码中的战略点放置 MessageBox.Show 调用。
附加另一个 Visual Studio 实例以调试第一个实例的设计环境。
有关更多信息,请参见如何:访问设计时服务。
编译器错误:“未能找到类型或命名空间名称‘<类型名>’”
应当引用 System.Design 程序集。与设计器相关的类型位于 System.Design 程序集内。这样的类型包括 System.Windows.Forms.Design 和 System.ComponentModel.Design 命名空间中的类型。
此外,确保使用 Imports 或 using 关键字导入了您需要的命名空间。有关更多信息,请参见如何:在 Windows 窗体中访问设计时支持。
设计时错误:“创建组件‘component name’失败”
在设计图面上从**“工具箱”**创建组件或控件时,可能会收到此错误。下表列出了可能会引发此错误的两种原因。
原因 |
说明 |
注释 |
---|---|---|
缺少默认构造函数 |
您的组件或控件必须有一个默认构造函数,此构造函数不带任何参数。 |
设计环境必须有一个默认构造函数,才能够创建您的类型的实例。 |
组件是泛型类型 |
您的组件或控件不能是“泛型”类型,“泛型”类型又称“模板”类型或“参数化”类型。设计环境不支持泛型类型。 |
如果您的泛型类型是从 UserControl 派生的,您尝试在 Visual Studio 的“UserControl 测试容器”中运行它,则您会收到以下错误: 未能创建 UserControl 'name' 错误消息为“无法创建 Type.ContainsGenericParameters 为 True 的类型。” 将从列表中移除 UserControl。 虽然您的组件和控件不能是泛型类型,但是它们可以使用泛型类型。 |
设计时错误:“值不能为 null。参数名:‘component name’”
在设计图面上从**“工具箱”**创建组件或控件时,可能会收到此错误。可能性最大的原因是您在尝试使用为 64 位程序集中构建的组件或控件。Visual Studio 设计环境不支持 64 位组件。
调试错误:“非法的线程间操作: 从不是创建控件‘control name’的线程访问它。”
如果您在 Windows 窗体应用程序中使用多线程,调用您的控件时一定注意要用一种线程安全的方式。此异常是由调试器引发的,在运行时不会出现,但我们极力建议您在发现此问题时进行修复。有关更多信息,请参见 如何:对 Windows 窗体控件进行线程安全调用。
设计时错误:“该文件内的类不是从可进行可视化设计的类继承,因此无法为该文件打开设计器”
具有组件或控件的文件可以包含多个类定义,但文件中的第一个类必须是您可以设计的类。文件中的第一个类必须实现 IComponent 接口,否则它必须是从 Component 类派生的或者是从一个从 Component 派生的类派生的。
删除组件后标志符号仍存在
如果您的自定义设计器创建了任何 Adorner 对象,您必须在您的设计器超出范围时从设计图面中删除这些对象。在您的设计器的 Dispose 方法中调用 BehaviorServiceAdornerCollection.Remove 来清除 Glyph 对象及相关的 Adorner 和 Behavior 对象。有关更多信息,请参见 如何:在设计模式下扩展控件的外观和行为。
自定义行为掩盖了默认设计器行为
默认控件设计器创建一个标志符号,该标志符号覆盖设计图面上的整个控件。这个标志符号叫做“主体标志符号”。如果您的自定义控件设计器创建一个与主体标志符号具有相同边界的标志符号,它将掩盖与主体标志符号关联的基础 Behavior 实现。这会阻止出现默认功能(例如智能标记和调整标志符号大小)。
您无法在 Behavior 对象之间传递消息,所以您无法处理鼠标消息,然后将其转发给任何基础 Behavior 对象。当您实现一个覆盖整个控件的标志符号时,您应负责您的自定义设计体验的整体外观和行为。
以意外的方式引发了设计器事件
如果您的自定义设计器将事件处理程序附加到设计器事件,如 ComponentRemovedActiveDesignerChanged 和 SelectionChanged,您必须在您的设计器的 Dispose 方法中分离您的事件处理程序。
如果不这样做,则可能导致在运行时出现意外行为。下面列出了可能会出现的一些症状:
错误信息框:“处理此命令时出错。”
错误信息框:“未将对象引用设置到对象的实例。”
删除组件或关闭设计器时未正确调用事件处理程序。
序列化集合失败
如果您要序列化您的自定义组件或控件的集合属性,请应用 DesignerSerializationVisibilityAttribute 并将其设置为 Content。有关更多信息,请参见 如何:使用 DesignerSerializationVisibilityAttribute 序列化标准类型的集合。
设计器获取 UndoEngine 引用失败
如果在加载窗体时尝试获取对 UndoEngine 服务的引用,GetService 方法将返回 null。
直到窗体完成其加载阶段,才创建和启用 UndoEngine 服务。窗体加载完毕后,对 GetService 的后续调用将返回 UndoEngine 引用。
一般来说,您很少需要直接引用 UndoEngine。那些您确实需要此引用的情形通常是由加载设计器之后发生的用户操作引起的。
设计环境无法识别对组件属性的更改
如果您直接设置属性,则设计环境无法识别对您的组件或控件的更改。对于引发的事件,如 ComponentChanged,您必须使用 PropertyDescriptor.SetValue 方法设置您的组件属性的值。这样便会向设计环境通报属性的更改,从而使设计图面和 PropertyGrid 控件能够正确更新。有关更多信息,请参见 如何:在设计模式下扩展控件的外观和行为。
DesignerAttribute 语法
通过将 DesignerAttribute 应用于您的自定义设计器设计的控件,将您的自定义设计器附加到此控件。
必须准确指定 DesignerAttribute 参数,否则设计环境不会加载您的自定义设计器。
更改组件或设计器后刷新设计环境
当您更改组件的设计时方面时,您必须重新生成组件项目。此外,如果有另外一个 Windows 窗体项目当前处于打开状态并且正在使用此组件,您可能需要刷新该项目才能看到更改。通常,必须关闭包含组件的设计窗口,然后重新将其打开。
关于新生成的 Windows 窗体的 FxCop 警告:DoNotInitializeUnnecessarily
Windows 窗体设计器使用 C# 语言为 Windows 窗体应用程序项目生成以下代码。
private System.ComponentModel.IContainer components = null;
根据应用的 FxCop 规则,FxCop 可能会生成“DoNotInitializeUnnecessarily”警告。这是因为公共语言运行时 (CLR) 的默认引用属性是 null。
如果设计器没有将 components 字段初始化为 null,则 C# 编译器将生成以下警告:
“不应对 Form1.components 赋值,Form1.components 将始终保持其默认值 null。”
您可以使用 SuppressMessageAttribute 消除 FxCop 警告,当这可能在类名称更改时引发维护问题。因此,我们建议您忽略 FxCop 警告。
分部类和 Windows 窗体设计器
默认情况下,Windows 窗体设计器将设计器序列化代码发给一个专用文件,此文件与您的组件的主文件是分开的。例如,在 Windows 窗体应用程序项目中,Form1 类的定义被分放在两个文件中,如下表所示。
文件(C# 文件名) |
功能 |
---|---|
Form1.cs |
主类文件 |
Form1.Designer.cs |
设计器发出的代码 |
文件(VB 文件名) |
功能 |
---|---|
Form1.vb |
主类文件 |
Form1.Designer.vb |
设计器发出的代码 |
一般来说,您不需要修改由 Windows 窗体设计器发出的代码。而是编辑主类文件。
Windows 窗体设计器使用 partial 关键字将 Form1 的实现划分成了两个单独的文件。这可以避免使设计器发出的代码与您的代码交错。有关 partial 关键字的更多信息,请参见 分部类和方法(C# 编程指南) 和 分部 (Visual Basic)。
Windows 窗体设计器不支持将可设计的类型的定义划分成两个以上的 partial 实现。此限制包括不允许创建一个新的类文件来包含类型的第三个分部定义,也不允许将类型的一个第三个分部类定义添加到主文件或设计器文件中。在 Windows 窗体设计器中无法显示以这种方式定义的成员。
早期的自定义控件将在设计器中导致意外的行为
当类型在设计器中无效时,ComponentSerializationService 会执行部分重载,用已更新的类型刷新设计器。早于 Visual Studio 2005 的 Visual Studio 版本将设计器完全重载。Visual Studio 2005 中的部分重载行为比完全重载速度更快,同时会保留撤消堆栈。
在 Visual Studio 2005 之前创建的组件和相应序列化程序可能无法进行部分重载。所创建的组件和控件仅在完全重载时进行反序列化,因此它们可能导致意外的行为。存在早期版本控件时的现象包括堆栈溢出、挂起或 Windows 窗体设计器中显示空白区域。
通过将以下设置添加到 devenv.exe.config 文件可以恢复为完全重载行为。如果是将 Visual Studio 2005 安装到默认位置,则此文件位于 C:\Program Files\Microsoft Visual Studio 8\Common7\IDE 文件夹中。
<appSettings>
<add key="EnableOptimizedDesignerReloading" value="false" />
</appSettings>
寄宿设计器中的智能标记将引发异常
如果在 Visual Studio 外部寄宿设计器,则设计器中的智能标记可能引发 NullReferenceException。若要解决此问题,请在设计器中提供 IUIService 引用并实现 Styles 属性。在由 Styles 公开的 IDictionary 中,将一个新的 Font 分配为“DialogFont”键所指定的元素,如下面的代码段所示。
Styles["DialogFont"] = new Font(...);
组件图标不出现在工具箱中
在 Visual Studio 中,当您使用 ToolboxBitmapAttribute 将图标与自定义组件关联时,对于自动生成的组件,位图将不会出现在工具箱中。若要查看位图,请使用**“选择工具箱项”**对话框重新加载控件。有关更多信息,请参见 工具箱图标。