当我们的 WPF 程序未按预期工作时,我们需要对其进行调试,就像调试任何其他语言一样。 然而,一开始这似乎是一项艰巨的任务,因为 WPF 与其他语言有很大不同。 例如,在声明依赖属性时,为了方便起见,我们通常会添加一个 CLR 属性包装器。 但是,当属性值发生变化时,WPF 框架不会调用它,因此我们需要等待很长时间才能命中该 setter
中的断点
当我们测试新开发的代码时,我们需要能够检查数据绑定属性的值,并且有多种方法可以做到这一点,尽管有些方法并不明显。 在本章中,我们将调查一些重要的信息来源,以帮助我们找到代码中的错误。
我们将发现各种策略来帮助我们调试数据绑定值,并了解如何在遇到可怕的 XamlParseException
时追踪问题的实际原因。 我们将很快详细介绍所有这些主题,但现在,让我们首先从绝对基础知识开始。
利用输出窗口
当我们对 XAML 进行更改但没有看到我们期望在 UI 中看到的内容时,首先要查找错误的位置是 Visual Studio 的“输出”窗口。 如果此窗口尚不可见,则可以通过从“视图”菜单中选择“输出”选项或按 Ctrl + W 然后按 O 来显示它。
但是,如果您遇到绑定错误,但在“输出”窗口中没有看到任何对其的引用,则可能是因为您的 Visual Studio 当前未设置为向其输出调试信息。 您可以在 Visual Studio 选项对话框窗口中打开此功能。 导航至工具 | 选项| 调试| 输出窗口| 常规输出设置。
常规输出设置部分有多个可以打开和关闭的选项。 最重要的是“所有调试输出”和“异常消息”,但通常最好将它们全部设置为“开”。 设置后,绑定错误将以以下格式显示在输出窗口中:
System.Windows.Data Error: 40 : BindingExpression path error:
‘ViewName’ property not found on ‘object’ ‘‘MainViewModel’
(HashCode=3910657)’. BindingExpression:Path=ViewName;
DataItem=‘MainViewModel’ (HashCode=3910657); target element is ‘TextBox’
(Name=‘NameTextBox’); target property is ‘Text’ (type ‘String’)
MainViewModel
类型的对象中没有名为ViewName
、HashCode
值为3910657
的公共属性。- 该错误是由指定为
ViewName
的Binding.Path
值引发的,该值是在名为NameTextBox
的TextBox
实例的 Text 属性上设置的
可以使用描述性名称而不是具体细节来重写,如下所示:
System.Windows.Data Error: 40 : BindingExpression path error:
‘PropertyOfBindingSource’ property not found on ‘object’
‘‘TypeOfBindingSource’ (HashCode=HashCodeOfBindingSource)’.
BindingExpression:Path=UsedBindingPath; DataItem=‘TypeOfBindingSource’
(HashCode=HashCodeOfBindingSource); target element is ‘TypeOfBindingTarget’
(Name=‘NameOfBindingTarget’); target property is
‘PropertyOfBindingTarget’ (type ‘TypeOfBindingTargetProperty’)
现在我们有了解释这些值代表什么的“关键”,我们可以看到它们确实非常具有描述性。 我们不仅提供了数据绑定 UI 控件的名称(如果已设置)和使用的绑定路径,还提供了数据源的类型,以及正在使用的该类型的实际实例的哈希码。 用过的。
这些错误突出显示了 XAML 文件中所犯的错误。 此窗口中显示的错误类型将包括错误标记的绑定路径,例如使用不存在的属性名称或无效的绑定源路径。 虽然它不能捕获所有问题,但有一种方法可以让它输出额外的信息,帮助我们追踪更难以捉摸的问题。 为此,首先显示“选项”对话框窗口。 导航至工具 | 选项| 调试| 输出窗口| WPF 跟踪设置。
在这里,您可以找到许多选项,每个选项都有不同的输出级别:动画、数据绑定、依赖属性、文档、Freezable、HWND 托管、标记、名称范围、资源字典和路由事件。 各个级别的输出及其含义如下:
- 关键:仅启用关键事件的跟踪
- 错误:启用关键和错误事件的跟踪
- 警告:启用对严重、错误和警告事件的跟踪
- 信息:启用关键、错误、警告和信息事件的跟踪
- 详细:启用关键、错误、警告、信息和详细事件的跟踪
- ActivityTracing:启用对 Stop、Start、Suspend、Transfer 和 Resume 事件的跟踪
将“数据绑定”选项永久设置为“警告”或“错误”,而将其他选项设置为“关闭”是相当常见的。 使用这些选项时的一般经验法则是使用所需的最低级别,除非尝试查找问题,因为它们会减慢应用程序的运行速度。 但应该注意的是,这个额外的调试跟踪输出根本不会影响发布版本。
如果将数据绑定条目设置为“详细”或“全部”输出,并在运行应用程序时查看“输出”窗口,您就会明白为什么它会对性能产生负面影响。 即使不在输出窗口中显示此调试信息,当存在绑定错误时,WPF 框架仍将执行大量检查。 因此,清除显示的所有错误和警告非常重要,以最大限度地减少框架在尝试解决这些错误和警告时所做的工作量。
使演示跟踪源工作
尽管它很有用,但在某些情况下使用输出窗口是不够的。 也许我们现在有太多的输出需要查看,并且希望在下班回家的路上查看它,或者也许我们需要在部署应用程序后查看此类调试跟踪信息。 在这些情况和其他情况下,是时候启用 WPF 演示跟踪源了。
我们可以使用许多不同的跟踪源来为我们输出详细的跟踪数据。 该选项与 WPF 跟踪设置选项中的选项相同,事实上,在设置值后,输出窗口已经向我们显示了调试跟踪输出。
默认情况下,WPF 使用 DefaultTraceListener
对象将信息发送到输出窗口,但我们可以覆盖它和/或将输出配置为发送到文本和/或 XML 文件。
为此,我们需要更改 app.config
文件,该文件位于启动项目的根文件夹中。 我们需要添加一个 system.diagnostics
部分,并在其中添加源、开关和共享侦听器元素。 Switchs
元素保存着决定输出电平的开关,如上一节所述。
SharedListeners
元素指定我们要使用哪种输出。 这三种类型是:
System.Diagnostics.ConsoleTraceListener: 将跟踪发送到输出窗口
System.Diagnostics.TextWriterTraceListener: 输出到文本文件
System.Diagnostics.XmlWriterTraceListener: 输出到 XML 文件
最后,我们需要为每个要监听的跟踪源添加一个 source
元素,并指定要与其一起使用的开关和监听器。 因此,我们能够将不同的跟踪源输出到不同的介质并具有不同的输出级别。 这些跟踪源与 WPF 跟踪设置选项中的跟踪源相同,尽管在配置文件中,我们需要指定它们的全名。
选择如下:
System.Windows.Media.Animation
System.Windows.Data
System.Windows.DependencyProperty
System.Windows.Documents
System.Windows.Freezable
System.Windows.Interop.HwndHost
System.Windows.Markup
System.Windows.NameScope
System.Windows.ResourceDictionary
System.Windows.RoutedEvent
System.Windows.Shell
让我们看一个示例配置文件:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<system.diagnostics>
<sources>
<source name="System.Windows.Data" switchName="Switch">
<listeners>
<add name