调试WPF事件或绑定时使用什么方法?
我试图使用断点,但似乎我的XAML或代码背后的某些内容从来没有碰到断点。
有没有办法看到我在WPF中点击某些内容时,会弹出哪些事件消息或者没有弹出来了解出现了什么问题?
答案 0 :(得分:28)
在构建 WPF 应用程序的最近3年里,我几乎全职收集了各种被动和预防性解决方案,以确保< em>所有正确绑定在一起。
注意: 我现在会给你一个快速摘要,然后在早上(10小时内)发回代码示例/截图。
这些是我最有效的工具:
1)创建一个转换器,在执行Convert
和ConvertBack
时中断调试器。一种快速有用的方法,可确保您拥有所期望的值。我第一次从Bea Stollnitz's blog post学到了这个技巧。
<强> DebugConverter.cs 强>
public class DebugConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (Debugger.IsAttached)
Debugger.Break();
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (Debugger.IsAttached)
Debugger.Break();
return Binding.DoNothing;
}
}
2)创建一个拦截任何错误的TraceListener
。这与您在附加调试器时在Visual Studio输出窗口中看到的内容类似。使用此方法,我可以在绑定操作期间抛出异常时使调试器中断。这比设置PresentationTraceSources.TraceLevel
更好,因为它适用于整个应用程序,而不是每个绑定。
<强> DataBindingErrorLogger.cs 强>
public class DataBindingErrorLogger : DefaultTraceListener, IDisposable
{
private ILogger Logger;
public DataBindingErrorLogger(ILogger logger, SourceLevels level)
{
Logger = logger;
PresentationTraceSources.Refresh();
PresentationTraceSources.DataBindingSource.Listeners.Add(this);
PresentationTraceSources.DataBindingSource.Switch.Level = level;
}
public override void Write(string message)
{
}
public override void WriteLine(string message)
{
Logger.BindingError(message);
if (Debugger.IsAttached && message.Contains("Exception"))
Debugger.Break();
}
protected override void Dispose(bool disposing)
{
Flush();
Close();
PresentationTraceSources.DataBindingSource.Listeners.Remove(this);
}
}
<强>用法强>
DataBindingErrorLogger = new DataBindingErrorLogger(Logger, SourceLevels.Warning);
在上文中,ILogger
是NLog日志编写者。我有一个更复杂的DefaultTraceListener
版本可以报告一个完整的堆栈跟踪并实际抛出异常,但这足以让你开始(如果你想自己实现它,Jason Bock有一个article on this extended implementation,虽然你需要代码才能真正起作用。)
3)使用Snoop WPF内省工具深入研究视图并检查数据对象。使用Snoop,您可以查看视图的逻辑结构,并以交互方式更改值以测试不同的条件。
Snoop WPF对于任何WPF应用程序的迭代时间都是绝对必要。在众多功能中, Delve 命令允许您深入查看视图/视图模型并以交互方式调整值。要深入研究属性,请右键单击以打开上下文菜单,然后选择Delve命令;要回到某个级别(un-deve?),右上角有一个小按钮 ^ 。例如,尝试深入研究DataContext
属性。
编辑:我无法相信我只是注意到了这一点,但是在Snoop WPF窗口中有一个数据上下文标签。
4)运行时检查INotifyPropertyChanged
中的#DEBUG
个事件。由于数据绑定系统依赖于在属性发生更改时收到通知,因此您通知正确的属性已更改是非常重要的。有了一点反射魔法,当出现问题时你可以Debug.Assert
。
<强> PropertyChangedHelper.cs 强>
public static class PropertyChangedHelper
{
#if DEBUG
public static Dictionary<Type, Dictionary<string, bool>> PropertyCache = new Dictionary<Type, Dictionary<string, bool>>();
#endif
[DebuggerStepThrough]
public static void Notify(this INotifyPropertyChanged sender, PropertyChangedEventHandler eventHandler, string propertyName)
{
sender.Notify(eventHandler, new PropertyChangedEventArgs(propertyName), true);
}
[DebuggerStepThrough]
public static void Notify(this INotifyPropertyChanged sender, PropertyChangedEventHandler eventHandler, string propertyName, bool validatePropertyName)
{
sender.Notify(eventHandler, new PropertyChangedEventArgs(propertyName), validatePropertyName);
}
[DebuggerStepThrough]
public static void Notify(this INotifyPropertyChanged sender, PropertyChangedEventHandler eventHandler, PropertyChangedEventArgs eventArgs)
{
sender.Notify(eventHandler, eventArgs, true);
}
[DebuggerStepThrough]
public static void Notify(this INotifyPropertyChanged sender, PropertyChangedEventHandler eventHandler, PropertyChangedEventArgs eventArgs, bool validatePropertyName)
{
#if DEBUG
if (validatePropertyName)
Debug.Assert(PropertyExists(sender as object, eventArgs.PropertyName), String.Format("Property: {0} does not exist on type: {1}", eventArgs.PropertyName, sender.GetType().ToString()));
#endif
// as the event handlers is a parameter is actually somewhat "thread safe"
// http://blogs.msdn.com/b/ericlippert/archive/2009/04/29/events-and-races.aspx
if (eventHandler != null)
eventHandler(sender, eventArgs);
}
#if DEBUG
[DebuggerStepThrough]
public static bool PropertyExists(object sender, string propertyName)
{
// we do not check validity of dynamic classes. it is possible, however since they're dynamic we couldn't cache them anyway.
if (sender is ICustomTypeDescriptor)
return true;
var senderType = sender.GetType();
if (!PropertyCache.ContainsKey(senderType))
PropertyCache.Add(senderType, new Dictionary<string,bool>());
lock (PropertyCache)
{
if (!(PropertyCache[senderType].ContainsKey(propertyName)))
{
var hasPropertyByName = (senderType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static) != null);
PropertyCache[senderType].Add(propertyName, hasPropertyByName);
}
}
return PropertyCache[senderType][propertyName];
}
#endif
}
HTH,
答案 1 :(得分:1)
您是否激活了视图输出。这将显示一些绑定错误。 PresentationTraceSources.TraceLevel="High"
会显示更多信息。它可能会在它到达断点之前遇到错误。在构造函数中设置一个断点,只是为了看它是否有效。
答案 2 :(得分:1)
在绑定上添加“直通”转换器有时可以帮助你在转换器中设置一个断点,当有绑定更新时它将被拉出。它还允许您查看通过Convert和ConvertBack值参数的绑定双向传递的值。
public class PassthroughConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return value; // Breakpoint here.
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
return value; // Breakpoint here.
}
}
如果您可以通过Name访问控件,那么在Window.xaml.cs中,您可以使用以下命令检查控件上的绑定状态:
BindingExpression be = comboMyCombo.GetBindingExpression(ComboBox.IsEnabledProperty);
在调试器中查看“be”可能会有所帮助(有时绑定会在某些操作中被重置/中断)。