[WPF] 如何調試Data Binding

前言

在WPF開發中,將ViewModel中對象綁定到UI上時,會出現明明已經將數據對象Binding到UI,可是UI上就是不顯示等等的問題。這篇博客將介紹WPF Data Binding調試相關的內容。express

場景一(Binding的屬性不存在)

ViewModel:ide

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new ViewModel() { Id = 100, Name = "Tom", Age = 24};
    }
}

public class ViewModel
{
    public int Id { get; set; } public string Name { get; set; }
    public int Age { get; set; }
}

XAML:this

<StackPanel Margin="10">
    <TextBlock Text="{Binding ID}" />
    <TextBlock Text="{Binding Name}" Margin="0,10" />
    <TextBlock Text="{Binding Age}" />
</StackPanel>

運行結果:spa

UI中Binding的ID值沒有顯示出來。請注意加粗的代碼,在UI代碼中,因爲拼寫錯誤,將Id寫成了ID。可是這段代碼在編譯時不會報錯,在VS Output窗口中也不會有提示/警告信息。在程序運行時,仔細查看VS Output窗口,此時會有以下信息 (對信息進行了精簡)
System.Windows.Data Error: 40 : BindingExpression path error: 'ID' property not found on 'object' ''ViewModel' (HashCode=20915929)'. BindingExpression:Path=ID; DataItem='ViewModel' (HashCode=20915929); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')調試

這段信息告訴提示說在ViewModel對象上沒有找到ID屬性,此時咱們再去檢查一下ViewModel發現,原來是將Id錯誤的寫成了ID。通常這種錯誤的提示開頭爲:System.Windows.Data Error: code

場景二(使用System.Diagnostics來追蹤)

XAML:orm

<Window x:Class="WpfBindingDebug.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <TextBlock Text="{Binding Title}" />
    </StackPanel>
</Window>

將Title屬性Binding到TextBlock的Text屬性上面,XAML和C#代碼中均未指定DataContext屬性。編譯項目並運行程序,在VS Output中沒有任何提示/警告信息。此時應該如何調試呢?能夠經過設置PresentationTraceSources對象的TraceLevel來強制WPF輸出全部的Binding方面的信息。xml

更多PresentationTraceSources信息能夠參考:對象

https://msdn.microsoft.com/en-us/library/system.diagnostics.presentationtracesources.tracelevel(v=vs.110).aspxblog

對XAML代碼進行以下修改(注意加粗的代碼行):

<Window x:Class="WpfBindingDebug.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <TextBlock Text="{Binding Title, diag:PresentationTraceSources.TraceLevel=High}" />
    </StackPanel>
</Window>

編譯並運行程序,在VS Output窗口中能夠找到關於Binding的信息(對信息進行了精簡):

System.Windows.Data Warning: 56 : Created BindingExpression (hash=39201736) for Binding (hash=44325851)
System.Windows.Data Warning: 58 :   Path: 'Title'
System.Windows.Data Warning: 60 : BindingExpression (hash=39201736): Default mode resolved to OneWay
System.Windows.Data Warning: 61 : BindingExpression (hash=39201736): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 62 : BindingExpression (hash=39201736): Attach to System.Windows.Controls.TextBlock.Text (hash=17911681)
System.Windows.Data Warning: 67 : BindingExpression (hash=39201736): Resolving source
System.Windows.Data Warning: 70 : BindingExpression (hash=39201736): Found data context element: TextBlock (hash=17911681) (OK)
....
System.Windows.Data Warning: 67 : BindingExpression (hash=39201736): Resolving source
System.Windows.Data Warning: 70 : BindingExpression (hash=39201736): Found data context element: TextBlock (hash=17911681) (OK)
System.Windows.Data Warning: 71 : BindingExpression (hash=39201736): DataContext is null
System.Windows.Data Warning: 67 : BindingExpression (hash=39201736): Resolving source  (last chance)
System.Windows.Data Warning: 70 : BindingExpression (hash=39201736): Found data context element: TextBlock (hash=17911681) (OK)
System.Windows.Data Warning: 78 : BindingExpression (hash=39201736): Activate with root item <null>
System.Windows.Data Warning: 106 : BindingExpression (hash=39201736):   Item at level 0 is null - no accessor
System.Windows.Data Warning: 80 : BindingExpression (hash=39201736): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 88 : BindingExpression (hash=39201736): TransferValue - using fallback/default value ''
System.Windows.Data Warning: 89 : BindingExpression (hash=39201736): TransferValue - using final value ''

注意:在Visual Studio 2010中須要進行以下設置才能看到上面的提示信息:由於VS 2010默認將下面的設爲Off。

Tools -> Options -> Debugging -> Output Window -> WPF Trace Settings -> Data Binding -> set to Warning

程序一直在嘗試尋找Visual Tree上的能夠Binding的Title值,最終找到一個合適的,DependencyProperty.UnsetValue。

上述方法對查找單個頁面Binding頗有用,固然咱們也能夠全局的來收集這些Binding信息。在App.xaml.cs中添加:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        PresentationTraceSources.Refresh();
        PresentationTraceSources.DataBindingSource.Listeners.Add(new ConsoleTraceListener());
        PresentationTraceSources.DataBindingSource.Listeners.Add(new DebugTraceListener());
        PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Warning | SourceLevels.Error;
        base.OnStartup(e);
    }
}

public class DebugTraceListener : TraceListener
{
    public override void Write(string message)
    {
        // Write your log here.
    }

    public override void WriteLine(string message)
    {
        // Write your log here.
    }
}

場景三(使用ValueConverter來調試)

場景一和場景二中的方法解決因拼寫錯誤或者無明確DataContext時很是有效。不過有時候真正的經過VS進行調試一下,更加直觀,便捷。可使用實現一個簡單的調試使用的Converter,而後將其Binding到目標上,

public class DebugConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Debugger.Break(); return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Debugger.Break();
        return value;
    }
}

Debugger.Break()的效果和VS中F9設置斷點是同樣的。
XAML

<Window x:Class="WpfBindingDebug.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfBindingDebug"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:DebugConverter x:Key="DebugConverter" />
    </Window.Resources>
    <StackPanel>
        <TextBlock Text="{Binding Id, Converter={StaticResource DebugConverter}}" />
    </StackPanel>
</Window>

感謝您的閱讀~若是您有其餘關於Data Binding的調試方式,歡迎在評論區指出~

相關文章
相關標籤/搜索