2012-03-07 7 views
9

¿Qué método utiliza al depurar eventos o enlaces WPF?Depuración de eventos WPF, enlaces

Traté de usar el punto de interrupción, pero parece que algo está mal con mi XAML o detrás del código que nunca llega al punto de interrupción.

¿Hay alguna manera de ver cuándo hago clic en algo en WPF, qué mensajes de evento están apareciendo o no apareciendo para entender qué salió mal?

Respuesta

23

En los 3 últimos años de la construcción de WPF aplicaciones casi a tiempo completo que he recogido una variedad de reactivos y preventivas soluciones para asegurar que todo lo une correctamente.

Nota: te dará un breve resumen de vez en cuando después de vuelta por la mañana (en 10 horas de tiempo) con ejemplos de código/capturas de pantalla.

Estos son mis herramientas más eficaces:

1) Crear un convertidor que rompe el depurador cuando se ejecuta el Convert y ConvertBack. Una forma rápida y útil de garantizar que tenga los valores que espera. La primera vez que supe de este truco fue 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) Crear unTraceListener que intercepta los errores. Esto es similar a lo que ve en la ventana de salida de Visual Studio cuando tiene un depurador conectado. Usando este método, puedo hacer que el depurador se rompa cuando se produce una excepción durante una operación de enlace. Esto es mejor que configurar PresentationTraceSources.TraceLevel, ya que se aplica a toda la aplicación, no por enlace.

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

} 

Uso

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

En lo anterior, es un escritor ILoggerNLog registro. Tengo una versión más compleja de DefaultTraceListener que puede informar un seguimiento completo de la pila y arrojar excepciones, pero esto será suficiente para comenzar (Jason Bock tiene un article on this extended implementation si desea implementarlo usted mismo, aunque necesitará un código para realmente haz que funcione).

3) Utilice la herramienta de introspección Snoop WPF para profundizar en su vista e inspeccionar sus objetos de datos. Con Snoop puede ver la estructura lógica de su vista y cambiar de forma interactiva los valores para probar diferentes condiciones.

Snoop WPF

Snoop WPF es absolutamente esencial al tiempo de iteración de cualquier aplicación de WPF. Entre sus muchas funciones, el comando Delve le permite profundizar en su modelo de vista/vista y ajustar de forma interactiva los valores. Para profundizar en una propiedad, haga clic derecho para abrir el menú contextual y seleccione el comando Delve; para volver a subir un nivel (¿no profundizar?), hay un pequeño botón ^ en la esquina superior derecha. Por ejemplo, intente profundizar en la propiedad DataContext.

Editar: No puedo creer que acabo de notar esto, sin embargo hay una lengüeta de datos Contexto en la ventana Snoop WPF.

DataContext Tab

4) controles de tiempo de ejecución en INotifyPropertyChanged eventos en #DEBUG. Como el sistema de enlace de datos depende de que se le notifique cuando las propiedades han cambiado, es importante para su cordura que esté notificando que la propiedad correcta ha cambiado. Con un poco de magia de reflexión puedes Debug.Assert cuando algo está mal.

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,

+0

Cualquier pregunta, me dan un grito. – Dennis

1

Tiene la salida de vista activa. Eso mostrará algunos errores vinculantes. PresentationTraceSources.TraceLevel = "High" mostrará más información. Podría estar llegando a un error antes de que llegue a su punto de interrupción. Establezca un punto de interrupción en el constructor solo para ver cómo funciona.

1

Agregar un convertidor "pass-through" en un enlace a veces puede ayudar, ya que le permite poner un punto de interrupción en el convertidor que se extraerá cuando haya una actualización de enlace. También le permite ver que los valores están pasando en ambos sentidos a través del enlace del parámetro de valor Convert y 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. 
    } 
} 

Si se puede acceder a un control por Nombre a continuación, en su Window.xaml.cs se puede comprobar el estado de las consolidaciones en el control usando:

BindingExpression be = comboMyCombo.GetBindingExpression(ComboBox.IsEnabledProperty); 

mirando 'ser' en el depurador puede ayudar (a veces los enlaces se reinician/se rompen en ciertas operaciones).

Cuestiones relacionadas