web-dev-qa-db-fra.com

Comment détecter une liaison de données WPF cassée?

En essayant de répondre à une question dans le voisinage ' nit Testing WPF Bindings ' J'ai eu la question tordante suivante ..
Quelle est la meilleure façon de savoir si la configuration du câblage de liaison de données WPF est incorrecte (ou si vous venez de casser quelque chose qui a été correctement câblé)?

Bien que l'approche des tests unitaires semble être comme Joel "arrachant votre bras pour enlever un éclat".

Tout le monde semble s'être engagé dans la liaison de données de manière importante avec WPF .. et cela a ses mérites.

70
Gishu

Le mieux que j'ai pu trouver ...

Comment puis-je déboguer les liaisons WPF? Par Beatriz Stollnitz

Étant donné que tout le monde ne peut pas toujours garder un œil sur la fenêtre de sortie à la recherche d'erreurs de liaison, j'ai adoré l'option # 2. C'est-à-dire l'ajouter à votre application.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.Windows.Data" switchName="SourceSwitch" >
        <listeners>
          <add name="textListener" />
        </listeners>
      </source>

    </sources>
      <switches>
        <add name="SourceSwitch" value="All" />
      </switches>

      <sharedListeners>
        <add name="textListener"
        type="System.Diagnostics.TextWriterTraceListener"
        initializeData="GraveOfBindErrors.txt" />
      </sharedListeners>

      <trace autoflush="true" indentsize="4"></trace>

  </system.diagnostics>
</configuration>

Associez-le à un bon script d'analyse d'expressions régulières pour extraire les informations pertinentes, que vous pouvez exécuter occasionnellement sur le GraveOfBindErrors.txt dans votre dossier de sortie

System.Windows.Data Error: 35 : BindingExpression path error: 'MyProperty' property not found on 'object' ''MyWindow' (Name='')'. BindingExpression:Path=MyProperty; DataItem='MyWindow' (Name=''); target element is 'TextBox' (Name='txtValue2'); target property is 'Text' (type 'String')
39
Gishu

Dans .NET 3.5, il a été introduit une nouvelle façon de générer spécifiquement des informations de traçage sur des liaisons de données spécifiques.

Cela se fait via la nouvelle propriété jointe System.Diagnostics.PresentationTraceSources.TraceLevel que vous pouvez appliquer à n'importe quelle liaison ou fournisseur de données. Voici un exemple:

<Window x:Class="WpfApplication1.Window1"
    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="Debug Binding Sample"
    Height="300"
    Width="300">
    <StackPanel>
        <TextBox Name="txtInput" />
        <Label>
            <Label.Content>
                <Binding ElementName="txtInput"
                         Path="Text"
                         diag:PresentationTraceSources.TraceLevel="High" />
            </Label.Content>
        </Label>
    </StackPanel>
</Window>

Cela placera des informations de trace pour cette liaison particulière dans la fenêtre de sortie de Visual Studio, sans aucune configuration de traçage requise.

68
Enrico Campidoglio

Vous pouvez utiliser la fonction de débogage de déclenchement de WPF Inspector. Téléchargez simplement l'outil depuis codeplex et attachez-le à votre application en cours d'exécution. Il affiche également des erreurs de liaison en bas de la fenêtre. Outil très utile!

enter image description here

5
Christian Moser

J'utilise la solution présentée ici pour transformer les erreurs de liaison en exceptions natives: http://www.jasonbock.net/jb/Default.aspx?blog=entry.0f221e047de740ee90722b248933a28d

Cependant, un scénario normal dans les liaisons WPF consiste à lever des exceptions au cas où l'entrée utilisateur ne peut pas être convertie en type cible (par exemple, un TextBox lié à un champ entier; l'entrée d'une chaîne non numérique entraîne une FormatException, le l'entrée d'un nombre trop grand entraîne une OverflowException). Un cas similaire se produit lorsque le Setter de la propriété source lève une exception.

La manière WPF de gérer cela est via ValidatesOnExceptions = true et ValidationExceptionRule pour signaler à l'utilisateur que l'entrée fournie n'est pas correcte (en utilisant le message d'exception).

Cependant, ces exceptions sont également envoyées à la fenêtre de sortie et donc "interceptées" par le BindingListener, ce qui entraîne une erreur ... clairement pas le comportement que vous souhaitez.

Par conséquent, j'ai développé la classe BindingListener pour NE PAS lever d'exception dans ces cas:

private static readonly IList<string> m_MessagesToIgnore =
        new List<String>()
        {
            //Windows.Data.Error 7
            //Binding transfer from target to source failed because of an exception
            //Normal WPF Scenario, requires ValidatesOnExceptions / ExceptionValidationRule
            //To cope with these kind of errors
            "ConvertBack cannot convert value",

            //Windows.Data.Error 8
            //Binding transfer from target to source failed because of an exception
            //Normal WPF Scenario, requires ValidatesOnExceptions / ExceptionValidationRule
            //To cope with these kind of errors
            "Cannot save value from target back to source"  
        };

