Alle indlæg af Lars Støttrup

CSS3 og Intellisense til CSS3

Dette indlæg vil fokusere på CSS3 og efterfølgende hvordan du får Visual Studio til at genkende CSS3 attributter.
De CSS3 attributer vi vil fokusere på er: border-radius og box-shadow.
CSS3 er ikke finalized endnu, så det er altid en god idé at bruge browser specifikke tags, som “-o-box-shadow, -moz-box-shadow, -webkit-box-shadow”.
Disse bliver brugt i resten af dette indlæg, men vil ikke være del af kodeeksemplerne.
Note: Per dags dato understøtter Internet Explorer overhovedet ikke CSS3 endnu.

Lad os begynde med en simpel ustylet knap:

Lad os starte med at give knappen runde hjørner vha. attributten border-radius:

.button1
{
   border-radius: 6px / 4px;
   /* alternativ: border-radius: 6px 6px 6px 6px / 4px 4px 4px 4px */
}

Syntaksen for “border-radius” minder om de andre “border”-attributter. Kanten i hvert hjørne “trækkes” rundt om en usynlig cirkel med den radius som er defineret. Hvis man, som jeg har valgt her, bruger “/” får cirklen 2 radier, og bliver derved oval.
Den komplette syntaks kan ses her.

Lad os tage hul på “box-shadow”:

.button2
{
   background-color:#606EA6;
   border:1px solid #3B4466;
   color:#FFFFFF;
   box-shadow:0 3px 3px #8CA0F2 inset, 0 -5px 2px #3B4466 inset, 2px 2px 3px black;
}

“box-shadow” attributten tillader et ubegrænset antal skygger, blot de er komma-separerede.
På ovenstående knap tilføjes der derfor 3 skygger. De første 2 parametre i attributten definerer horisontal/vertikal forskydning af skyggen; tredje param angiver hvor meget blur-effekt skyggen skal have, hvor 0 er “ingen blur”. Efterfølgende defineres en farve, og “inset” (kan undlades) definerer at skyggen skal være inde i elementet, fremfor udenpå.
Den komplette syntaks for box-shadow kan ses her.

Så hvordan får vi Visual Studio til at genkende disse nye attributter?
(Hvis du ikke er komfortabel med at rode i Windows registreringsdatabase, skal du nok springe over dette trin)
Visual Studio gemmer de eksisterende templates i din Visual Studio installationsmappe, i mappen: \Common7\Packages\1033\schemas\CSS
Tag en kopi af css21.xml og kald den nye fil css3draft.xml. Åben filen og find den sektion der hedder “Edges Properties”.
Indsæt 2 nye noder med følgende:

<cssmd:property-def _locID="box-shadow" _locAttrData="description,syntax" type="composite"
            description="Attaches one or more drop-shadows to the box"
            syntax="inset? && [ length{2,4} && color? ] [ , inset? && [ length{2,4} && color? ]*" />

<cssmd:property-def _locID="border-radius" _locAttrData="description,syntax" type="composite"
            description="Defines the radii of a quarter ellipse that defines the shape of the corners of the outer border edge of an element"
            syntax="[ length | percentage ]{1,4} [ / [ length | percentage ]{1,4} ]?" />

Disse definerer vores 2 nye CSS3 attributter.

Kør regedit.exe og søg igennem HKEY_LOCAL_MACHINE efter en key (og kun keys!) med GUID: {A764E895-518D-11d2-9A89-00C04F79EFC3}
Du har fundet den rigtige GUID hvis den har en underfolder ved navn “Schemas” (og stadig er under HKEY_LOCAL_MACHINE).
Denne underfolder inderholder formegentligt “Schema 1” til “Schema 4”.
Lav en ny key ved navn Schema 5 og to strings med navnene “File” og “Friendly Name”.
Sæt file til css3draft.xml og “Friendly Name” til hvad end du nu har lyst til, f.eks “CSS 3 Draft”.

Menulinjen “Style” (i VS) vil nu have en ekstra værdi i dropdownlisten over CSS schemas.
Visual Studio Style Menu Bar

LINQ to XML

Language-Integrated Query (LINQ) er en teknologi der tillader query-egenskaber direkte i C#. I query-form ligner det SQL i syntax og opbygning. Alternativet er method-syntax som vi ikke vil bruge tid på i dette indlæg. Til de interesserede kan forskellen mellem query og method syntax ses her: http://msdn.microsoft.com/en-us/library/bb397947.aspx. MS anbefaler at man bruger query syntax alle de steder man kan, frem for method syntax.

LINQ to XML er en del af LINQ, og er designet til at trække data ud af xml, fra eksempelvis en fil.

Vi skal bruge namespacet ”System.Xml.Linq”. Dette namespace indeholder klasser som XDocument, XElement, XAttribute og XNode. Det er nogle af disse vi bruger i vores query.

LINQ syntaksen minder meget om SQL og kan ses her:
http://msdn.microsoft.com/en-us/library/bb308959.aspx#linqoverview_topic5

Lad os springe ud i det.

Lad os først finde noget XML vi selektivt vil trække data ud af. Følgende er et log udtræk fra SVN:

