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

如何检测损坏的WPF数据绑定?,wpf,data-binding,Wpf,Data Binding,在试图回答附近的一个问题时,我问了以下一个琐碎的问题。 如果WPF数据绑定布线设置不正确(或者您刚刚损坏了正确连接的东西),最好的方法是什么? 尽管单元测试方法看起来像Joel的“撕下你的手臂来移除碎片”。。我正在四处寻找更简单、开销更小的方法来检测这一点 每个人似乎都致力于使用WPF进行数据绑定。。它确实有它的优点。我能找到的最好的 因为每个人都不能总是盯着输出窗口寻找绑定错误,所以我喜欢选项2。也就是说,将此添加到App.Config <?xml version="1.0" encod

在试图回答附近的一个问题时,我问了以下一个琐碎的问题。
如果WPF数据绑定布线设置不正确(或者您刚刚损坏了正确连接的东西),最好的方法是什么?

尽管单元测试方法看起来像Joel的“撕下你的手臂来移除碎片”。。我正在四处寻找更简单、开销更小的方法来检测这一点

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

我能找到的最好的

因为每个人都不能总是盯着输出窗口寻找绑定错误,所以我喜欢选项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>

在.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的输出窗口中仅放置该特定绑定的跟踪信息,而不需要任何跟踪配置。

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


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

可在此处找到:

打开跟踪文件的步骤

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

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

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

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

1.服务跟踪查看器启动时,单击“文件”,然后指向“打开”。 导航到您的 跟踪文件被存储

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

注意:单击时按SHIFT键 要选择和删除的多个跟踪文件 同时打开它们。服务 跟踪查看器合并所有文件的内容 文件并显示一个视图。对于 例如,您可以打开的跟踪文件 客户端和服务。这是 启用消息时非常有用 中的日志记录和活动传播 配置这样,你就可以 检查之间的消息交换 客户和服务。也可以拖动 将多个文件放入查看器,或使用 “项目”选项卡。见管理层 项目部分了解更多详细信息

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

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


我使用此处提供的解决方案将绑定错误转换为本机异常:

但是,WPF绑定中的一个正常场景是,如果用户输入无法转换为目标类型,则抛出异常(例如,绑定到整数字段的文本框;输入非数字字符串会导致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"  
        };

您可以使用WPF检查器的触发器调试功能。只需从codeplex下载该工具并将其连接到您的跑步应用程序。它还在窗口底部显示绑定错误。 非常有用的工具


对于像我这样寻找在给定跟踪级别启用所有WPF跟踪的纯编程方式的人,下面是一段代码。作为参考,它基于以下文章:

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

这就是我在一些创业场所(应用程序等)使用它的方式:

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


我在2021年的建议:

最好的方法是使用Nuget的
Benoit Blanchon
small library 他原来在这里的职位:

他的GitHub链接以及有关如何使用it+Nuget命令的更多信息:

其特点(到目前为止!):

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

快乐编码

只是一张纸条。。。有时绑定错误不会显示在输出窗口中。有时它们会出现在即时窗口中。有时它们根本不显示,这真的很烦人。查看datacontext和查找绑定的最佳工具!几天的尝试和错误解决了一个简单的堆栈跟踪,然后我的手拍在我的额头上。这几乎就像我在周五渴望的BOFH喜剧解脱
        ....
        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();
            }
        }
    }
....
#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;
        }
    }
}