TOC

This article is currently in the process of being translated into Korean (~45% done).

데이터 바인딩:

Debugging data bindings

data binding은 프로그램 실행 시간에 평가되고, 실패해도 예외가 던져지지 않는 이유로, 잘못된 바인딩을 추적하기 상당히 어려울 수 있다. 이러한 문제들은 몇몇 다른 상황들에서도 일어날 수 있지만, 일상적으로 없는 속성에 바인딩을 시도하거나, 속성의 이름을 잘 못 기억했거나,철자법이 틀릴 때에 발생한다. 아래에 예가 있다.

<Window x:Class="WpfTutorialSamples.DataBinding.DataBindingDebuggingSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DataBindingDebuggingSample" Height="100" Width="200">
    <Grid Margin="10" Name="pnlMain">
		<TextBlock Text="{Binding NonExistingProperty, ElementName=pnlMain}" />
	</Grid>
</Window>

출력창

확인할 첫번째 위치는 Visual Studio Output 창이다. Visual Studio 창의 아랫 부분에 위치해 있는데 없으면 [Ctrl+Alt+O] 단축키로 활성화 할 수 있는 창이다. Debugger로 인해 출력 결과가 쏟아 지지만, 위와 같은 잘 못된 예시 프로그램이 실행될때엔 다음과 같은 줄을 찾을려고 노력해야 한다.

System.Windows.Data Error: 40 : BindingExpression path error: 'NonExistingProperty' property not found on 'object' ''Grid' (Name='pnlMain')'. BindingExpression:Path=NonExistingProperty; DataItem='Grid' (Name='pnlMain'); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

이 긴 줄에 줄바꿈도 없으니 약간 질리게 보일지도 모르겠지만 중요한 부분은 이 부분이다:

'NonExistingProperty' property not found on 'object' ''Grid' (Name='pnlMain')'.

이것은 당신이 pnlMain이라는 이름을 가진, Grid 타입의 객체에서 "NonExistingProperty"로 불리는 속성을 사용할려고 시도했음을 말해줍니다. 이것은 실재로 상당히 간결하고 당신이 속성명을 수정하거나 문제되는 실제 객체와 바인드하는 것을 도와주게 됩니다.

Adjusting the trace level

위의 예는 WPF에게 프로그래머가 뭘 할려고 하고 왜 그것이 동작하지 않는지가 명확하기 때문에 수정하기 쉬운 편입니다. 그렇지만 다음 예를 한번 생각해보세요.

<Window x:Class="WpfTutorialSamples.DataBinding.DataBindingDebuggingSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DataBindingDebuggingSample" Height="100" Width="200">
    <Grid Margin="10">
		<TextBlock Text="{Binding Title}" />
	</Grid>
</Window>

"Title" 속성을 바인드 할려고 시도했지만 객체가 어디에 있나요? data context에 대한 글에서 말했듯이,WPF는 여기 TextBlock에는 DataContext 속성을 사용하는데,그것은 control 계층에서 상속받을 것입니다. 그러나 이 예제에서 data context를 할당하는 걸 잊어버렸습니다. 이것은 기본적으로 프로그래머가 NULL 객체의 속성을 얻을려고 시도하고 있다는 걸 의미합니다. WPF는 이 프로그램이 완벽히 유효한 바인딩이라고 이해할 거지만, 객체가 아직 초기화되지 않았고, 그래서 WPF는 문제가 있다고 말하지 않을 것입니다. 당신이 이 예제를 실행하고 Output 창을 들여다 봐도 어떤 binding 오류는 보지 못할 것입니다.<

그렇지만,당신이 기대한 동작이 아닌 경우에 WPF로 하여금 그것이 부딪치는 binding 문제를 강제로 보여주게 하는 방법이 있습니다. 그건 System.Diagnostics namespace에 있는 PresentationTraceSources 객체에 TraceLevel을 설정하는걸로 할 수 있습니다.

<Window x:Class="WpfTutorialSamples.DataBinding.DataBindingDebuggingSample"
        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="DataBindingDebuggingSample" Height="100" Width="200">
    <Grid Margin="10">
		<TextBlock Text="{Binding Title, diag:PresentationTraceSources.TraceLevel=High}" />
	</Grid>
</Window>

제일 상단에 System.Diagnostic namespace에 대한 참조를 추가하고 binding에 그 속성을 사용한 걸 눈여겨 보세요. WPF는 Output 창에 이 특별한 바인딩에 대해 많은 정보를 보여 줄 겁니다.

