调试WPF事件,绑定 [英] Debugging WPF events, bindings

查看:161
本文介绍了调试WPF事件,绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我试图使用断点,但是似乎我的XAML或代码后面有问题它不会打破断点。



有没有办法看到什么时候点击WPF中的某些东西,什么事件消息弹出或不弹出来了解出现了什么问题

解决方案

在过去3年的WPF应用程序中,几乎全职我收集了各种的反应性预防性解决方案,以确保一切正确绑定在一起。



注意: 我将立即给您一个快速摘要,然后在早上(10小时内)发回代码样本/截图



这些是我最有效的工具:



1) 创建一个转换器,当执行 Convert ConvertBack 时,会中断调试器。一种快速有用的方法来确保您具有期望的值。我第一次从 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)
调试器。打破();

return Binding.DoNothing;
}

}

2) strong>创建一个拦截任何错误的 TraceListener 。这与您在调试器附加时在Visual Studio输出窗口中看到的相似。使用这种方法,当绑定操作中抛出异常时,我可以让调试器中断。这比设置 PresentationTraceSources.TraceLevel 更好,因为它适用于整个应用程序,而不是每个绑定。



DataBindingErrorLogger.cs

  public DataBindingErrorLogger类:DefaultTraceListener,IDisposable 
{
private ILogger Logger;

public DataBindingErrorLogger(ILogger logger,SourceLevels级别)
{
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 disposal)
{
Flush();
关闭();

PresentationTraceSources.DataBindingSource.Listeners.Remove(this);
}

}

用法

  DataBindingErrorLogger =新的DataBindingErrorLogger(Logger,SourceLevels.Warning); 

在上面, ILogger 一个href =http://nlog-project.org/ =noreferrer> NLog 日志记录器。我有一个更复杂的版本的 DefaultTraceListener ,可以报告一个完整的堆栈跟踪,实际上抛出异常,但这将足以让你开始(Jason Bock有一个关于这个扩展实现的文章,如果你想自己实现,虽然你会需要



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





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



编辑:不要相信我只是注意到了,但是Snoop WPF窗口中有一个数据上下文选项卡。





4)运行时检查 INotifyPropertyChanged #DEBUG 中的事件。由于数据绑定系统依赖于属性发生更改时的通知,因此,您通知正确的属性已更改很重要。有一点反思魔法,你可以在某些事情出错时, Debug.Assert



PropertyChangedHelper.cs

  public静态类PropertyChangedHelper 
{
#if DEBUG
public static Dictionary< Type,Dictionary< string,bool>> PropertyCache = new Dictionary< Type,Dictionary< string,bool>>();
#endif

[DebuggerStepThrough]
public static void Notify(此INotifyPropertyChanged发件人,PropertyChangedEventHandler eventHandler,string propertyName)
{
sender.Notify(eventHandler ,new PropertyChangedEventArgs(propertyName),true);
}

[DebuggerStepThrough]
public static void Notify(此INotifyPropertyChanged发件人,PropertyChangedEventHandler eventHandler,string propertyName,bool validatePropertyName)
{
sender.Notify (eventHandler,new PropertyChangedEventArgs(propertyName),validatePropertyName);


[DebuggerStepThrough]
public static void Notify(此INotifyPropertyChanged发件人,PropertyChangedEventHandler eventHandler,PropertyChangedEventArgs eventArgs)
{
sender.Notify(eventHandler, eventArgs,true);
}

[DebuggerStepThrough]
public static void Notify(此INotifyPropertyChanged发件人,PropertyChangedEventHandler eventHandler,PropertyChangedEventArgs eventArgs,bool validatePropertyName)
{
#if DEBUG
if(validatePropertyName)
Debug.Assert(PropertyExists(sender as object,eventArgs.PropertyName),String.Format(Property:{0}不存在于类型:{1},eventArgs。 PropertyName,sender.GetType()。ToString()));
#endif

//作为事件处理程序是一个参数实际上有些线程安全
// http://blogs.msdn.com/b/ericlippert/存档/ 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不要检查动态类的有效性。这是可能的,但由于它们是动态的,我们无法缓存它们。
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);
}
}

返回PropertyCache [senderType] [propertyName];
}
#endif

}

HTH ,


What method do you use when debugging WPF events or bindings?

I tried to use breakpoint, but it seems like that something's wrong with my XAML or behind the code that it never hits the breakpoint.

Is there a way to see when I click something in WPF, what event messages are popping up or not popping up to understand what went wrong?

解决方案

In last 3 years of building WPF applications almost full-time I have collected a variety of reactive and preventative solutions to ensure that everything binds together properly.

Note: I will give you a quick summary now and then post back in the morning (in 10 hours time) with code samples / screenshots.

These are my most effective tools:

1) Create a converter that breaks the debugger when the Convert and ConvertBack is executed. A quick and useful way to ensure that you have the values that you expect. I first learnt of this trick from 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) Create a TraceListener that intercepts any errors. This is similar to what you see in the Visual Studio Output Window when you have a debugger attached. Using this method I can get the debugger to break when there is a exception thrown during a binding operation. This is better than setting PresentationTraceSources.TraceLevel as it applies to the entire application, not per-binding.

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

}

Usage

DataBindingErrorLogger = new DataBindingErrorLogger(Logger, SourceLevels.Warning);

In the above, ILogger is an NLog log writer. I have a more complex version of DefaultTraceListener that can report a full stack trace and actually throw exceptions however this will be enough to get you started (Jason Bock has an article on this extended implementation if you want to implement it yourself, although you will need code to actually to get it work).

3) Use the Snoop WPF introspection tool to delve into your view and inspect your data objects. Using Snoop you can view the logical structure of your view and interactively change values to test different conditions.

Snoop WPF is absolutely essential to the iteration time of any WPF application. Among its many features, the Delve command allows you to drill down to your view/view model and interactively tweak values. To delve into a property, right-click to open the context menu and select Delve command; to go back up a level (un-delve?) there is a small button ^ in the top-right corner. For example, try delving into the DataContext property.

Edit: I can't believe I just noticed this, however there is a Data Context tab in the Snoop WPF window.

4) Runtime checks on INotifyPropertyChanged events in #DEBUG. As the Data Binding system relies on being notified when properties have changed, it is important for your sanity that you are notifying that the correct property has changed. With a bit of reflection magic you can Debug.Assert when something is wrong.

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,

这篇关于调试WPF事件,绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