Julekalender 2004 om Whidbey


21. dec 2004 16:42

En af de rigtig smarte features i Whidbey er muligheden for at skrive customizerede views på data-typer til brug under debugging. Indbygget i Whidbey findes der allerede en Visualizer til DataSets og 3 forskellige til strings - man kan se en string som Xml, som Html eller som almindelig tekst. Disse Visualizers er alle statiske i betydningen de foretager en formatering af data, så det præsenteres på en bestemt måde. Man kan imidlertid tage endnu et skridt og foretage sig lidt mere under visualiseringen. Dagens eksempel er en lille Visualizer der indeholder præ-definerede Regular Expressions, som man kan teste strings med.

Projektet

Dette projekt blev lavet i Whidbey Beta1 Version 8.0.40607.85 og grunden til at nævne det, er, at der har været forskellige måder at få en fat i den dll man skal hente sit System.Diagnostics.IDebugVisualizer-interface på. I denne version gøres det ved at tilføje en reference til Microsoft.VisualStudio.DebuggerVisualizers.dll.

Allerførst skal man have erklæret hvilken type denne Visualizer er beregnet til. Dette gøres ved at tilføje en global attribute før namespace-erklæringen:

[assembly: System.Diagnostics.DebuggerVisualizer(
  typeof(System.Diagnostics.VisualizerObjectSource),
  typeof(Captator.Diagnostics.RegexVisualizer),
  System.Diagnostics.VisualizerUIType.Modal,
  Target = typeof(System.String),
  Description = "Test mod regex")]

Dernæst erklærer vi den klasse som implementerer System.Diagnostics.IDebugVisualizer-interfacet. Dette interface har kun en enkelt metode (Show) som bliver kaldt, når udvikleren vælger at undersøge en string med vores visualizer.

namespace Captator.Diagnostics
{
  public class RegexVisualizer : System.Diagnostics.IDebugVisualizer
  {
    // statisk dictionary til regular expressions - denne dictionary er generisk...
    private static System.Collections.Generic.Dictionary<string, System.Text.RegularExpressions.Regex> _regexs;

    static RegexVisualizer()
    {
      // instantiering af et rigt udvalg af regexs
      _regexs = new System.Collections.Generic.Dictionary<string, System.Text.RegularExpressions.Regex>();
      _regexs["Postnummer"] = new System.Text.RegularExpressions.Regex(@"^\d{4}$");
      _regexs["Heltal"] = new System.Text.RegularExpressions.Regex(@"^\d+$");
    }

    public void Show(System.IServiceProvider windowService,
      System.Diagnostics.IVisualizerObjectProvider objectProvider,
      System.Diagnostics.VisualizerUIType uiType)
    {
      // hent værdien af den string der debugges
      string stringValue = objectProvider.GetObject().ToString();

      // instantiér og vis form med regexs og værdi
      RegexForm frm = new RegexForm();
      frm.InitRegexes(stringValue, _regexs);
      frm.ShowDialog();
    }
  }
}

Og til slut laves den Form, som står for selve grænsefladen til udvikleren:

// 
// Copyright 2004, Captator 
//

namespace Captator.Diagnostics
{
  // partiel klasse - den af Whidbey genererede kode til Ui står i en anden fysisk fil
  partial class RegexForm : System.Windows.Forms.Form
  {
    // dictionary med regexs der er til rådighed
    private System.Collections.Generic.Dictionary<string, System.Text.RegularExpressions.Regex> _regexs;
    // den string-værdi, der skal testes
    private string _stringValue;

    public RegexForm()
    {
      InitializeComponent();
    }

    // Initialisér med regexs og string-værdi
    public void InitRegexes(string stringValue, 
        System.Collections.Generic.Dictionary<string, System.Text.RegularExpressions.Regex> regexs)
    {
      _regexs = regexs;
      _stringValue = stringValue;
      this.cboRegexOptions.Items.Clear();
      foreach (string name in regexs.Keys)
      {
        this.cboRegexOptions.Items.Add(name);
      }
      // vælg den første den bedste
      this.cboRegexOptions.SelectedIndex = 0;
    }

    public void ExecuteMatch()
    {
      // find ud af hvilket regex der blev valgt
      string name = this.cboRegexOptions.SelectedItem.ToString();
      if (_regexs.ContainsKey(name))
      {
        // lav match og fortæl udvikler hvorvidt det gik godt eller skidt
        bool match = _regexs[name].IsMatch(_stringValue);
        if (match)
        {
          lblMatchResult.Text = string.Format("'{0}' matcher {1}", _stringValue, name);
          lblMatchResult.ForeColor = System.Windows.Forms.Control.DefaultForeColor;
        }
        else
        {
          lblMatchResult.Text = string.Format("'{0}' matcher ikke {1}", _stringValue, name);
          lblMatchResult.ForeColor = System.Drawing.Color.Red;
        }
      }
      else
      {
        // 
        lblMatchResult.Text = "Der findes ingen regex '" + name + "'";
        lblMatchResult.ForeColor = System.Drawing.Color.Red;
      }
    }

    private void btnMatch_Click(object sender, System.EventArgs e)
    {
      this.ExecuteMatch();
    }
  }
}

Når man så har buildet skal dll'en lægges et sted, hvor Whidbey kan finde den. I denne version er det %Visual Studio 8%\Common7\Packages\Debugger\Visualizers\. Og næste gang man debugger et projekt og rammer et breakpoint, kan man i f.x. locals-vinduet se en lille lup ud for alle strings. Klikker man på den, åbenbarer der sig et væld af muligheder:



Og vi kan konstatere, at "8000" er et postnummer (ihvertfald i følge vores definition *<;o) og at "Dette er ikk..." ikke er.





Afsluttende

Som en lille bonus skal nævnes muligheden for at køre en command-line, når der fyres en post-build event. (Dette er i øvrigt også muligt i VS 2003). Under testningen af denne Visualizer blev det efterhånden kedeligt at skulle kopiere dll'en til Visualizer-mappen efter hvert build, og under Properties på projektet blev der sat en linie kode ind i Post-build event command line, der kopierede dll'en til den rette mappe:


copy "$(TargetPath)" "C:\Program Files\Microsoft Visual Studio 8\Common7\Packages\Debugger\Visualizers\"


Som de fleste andre eksempler kunne også dette have været skruet sammen på en anden måde. Men det illustrerer meget godt, hvordan man kan gøre stort set alt med den data man kigger på, mens man kigger på den. Hvis det var vigtigt at man under debuggingen kunne afgøre hvorvidt, der var tale om et gyldigt postnummer, kunne man måske henvende sig til en WebService eller en database for at afgøre dette. Mens man debugger...

Visualizers kommer virkelig til deres ret i forbindelse med udvikling af applikationer, hvor det er vigtigt at kende data og state for sine forretningsobjekter. Så kan man skrive en Visualizer, som præsenterer data på en (eller flere) fordelagtige, skræddersyede måder.



Abonnér på mit RSS feed.   Læs også de øvrige indlæg i denne Blog.