调试WPF事件,绑定

pro*_*eek 11 debugging wpf

调试WPF事件或绑定时使用什么方法?

我试图使用断点,但似乎我的XAML或代码背后的东西是错误的,它从来没有碰到断点.

有没有办法看到我点击WPF中的内容,什么事件消息弹出或没有突然出现,以了解出现了什么问题?

Den*_*nis 28

在过去3年几乎全职构建WPF应用程序的过程中,我收集了各种反应性预防性解决方案,以确保所有内容都能正确绑定.

注意: 我现在会给你一个简短的摘要,然后在早上(10小时内)发回代码样本/截图.

这些是我最有效的工具:

1)创建一个转换器,在执行ConvertConvertBack执行时中断调试器.一种快速有用的方法,可确保您拥有所期望的值.我首先从Bea Stollnitz的博客文章中了解到了这个伎俩.

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

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);
    }

}
Run Code Online (Sandbox Code Playgroud)

用法

DataBindingErrorLogger = new DataBindingErrorLogger(Logger, SourceLevels.Warning);
Run Code Online (Sandbox Code Playgroud)

在上面,ILoggerNLog日志编写器.我有一个更复杂的版本DefaultTraceListener可以报​​告完整的堆栈跟踪并实际抛出异常,但这足以让你开始(Jason Bock有一篇关于这个扩展实现文章,如果你想自己实现它,尽管你需要代码实际上让它工作).

3)使用Snoop WPF内省工具深入研究视图并检查数据对象.使用Snoop,您可以查看视图的逻辑结构,并以交互方式更改值以测试不同的条件.

Snoop WPF

Snoop WPF 对于任何WPF应用程序的迭代时间都是绝对必要的.在其众多功能中,Delve命令允许您深入查看视图/视图模型并以交互方式调整值.要深入研究属性,请右键单击以打开上下文菜单,然后选择Delve命令; 要回到某个级别(un-deve?),^右上角会有一个小按钮.例如,尝试深入研究该DataContext属性.

编辑:我不敢相信我刚注意到这一点,但是Snoop WPF窗口中有一个数据上下文选项卡.

DataContext选项卡

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

}
Run Code Online (Sandbox Code Playgroud)

HTH,