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


72

在尝试回答附近的“ Unit Testing WPF Bindings ” (单元测试WPF绑定)附近的问题时,我遇到了以下问题。.
找出WPF Data Binding接线设置不正确(或者断开了正确接线的东西)的最佳方法是什么? ?

尽管单元测试的方法似乎就像乔尔(Joel)的“撕下手臂以去除碎片”。

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


3
请注意...有时绑定错误不会显示在“输出”窗口中。有时它们会显示在即时窗口中。有时它们根本不显示,这很烦人。

Answers:


71

在.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的“输出窗口”中,而无需任何跟踪配置。


优秀!通过简单的堆栈跟踪即可解决数天的反复试验,然后将我的手sm到额头上。这几乎就像我在星期五下午渴望的BOFH漫画浮雕一样。
非正式的

40

最好的我能找到...

如何调试WPF绑定?由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')

5

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

但是,WPF绑定中的正常情况是在用户输入无法转换为目标类型的情况下引发异常(例如,绑定到整数字段的TextBox;非数字字符串的输入会导致FormatException,输入的数字太大会导致OverflowException。类似的情况是当source属性的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"  
        };

公共中的修改行覆盖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();
            }
        }
    }

5

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

在此处输入图片说明


查看数据上下文和查找绑定路径的最佳工具
Abdul Rauf


2

这对我们很有帮助,但我想补充一下那些觉得有用的人,Microsoft提供了一个与sdk一起读取该文件的实用程序。

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

打开跟踪文件

1.通过使用命令窗口导航到WCF安装位置(C:\ Program Files \ Microsoft SDKs \ Windows \ v6.0 \ Bin),启动Service Trace Viewer,然后键入SvcTraceViewer.exe。(尽管我们在\ v7.0 \ Bin中找到了我们的)

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

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

/ unregister:使用SvcTraceViewer.exe注销文件扩展名“ .svclog”和“ .stvproj”的关联

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

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

注意:在单击多个跟踪文件的同时按SHIFT键可以同时选择并打开它们。Service Trace Viewer合并所有文件的内容并显示一个视图。例如,您可以打开客户端和服务的跟踪文件。在配置中启用消息日志记录和活动传播时,此功能很有用。这样,您可以检查客户端和服务之间的消息交换。您也可以将多个文件拖到查看器中,或使用“项目”选项卡。有关更多详细信息,请参见“管理项目”部分。

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

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

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


1

对于像我这样寻求以纯编程方式在给定跟踪级别启用所有WPF跟踪的人来说,这里有一段代码可以做到这一点。作为参考,它基于本文: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;
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.