如何检测损坏的 WPF 数据绑定?

Posted

技术标签:

【中文标题】如何检测损坏的 WPF 数据绑定?【英文标题】:How to detect broken WPF Data binding? 【发布时间】:2010-09-25 02:53:45 【问题描述】:

在尝试回答“Unit Testing WPF Bindings”附近的问题时,我遇到了以下琐碎的问题..如果您的 WPF 数据绑定接线设置不正确(或者您只是破坏了正确连接的东西)?

虽然单元测试方法似乎就像 Joel 的“扯掉你的手臂以去除碎片”。我正在四处寻找更简单的开销更少的方法来检测这一点。

似乎每个人都致力于使用 WPF 进行数据绑定。它确实有其优点。

【问题讨论】:

请注意...有时绑定错误不会显示在“输出”窗口中。有时它们会出现在即时窗口中。有时他们根本不出现,这真的很烦人。 【参考方案1】:

在 .NET 3.5 中引入了一种新方法来专门输出有关特定数据绑定的跟踪信息。 这是通过新的 System.Diagnostics.PresentationTraceSources.TraceLevel 附加属性完成的,您可以将其应用于任何绑定或数据提供程序。这是一个例子:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
    Title="Debug Binding Sample"
    Height="300"
    Width="300">
    <StackPanel>
        <TextBox Name="txtInput" />
        <Label>
            <Label.Content>
                <Binding ElementName="txtInput"
                         Path="Text"
                         diag:PresentationTraceSources.TraceLevel="High" />
            </Label.Content>
        </Label>
    </StackPanel>
</Window>

这会将特定绑定的跟踪信息放在 Visual Studio 的输出窗口中,而无需任何跟踪配置。

【讨论】:

优秀!几天的试验和错误通过一个简单的堆栈跟踪解决了——然后我的手拍打着我的前额。这几乎就像我在周五下午渴望的 BOFH 漫画救济。【参考方案2】:

我能找到的最好的......

How can I debug WPF Bindings? by Beatriz Stollnitz

因为每个人都不能总是盯着输出窗口寻找绑定错误,所以我喜欢 Option#2。将其添加到您的 App.Config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.Windows.Data" switchName="SourceSwitch" >
        <listeners>
          <add name="textListener" />
        </listeners>
      </source>

    </sources>
      <switches>
        <add name="SourceSwitch" value="All" />
      </switches>

      <sharedListeners>
        <add name="textListener"
        type="System.Diagnostics.TextWriterTraceListener"
        initializeData="GraveOfBindErrors.txt" />
      </sharedListeners>

      <trace autoflush="true" indentsize="4"></trace>

  </system.diagnostics>
</configuration>

将其与一个好的正则表达式扫描脚本配对以提取相关信息,您可以偶尔在输出文件夹中的 GraveOfBindErrors.txt 上运行这些信息

System.Windows.Data Error: 35 : BindingExpression path error: 'MyProperty' property not found on 'object' ''MyWindow' (Name='')'. BindingExpression:Path=MyProperty; DataItem='MyWindow' (Name=''); target element is 'TextBox' (Name='txtValue2'); target property is 'Text' (type 'String')

【讨论】:

【参考方案3】:

我使用此处介绍的解决方案将绑定错误转化为原生异常:http://www.jasonbock.net/jb/Default.aspx?blog=entry.0f221e047de740ee90722b248933a28d

但是,WPF 绑定中的一个正常情况是在用户输入无法转换为目标类型的情况下引发异常(例如,绑定到整数字段的 TextBox;非数字字符串的输入会导致FormatException,输入的数字太大导致OverflowException)。类似的情况是源属性的 Setter 抛出异常。

处理此问题的 WPF 方法是通过 ValidatesOnExceptions=true 和 ValidationExceptionRule 向用户发出所提供的输入不正确的信号(使用异常消息)。

但是,这些异常也会发送到输出窗口,因此被 BindingListener “捕获”,从而导致错误...显然不是您想要的行为。

因此,我扩展了 BindingListener 类以在这些情况下不抛出异常:

private static readonly IList<string> m_MessagesToIgnore =
        new List<String>()
        
            //Windows.Data.Error 7
            //Binding transfer from target to source failed because of an exception
            //Normal WPF Scenario, requires ValidatesOnExceptions / ExceptionValidationRule
            //To cope with these kind of errors
            "ConvertBack cannot convert value",

            //Windows.Data.Error 8
            //Binding transfer from target to source failed because of an exception
            //Normal WPF Scenario, requires ValidatesOnExceptions / ExceptionValidationRule
            //To cope with these kind of errors
            "Cannot save value from target back to source"  
        ;

public override void WriteLine(string message)中的修改行:

        ....
        if (this.InformationPropertyCount == 0)
        
            //Only treat message as an exception if it is not to be ignored
            if (!m_MessagesToIgnore.Any(
                x => this.Message.StartsWith(x, StringComparison.InvariantCultureIgnoreCase)))
            
                PresentationTraceSources.DataBindingSource.Listeners.Remove(this);

                throw new BindingException(this.Message,
                    new BindingExceptionInformation(this.Callstack,
                        System.DateTime.Parse(this.DateTime),
                        this.LogicalOperationStack, int.Parse(this.ProcessId),
                        int.Parse(this.ThreadId), long.Parse(this.Timestamp)));
            
            else
            
                //Ignore message, reset values
                this.IsFirstWrite = true;
                this.DetermineInformationPropertyCount();
            
        
    