Lignes modifiées dans public override void WriteLine (message de chaîne) :

        ....
        if (this.InformationPropertyCount == 0)
        {
            //Only treat message as an exception if it is not to be ignored
            if (!m_MessagesToIgnore.Any(
                x => this.Message.StartsWith(x, StringComparison.InvariantCultureIgnoreCase)))
            {
                PresentationTraceSources.DataBindingSource.Listeners.Remove(this);

                throw new BindingException(this.Message,
                    new BindingExceptionInformation(this.Callstack,
                        System.DateTime.Parse(this.DateTime),
                        this.LogicalOperationStack, int.Parse(this.ProcessId),
                        int.Parse(this.ThreadId), long.Parse(this.Timestamp)));
            }
            else
            {
                //Ignore message, reset values
                this.IsFirstWrite = true;
                this.DetermineInformationPropertyCount();
            }
        }
    }
4
Jeroen de Bekker

Voici une technique utile pour déboguer/tracer efficacement les déclencheurs. Il vous permet de consigner toutes les actions de déclenchement avec l'élément sur lequel on agit:

http://www.wpfmentor.com/2009/01/how-to-debug-triggers-using-trigger.html

2
Dan Lamping

Cela nous a été très utile mais je voulais ajouter à ceux qui trouvent cela utile qu'il existe un utilitaire que Microsoft fournit avec le sdk pour lire ce fichier.

Trouvé ici: http://msdn.Microsoft.com/en-us/library/ms732023.aspx

Pour ouvrir un fichier de trace

1.Démarrez Service Trace Viewer à l'aide d'une fenêtre de commande pour accéder à votre emplacement d'installation WCF (C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin), puis tapez SvcTraceViewer.exe. (bien que nous ayons trouvé le nôtre dans\v7.0\Bin)

Remarque: L'outil Service Trace Viewer peut s'associer à deux types de fichiers: .svclog et .stvproj. Vous pouvez utiliser deux paramètres en ligne de commande pour enregistrer et désinscrire les extensions de fichier.

/ register: enregistrez l'association des extensions de fichier ".svclog" et ".stvproj" avec SvcTraceViewer.exe

/ unregister: désenregistrez l'association des extensions de fichier ".svclog" et ".stvproj" avec SvcTraceViewer.exe

1.Lorsque Service Trace Viewer démarre, cliquez sur Fichier, puis pointez sur Ouvrir. Accédez à l'emplacement de stockage de vos fichiers de trace.

2. Double-cliquez sur le fichier de trace que vous souhaitez ouvrir.

Remarque: Appuyez sur MAJ tout en cliquant sur plusieurs fichiers de trace pour les sélectionner et les ouvrir simultanément. Service Trace Viewer fusionne le contenu de tous les fichiers et présente une vue. Par exemple, vous pouvez ouvrir des fichiers de trace du client et du service. Ceci est utile lorsque vous avez activé la journalisation des messages et la propagation des activités dans la configuration. De cette façon, vous pouvez examiner l'échange de messages entre le client et le service. Vous pouvez également faire glisser plusieurs fichiers dans la visionneuse ou utiliser l'onglet Projet. Voir la section Gestion du projet pour plus de détails.

3.Pour ajouter des fichiers de trace supplémentaires à la collection ouverte, cliquez sur Fichier, puis pointez sur Ajouter. Dans la fenêtre qui s'ouvre, accédez à l'emplacement des fichiers de trace et double-cliquez sur le fichier que vous souhaitez ajouter.

De plus, en ce qui concerne le filtrage du fichier journal, nous avons trouvé ces liens extrêmement utiles:

http://msdn.Microsoft.com/en-us/library/ms751526.aspx

2
ErocM

Pour ceux comme moi qui recherchent un moyen purement programmatique d'activer tout le traçage WPF à un niveau de trace donné, voici un morceau de code qui le fait. Pour référence, il est basé sur cet article: Tracer les sources dans WPF .

Il ne nécessite pas de modification dans le fichier app.config, et il ne nécessite pas non plus de modifier le registre.

Voici comment je l'utilise, dans un lieu de démarrage (App, etc.):

....
#if DEBUG
    WpfUtilities.SetTracing();
#endif
....

Et voici le code utilitaire (par défaut, il envoie tous les avertissements à l'écouteur de trace par défaut):

public static void SetTracing()
{
    SetTracing(SourceLevels.Warning, null);
}

public static void SetTracing(SourceLevels levels, TraceListener listener)
{
    if (listener == null)
    {
        listener = new DefaultTraceListener();
    }

    // enable WPF tracing
    PresentationTraceSources.Refresh();

    // enable all WPF Trace sources (change this if you only want DataBindingSource)
    foreach (PropertyInfo pi in typeof(PresentationTraceSources).GetProperties(BindingFlags.Static | BindingFlags.Public))
    {
        if (typeof(TraceSource).IsAssignableFrom(pi.PropertyType))
        {
            TraceSource ts = (TraceSource)pi.GetValue(null, null);
            ts.Listeners.Add(listener);
            ts.Switch.Level = levels;
        }
    }
}
1
Simon Mourier