<?xml version=”1.0″?>
<log>
  <logentry revision=”200″>
    <author>Lars</author>
    <date>2010-06-03T08:24:19.329726Z</date>
    <paths>
      <path action=”M”>/sti/Upload.ascx.cs</path>
      <path action=”D”>/sti/Projekt.csproj.user</path>
    </paths>
    <msg>Changes to upload path handling</msg>
  </logentry>
  <logentry revision=”199″>
    <author>Lars</author>
    <date>2010-05-31T14:30:23.080950Z</date>
    <paths>
      <path action=”M”>/sti/Database.mdf</path>
      <path action=”M”>/sti/Database.ldf</path>
    </paths>
    <msg>Shrinked database again</msg>
  </logentry>
  <logentry revision=”198″>
    <author>Lars</author>
    <date>2010-05-31T13:59:33.689391Z</date>
    <paths>
      <path action=”A”>/sti/Projekt.sln</path>
      <path action=”A”>/sti/Projekt.suo</path>
    </paths>
    <msg>VS2010 Solution files.</msg>
  </logentry>
  <logentry revision=”197″>
    <author>Sral</author>
    <date>2010-05-28T07:10:51.597195Z</date>
    <paths>
      <path action=”M”>/sti/</path>
    </paths>
    <msg>Added files/folders to ignore filter.</msg>
  </logentry>
</log>

Først tæller vi alle log entries grupperet på author. Det kan klares således:

var output = from logentry in SvnLog.Elements("logentry")
    group logentry by (string) logentry.Element("author")
    into logentries
    select new { 
        Author = logentries.Key,
        Entries = logentries.Count()
    };

Vi piller querien fra hinanden og ser på de enkelte dele.

from logentry in SvnLog.Elements("logentry")

Dette statement returnerer en IEnumerable liste af XElementer indeholdende alle under-noder i hvert <logentry> element. Disse bliver så placeret i en variabel kaldet “logentry”.

group logentry by (string) logentry.Element("author") into logentries

Vi grupperer her på datasættet fra før med en under-node i <logentry> ved navn <author>. Note: Det er vigtigt at huske castet til string da de fleste klasser i System.Xml.Linq har custom type converters (http://msdn.microsoft.com/en-us/library/ayybcxe5.aspx).

Et cast til string giver elementets værdi, hvor logentry.Element(“author”).ToString() ville give en string repræsentation af objektet.

Til sidst placerer vi vores gruppering i en ny variable ved navn ”logentries”.

select new { Author = logentries.Key, Entries = logentries.Count() };

Her over laver vi en anonym type indeholdende vores fundne data (Anonymous Types: http://msdn.microsoft.com/en-us/library/bb397696.aspx)
Logentries.Key er altid hvad man grupperer på. I dette eksempel laver vi en simpel count på de fundne værdier.

Vi kører det igennem en foreach løkke:

foreach (var d in output) {
     Console.WriteLine("{0}: {1} entries.", d.Author, d.Entries);
}

Resultat:

Lars: 3 entries.
Sral: 1 entries.

Lad os prøve en lidt mere avanceret query med nestede selects.

Vi prøver følgende: Vores query skal tælle attributen action (i <path>) grupperet på værdien. For at være endnu mere vanskelig grupperer vi også på author igen.
Resultatet vi søger skulle gerne se sådan ud:

Lars: A:2, M:3, D:1
Sral: A:0, M:1, D:0

Querien kommer til at se således ud:

var data =
  from logentry
  in SvnLog.Elements("logentry")
  group logentry by (string)commit.Element("author")
    into logentries
    select new
    {
      Author = logentries.Key,
      Added = (
          from path in logentries.Elements("paths").Elements("path")
          where (string)path.Attribute("action") == "A"
          select path
      ).Count(),
      Modified = (
        from path in logentries.Elements("paths").Elements("path")
        where (string)path.Attribute("action") == "M"
        select path
      ).Count(),
      Deleted = (
        from path in logentries.Elements("paths").Elements("path")
        where (string)path.Attribute("action") == "D"
        select path
      ).Count()
    };

Det første i querien ligner statementet fra før: vi grupperer blot på author. Inde i vores anonyme type har vi nu 3 næsten ens selects:

from path in logentries.Elements("paths").Elements("path")

Vi finder alle ”path” noder som vores gruppering i det ydre scope indeholder.

where (string)path.Attribute("action") == "A"

Af de fundne path noder, finder vi alle dem der har attributten ”A”.

select path

Dem der matcher vores where clause selecter vi, plus pakker det hele ind til sidst, så vi kan lave en Count() på det.

foreach (var user in data) {
   Console.WriteLine("{0}: A:{1}, M:{2}, D:{3}",
      user.Author, user.Added, user.Modified, user.Deleted);
}

Og nu får vi det output vi søgte:

Lars: A:2, M:3, D:1
Sral: A:0, M:1, D:0

Click tracking

Vi har installeret CrazyEgg click tracking på hinnerup.net.

CrazyEgg er et værktøj til at visualisere brugernes færden på ens site, og indeholder smarte funktioner som: Click-overlay, Heatmap og Confetti.

Man indsætter en enkelt linje kode på de sider der skal trackes:

Derefter opsættes de sider der skal trackes i CrazyEgg:

Efter nogle dage begynder statistikken at være på plads.

Her er de forskellige overlays.

Standard overlay:

Heatmap:

Confetti:

Standard overlayet giver os mulighed for at se hvor mange clicks et givet link har fået. Heatmap er rimeligt selvforklarende – den viser os hvor “populært” et link eller anden detalje er. Den mest interessante er Confetti, som har følgende indstillings-muligheder:

Alle disse overlays skulle sammenlagt give ens hjemmesidedesigner mulighed for at designe en mere brugervenlig oplevelse.