【讨论】:

【参考方案4】:

您可以使用 WPF Inspector 的触发调试功能。只需从 codeplex 下载该工具并将其附加到您正在运行的应用程序中。它还在窗口底部显示绑定错误。 非常有用的工具!

【讨论】:

查看数据上下文和查找绑定路径的最佳工具【参考方案5】:

这是一种有效调试/跟踪触发器的有用技术。它允许您记录所有触发操作以及被操作的元素:

http://www.wpfmentor.com/2009/01/how-to-debug-triggers-using-trigger.html

【讨论】:

【参考方案6】:

这对我们非常有帮助,但我想向那些觉得这很有用的人补充一点,Microsoft 随 sdk 提供了一个实用程序来读取此文件。

在这里找到:http://msdn.microsoft.com/en-us/library/ms732023.aspx

打开跟踪文件

1.启动服务跟踪查看器,使用命令窗口导航到您的 WCF 安装位置(C:\Program 文件\微软 SDKs\Windows\v6.0\Bin),然后键入 SvcTraceViewer.exe。 (虽然我们在 \v7.0\Bin 中找到了我们的)

注意:服务跟踪查看器工具 可以关联两种文件类型: .svclog 和 .stvproj。你可以使用两个 命令行中要注册的参数 并注销文件扩展名。

/register:注册关联的 文件扩展名“.svclog”和 带有 SvcTraceViewer.exe 的“.stvproj”

/unregister: 取消注册 文件扩展名的关联 “.svclog”和“.stvproj” SvcTraceViewer.exe

1.Service Trace Viewer 启动时,单击文件,然后指向打开。 导航到您的位置 跟踪文件被存储。

2.双击要打开的跟踪文件。

注意:单击时按 SHIFT 多个跟踪文件可供选择和 同时打开它们。服务 Trace Viewer 合并所有的内容 文件并呈现一个视图。为了 例如,您可以打开以下跟踪文件 客户端和服务。这是 启用消息时很有用 日志记录和活动传播 配置。这样,您可以 检查之间的消息交换 客户和服务。你也可以拖动 多个文件进入查看器,或使用 项目选项卡。见管理 项目部分了解更多详情。

3.要向打开的集合添加其他跟踪文件,请单击文件 然后指向添加。在窗口 打开,导航到该位置 跟踪文件并双击 您要添加的文件。

另外,对于日志文件的过滤,我们发现这些链接非常有用:

http://msdn.microsoft.com/en-us/library/ms751526.aspx

【讨论】:

【参考方案7】:

对于像我这样正在寻找一种在给定跟踪级别启用所有 WPF 跟踪的纯编程方式的人,这里有一段代码可以做到这一点。供参考,基于这篇文章:Trace sources in WPF。

不需要更改 app.config 文件,也不需要更改注册表。

这就是我使用它的方式,在一些启动的地方(应用程序等):

....
#if DEBUG
    WpfUtilities.SetTracing();
#endif
....

这是实用程序代码(默认情况下,它会将所有警告发送到默认跟踪侦听器):

public static void SetTracing()

    SetTracing(SourceLevels.Warning, null);


public static void SetTracing(SourceLevels levels, TraceListener listener)

    if (listener == null)
    
        listener = new DefaultTraceListener();
    

    // enable WPF tracing
    PresentationTraceSources.Refresh();

    // enable all WPF Trace sources (change this if you only want DataBindingSource)
    foreach (PropertyInfo pi in typeof(PresentationTraceSources).GetProperties(BindingFlags.Static | BindingFlags.Public))
    
        if (typeof(TraceSource).IsAssignableFrom(pi.PropertyType))
        
            TraceSource ts = (TraceSource)pi.GetValue(null, null);
            ts.Listeners.Add(listener);
            ts.Switch.Level = levels;
        
    

【讨论】:

【参考方案8】:

我在 2021 年的建议:

最好的方法是使用 Nuget 的 Benoit Blanchon 小型库

他的原帖在这里:https://***.com/a/19610384/6296708

他的 GitHub 链接以及有关如何使用它的更多信息 + Nuget 命令:https://github.com/bblanchon/WpfBindingErrors

它的特点(直到现在!):

绑定错误时抛出异常(+ 行号) 如果源变量抛出任何异常,该库将捕获并显示它。 单元测试也支持!

编码愉快!

【讨论】:

以上是关于如何检测损坏的 WPF 数据绑定?的主要内容,如果未能解决你的问题,请参考以下文章

WPF 数据绑定:如何使用 XAML 将枚举数据绑定到组合框? [复制]

WPF数据绑定问题:子控件宽度是父容器宽度的1/3,如何做?

wpf数据绑定发生时如何显示加载图形/动画

自定义 WPF 数据绑定:如何添加自定义逻辑?

WPF Image控件如何根据数据状态绑定不同的图片

WPF数据绑定问题(很菜的)