在 Visual Studio 中进行 WPF, UWP, Silverlight 开发时,经常会遇到 XAML 设计器由于遭遇异常而无法正常显示设计器视图的情况。很多时候由于最终生成的项目在运行时并不会出现同样的异常,我们往往会忽略设计时异常,仅仅通过关闭设计器在设计时执行代码的功能来规避设计器异常的问题。
规避设计时异常
规避设计时异常,一般有两种做法:
关闭设计器执行代码
在 Visual Studio 2015 中,XAML 设计器左下角的这个按钮可以随时开启或关闭设计器执行代码。如果在 UI 相关的代码中存在问题,而设计器不去执行,就不会造成设计器异常。但代价是无法在设计器中预览数据绑定等效果。
判断设计时还是运行时
另一种做法是在代码中检测当前代码是否运行在设计时中,如果是,则不继续执行。对于 UWP 应用,可以通过 Windows.ApplicationModel.DesignMode.DesignModeEnabled
属性检测当前是否出于设计时;对于 WPF,可以使用 System.ComponentModel.DesignerProperties.GetIsInDesignMode(DependencyObject)
方法进行检测。
但很多时候我们需要在设计器中实时查看自定义控件的表现、预览数据绑定的效果,这时就不能仅仅通过关闭设计器执行代码或通过判断是否处于来规避设计时异常了,而必须对设计时代码进行调试,定位并解决问题。
调试设计时代码
要对设计时代码进行调试,原理与平时调试项目是相同的,只需一些额外的步骤。
假设我们有一个 UWP 项目,项目中构建了一个自定义控件,代码如下:
public class MyCustomControl : ContentControl { public MyCustomControl() { if (DesignMode.DesignModeEnabled) { this.Content = "✔DESIGN MODE ENABLED!"; throw new Exception("my exception for design mode!"); } else { this.Content = "✖DESIGN MODE DISABLED!"; } throw new Exception("my exception for design mode!"); } private string _myProperty1 = ""; public string MyProperty1 { get { return _myProperty1; } set { _myProperty1 = value; } } }
可以看到在控件的构造函数中判断当前是否处于设计时,如果是,则抛出一个异常。假设此时我们不清楚自定义控件中造成设计器异常的原因,我们需要在自定义控件的构造中设断点进行中断调试。
调试步骤如下:
- 关闭当前 Visual Studio 打开的所有文档。
- 打开进程管理器,结束所有名为 XDesProc.exe 的进程。该进程即 XAML 设计器的进程。
- 在要调试的项目中,打开任意 XAML 页面。
- 启动一个新的 Visual Studio 实例,并通用打开要调试的项目,在代码中的目标位置设好断点。
- 在新启动的 Visual Studio 中,点击
调试(Debug)
菜单,选择附加到进程 (Attach to Process)
。 - 在
附加到进程 (Attach to Process)
窗口中,将附加到 (Attach to):
更改为 Managed (v4.6, v4.5, v4.0) code,注意不是Managed (CoreCLR)
。 - 在
可用进程 (Available Processes)
中选择XDesProc.exe
,然后点击附加 (Attach)
按钮开始调试。注意如果前述步骤操作正确,这一步骤中进程列表里只会有一个XDesProc.exe
,也就是第一个 Visual Studio 的设计器进程。 - 在第一个 Visual Studio 中,确保开启了设计器执行代码按钮,打开包含自定义控件的 XAML 页面,此时设计器会对自定义控件实例化,执行自定义控件的构造函数,继而触发在第二个 Visual Studio 的调试中设置的断点。
对于 WPF 项目,要调试设计时异常,所用步骤与上述步骤相同。一般造成设计时异常的常见原因是自定义控件、XAML 页面代码或数据绑定所引用的资源在设计时没有实例化;自定义控件的计算和布局方法存在错误等。无论如何,在设计时可见 XAML 元素并对 UI 行为进行预览对于完善开发体验,确保应用正常工作有着重要意义,推荐不要因为最终生成的应用可以正常运行就忽略设计时异常,而是通过调试进行定位修复。