System.Windows.Data Warning: 55 : Created BindingExpression (hash=2902278) for Binding (hash=52760599)
System.Windows.Data Warning: 57 :   Path: 'Title'
System.Windows.Data Warning: 59 : BindingExpression (hash=2902278): Default mode resolved to OneWay
System.Windows.Data Warning: 60 : BindingExpression (hash=2902278): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 61 : BindingExpression (hash=2902278): Attach to System.Windows.Controls.TextBlock.Text (hash=18876224)
System.Windows.Data Warning: 66 : BindingExpression (hash=2902278): Resolving source
System.Windows.Data Warning: 69 : BindingExpression (hash=2902278): Found data context element: TextBlock (hash=18876224) (OK)
System.Windows.Data Warning: 70 : BindingExpression (hash=2902278): DataContext is null
System.Windows.Data Warning: 64 : BindingExpression (hash=2902278): Resolve source deferred
System.Windows.Data Warning: 66 : BindingExpression (hash=2902278): Resolving source
System.Windows.Data Warning: 69 : BindingExpression (hash=2902278): Found data context element: TextBlock (hash=18876224) (OK)
System.Windows.Data Warning: 70 : BindingExpression (hash=2902278): DataContext is null
System.Windows.Data Warning: 66 : BindingExpression (hash=2902278): Resolving source
System.Windows.Data Warning: 69 : BindingExpression (hash=2902278): Found data context element: TextBlock (hash=18876224) (OK)
System.Windows.Data Warning: 70 : BindingExpression (hash=2902278): DataContext is null
System.Windows.Data Warning: 66 : BindingExpression (hash=2902278): Resolving source
System.Windows.Data Warning: 69 : BindingExpression (hash=2902278): Found data context element: TextBlock (hash=18876224) (OK)
System.Windows.Data Warning: 70 : BindingExpression (hash=2902278): DataContext is null
System.Windows.Data Warning: 66 : BindingExpression (hash=2902278): Resolving source  (last chance)
System.Windows.Data Warning: 69 : BindingExpression (hash=2902278): Found data context element: TextBlock (hash=18876224) (OK)
System.Windows.Data Warning: 77 : BindingExpression (hash=2902278): Activate with root item <null>
System.Windows.Data Warning: 105 : BindingExpression (hash=2902278):   Item at level 0 is null - no accessor
System.Windows.Data Warning: 79 : BindingExpression (hash=2902278): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 87 : BindingExpression (hash=2902278): TransferValue - using fallback/default value ''
System.Windows.Data Warning: 88 : BindingExpression (hash=2902278): TransferValue - using final value ''

By reading through the list, you can actually see the entire process that WPF goes through to try to find a proper value for your TextBlock control. Several times you will see it being unable to find a proper DataContext, and in the end, it uses the default {DependencyProperty.UnsetValue} which translates into an empty string.

Using the real debugger

The above trick can be great for diagnosing a bad binding, but for some cases, it's easier and more pleasant to work with the real debugger. Bindings doesn't natively support this, since they are being handled deep inside of WPF, but using a Converter, like shown in a previous article, you can actually jump into this process and step through it. You don't really need a Converter that does anything useful, you just need a way into the binding process, and a dummy converter will get you there:

<Window x:Class="WpfTutorialSamples.DataBinding.DataBindingDebuggingSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:self="clr-namespace:WpfTutorialSamples.DataBinding"
        Title="DataBindingDebuggingSample" Name="wnd" Height="100" Width="200">
	<Window.Resources>
		<self:DebugDummyConverter x:Key="DebugDummyConverter" />
	</Window.Resources>
    <Grid Margin="10">
		<TextBlock Text="{Binding Title, ElementName=wnd, Converter={StaticResource DebugDummyConverter}}" />
	</Grid>
</Window>
using System;
using System.Windows;
using System.Windows.Data;
using System.Diagnostics;

namespace WpfTutorialSamples.DataBinding
{
	public partial class DataBindingDebuggingSample : Window
	{
		public DataBindingDebuggingSample()
		{
			InitializeComponent();
		}
	}

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

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

In the Code-behind file, we define a DebugDummyConverter. In the Convert() and ConvertBack() methods, we call Debugger.Break(), which has the same effect as setting a breakpoint in Visual Studio, and then return the value that was given to us untouched.

In the markup, we add a reference to our converter in the window resources and then we use it in our binding. In a real world application, you should define the converter in a file of its own and then add the reference to it in App.xaml, so that you may use it all over the application without having to create a new reference to it in each window, but for this example, the above should do just fine.

If you run the example, you will see that the debugger breaks as soon as WPF tries to fetch the value for the title of the window. You can now inspect the values given to the Convert() method, or even change them before proceeding, using the standard debugging capabilities of Visual Studio.

If the debugger never breaks, it means that the converter is not used. This usually indicates that you have an invalid binding expression, which can be diagnosed and fixed using the methods described in the start of this article. The dummy-converter trick is only for testing valid binding expressions.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!