Jeg er træt af spam.  Jeg forstår ikke pointen med spam, for det må være meget begrænset, hvad der tjenes af penge på at sende spam ud.  Nogle spam mails indeholder endda kun volapyk, så jeg har ingen idé om, hvorfor folk sender dem ud.  Det er ikke fordi, jeg får oceaner af mails, men der dukker vel omkring 5 spam mails op om dagen.

På det seneste har jeg fået lidt mere seriøse og målrettede spam mails fra østeuropa og Indien med tilbud om at indgå samarbejde om outsourcing af udviklingsarbejde.  Disse mails bærer præg af, at de ikke bare er sendt ud i flæng, og at der rent faktisk står et reelt firma bag, som beskæftiger sig med programmering.  Det skumle ved disse mails er, at de er sendt til en af mine emailadresser, hvor mit navn ikke indgår, og ikke desto mindre er disse mails adresseret til “Jakob”.  Jeg har ingen idé om, hvordan afsenderen har fundet ud af at koble mailadresse og navn sammen, og det gør mig mistænksom.

Derfor har jeg en mistanke om, at et eller andet firma et eller andet sted har solgt min emailadresse.

Inspireret af Jeff Blankenburg har jeg derfor nu opsat en catchall emailadresse på mit domæne styrdindiabetes.dk, og hver gang jeg opretter en profil ved registrering på et eller andet website, benytter jeg en emailadresse opfundet til formålet.  Emailadressen findes ikke i virkeligheden, men når man sender til den, ryger den i min catchall mailbox, og jeg kan se, hvilken adresse, der er sendt til.

Hvis jeg f.eks. skal købe noget fra www.foobar.com, oplyser jeg mailadressen foobar på domænet styrdindiabetes.dk.  På den måde kan jeg forhåbentlig se, hvem synderen er, hvis jeg en dag begynder at modtage spam på den email.

Ulempen ved en catchall er, at man risikerer spam fra folk, der bare spammer tilfældige mailadresser på domænet.

Nu vil tiden vise, om idéen holder.

Mark Seemann skrev forleden et godt indlæg om de udfordringer, man møder, når man skal mappe DTO objekter til domain objekter og videre til viewmodel objekter.

Jeg er en af dem, der har forsøgt sig med at snyde og lade DTO objekter vandre op igennem forretningslaget, og det bliver hurtigt grimt. Bare det at DTO klasserne alle har default constructor gør, at vedligeholdelse af koden bliver en pine. Mark er inde på, at mapning mellem DTO objekter og domain objekter kan være svær, fordi ORM frameworks ikke understøtter mapping, når der ikke findes en default constructor og alle propeties ikke har en setter.

Jeg har haft nogenlunde succes med at bruge AutoMapper til det. Det viser sig, at AutoMapper er i stand til at mappe properties på source objektet til constructor parametre på destination objektet. Man skal bare sørge for, at parametre og properties hedder det samme (de behøver ikke have samme casing), hvilket i mine øjne i øvrigt er god ting at have i sin kodestandard alligevel (konsistent navngivning).

Lad os tage udgangspunkt i følgende klasser Track og DbTrack (tyvstjålet fra Mark’s indlæg):

   1:  public class Track
   2:  {
   3:      private readonly int id;
   4:      private string name;
   5:      private string artist;
   6:   
   7:      public Track(int id, string name, string artist)
   8:      {
   9:          if (name == null)
  10:              throw new ArgumentNullException("name");
  11:          if (artist == null)
  12:              throw new ArgumentNullException("artist");
  13:   
  14:          this.id = id;
  15:          this.name = name;
  16:          this.artist = artist;
  17:      }
  18:   
  19:      public int Id
  20:      {
  21:          get { return this.id; }
  22:      }
  23:   
  24:      public string Name
  25:      {
  26:          get { return this.name; }
  27:          set
  28:          {
  29:              if (value == null)
  30:                  throw new ArgumentNullException("value");
  31:   
  32:              this.name = value;
  33:          }
  34:      }
  35:   
  36:      public string Artist
  37:      {
  38:          get { return this.artist; }
  39:          set
  40:          {
  41:              if (value == null)
  42:                  throw new ArgumentNullException("value");
  43:   
  44:              this.artist = value;
  45:          }
  46:      }
  47:  }
  48:   
  49:  public class DbTrack
  50:  {
  51:      public int Id { get; set; }
  52:      public string Name { get; set; }
  53:      public string Artist { get; set; }
  54:  }

Track har ikke en default constructor og har setter properties for Name og Artist, men ikke for Id. AutoMapper kan – uden nogen egentlig konfiguration – benyttes til at mappe fra DbTrack til Track på følgende måde:

   1:  Mapper.CreateMap<DbTrack, Track>();
   2:   
   3:  var source = new DbTrack()
   4:  {
   5:      Id = 12,
   6:      Name = "Name12",
   7:      Artist = "PSB"
   8:  };
   9:   
  10:  var destination = Mapper.Map<Track>(source);

Kaldet til Mapper.Map i linje 10 vil først kalde constructoren på Track med de rigtige parametre bestemt ud fra parametrenes navne, og da Track klassen har setters på to properties, vil disse properties blive kaldt efterfølgende. Hvis Track klassen kun havde getters på alle properties, ville det naturligvis kun være constructoren, der blev kaldt. Hvis Track klassen har parametre i constructoren, som har navne, der ikke kan matches med properties på DbTrack klassen, vil AutoMapper fejle med en exception.

TFS er en god ting at have (især når man tidligere brugte VSS), men da antallet af aktive udviklere lige i min afdeling (som er et aktuariat) kan tælles på en finger, er “vi” ikke storforbrugere af de avancerede TFS features med automatisk builds og what-not.  Der er derfor nok at lære, når man ind imellem støder på noget ud over det lidt mere sædvanlige som check-ins og bug reports.

Forleden løb jeg ind i problemer, da jeg havde fat i et projekt, der kun sjældent bliver rettet i.  Det viste sig, at en kollega, der ikke længere sidder i aktuariatet, havde nogle udestående pending changes på nogle filer.

En søgning ledte mig hurtigt til “tf undo” kommandoen, som giver mulighed for at lave undo på pending changes for andre brugere i andre workspaces.  Så med følgende kommando var jeg hurtigt kørende igen:

tf undo /workspace:kollegasmaskinnavn;kollegasinitialer /recursive $/Foldernavn/Projektnavn /server:TFSservernavn

Jeg har for lang tid siden skrevet et indlæg på dotninjas om, hvordan man laver en single file generator (også kaldet et custom tool) til Visual Studio.  Desværre ligger det så lang tid tilbage, at jeg ikke kan grave et link frem.  En single file generator benyttes mange steder til at genere kode ud fra indholdet af en anden fil.  For eksempel benyttes det af Entity Framework til at lave klasser ud fra en XML fil som vist på billedet herunder, hvor Custom Tool er sat til “EntityModelCodeGenerator”.

CustomTool

For at lave en Single File Generator skal man implementere nogle bestemte COM interfaces og registrere sin dll på en bestemt måde i registry, og metoden fra min gamle (bortkomne) artikel har fungeret fint indtil fornylig, hvor jeg langt om længe fik mig en Windows 7 64 bit boks på arbejdet.  Jeg har prøvet alt på den maskine både med Wow32To64, og hvad ved jeg.  Men VS 2010 blev ved med fuldstændig at ignorere mit gamle custom tool.

På MSDN er der dokumentation af, hvordan man laver Single File Generators, og der findes også et sample i VS SDK’en, som beskriver, hvordan man kan lave en Single File Generator vha. VS Extensions.  Eksemplet virker fint, men det er ikke særlig godt beskrevet.  Især er det ikke beskrevet, hvordan man sætter projektet op til at blive kompileret helt korrekt, så registry entries bliver genereret korrekt.  Det håber jeg, at dette indlæg kan råde bod på.

Målet er at lave en Single File Generator, der nemt kan distribueres vha. af en .vsix pakke, så den nemt kan installeres som en Visual Studio Extension.  Dette er noget nemmere end min gamle metode, hvor der skulle registreres en dll vha. regasm.exe.

Det er en forudsætning, at man har Visual Studio Service Pack 1 samt Visual Studio SDK Service Pack 1 installeret.  Når SDK’en er installeret, er der adgang til et par VS projekttemplates, som kan bruges til at lave VS Extensions.  Til at lave en Single File Generator, skal man bruge den template, der hedder “VSIX Project”.

vsixproject

Efter projektet er oprettet, skal man først tilføje nogle referencer til en række dll’er for at få fat i de rigtige COM interfaces.  Det drejer sig om følgende:

  • EnvDTE
  • EnvDTE80
  • Microsoft.VisualStudio.Designer.Interfaces
  • Microsoft.VisualStudio.OLE.Interop
  • Microsoft.VisualStudio.Shell.10.0
  • Microsoft.VisualStudio.Immutable.10.0
  • Microsoft.VisualStudio.Interop
  • Microsoft.VisualStudio.Interop.8.0
  • Microsoft.VisualStudio.Interop.9.0
  • Microsoft.VisualStudio.Interop.10.0
  • VSLangProj
  • VSLangProj2
  • VSLangProj80

Det er muligt, at nogle enkelte referencer ikke er nødvendige.  Det har jeg ikke testet.  Det er i øvrigt vigtigt, at alle referencer har “Embed Interop Types” sat til “False”.

EmbedInteropType

Med det ovennævnte sample følger nogle standardimplementationer af to interfaces IVsSingleFileGenerator og IObjectWithSite.  Det nemmeste er at bruge disse implementationer, så vi kopierer BaseCodeGenerator.cs og BaseCodeGeneratorWithSite.cs.  Bemærk, at BaseCodeGenerator indeholder nogle Trace.WriteLine statements, man skal fjerne for at kompilere, hvis man kopierer de to klasser direkte fra eksempelkoden.

Dernæst tilføjes filen CodeGeneratorRegistrationAttribute.cs, som kommer med SDK’en, og som standard ligger under “C:\Program Files (x86)\Microsoft Visual Studio 2010 SDK SP1\VisualStudioIntegration\Common\Source\CSharp\RegistrationAttributes\”.  Denne klasse indeholder en implementation af en custom attribute, som VS benytter til at generere de nødvendige registry entries i en såkaldt .pkgdef fil.  Mere om det om lidt.

Det er nu tiden til at implementere selve kodegenereringen.  Dette gøres ved at tilføje en klasse, som skal nedarve fra BaseCodeGeneratorWithSite.

    [ComVisible(true)]
    [Guid("A4008ECD-02C6-418F-A0BD-083461479064")]
    [CodeGeneratorRegistration(typeof(DotNinjasCodeGenerator), 
        "DotNinjas sample C# code generator", 
        vsContextGuids.vsContextGuidVCSProject, 
        GeneratesDesignTimeSource = true)]
    [ProvideObject(typeof(DotNinjasCodeGenerator))]
    public class DotNinjasCodeGenerator : BaseCodeGeneratorWithSite
    {
        protected override byte[] GenerateCode(string inputFileContent)
        {
            return Encoding.ASCII.GetBytes("// Some generated code goes here");
        }
    }

Ikke overraskende er det metoden GenerateCode, der kaldes af VS, når man kører sit custom tool.  Parameteren inputFileContent vil indeholde indholdet af den fil, som vores custom tool bliver anvendt på.

Bemærk den imponerende række attributes på klassen.

Klassen skal nødvendigvis være COM visible og dermed også have en Guid attribute (husk også at gøre hele assembly COM visible).  CodeGeneratorRegistration benyttes som nævnt til at generere de nødvendige registry settings automatisk.  Disse registry settings ryger i en .pkgdef fil.  Bemærk vsContextGuids.vsContextGuidVCSProject som angiver, at vores kodegenerator vil generere C# kode.  ProvideObject fortæller VS, hvilken klasse der skal oprettes til at generere koden.  Det er et krav, at klassen markeret med ProvideObject skal have en default constructor.  Bemærk at det lidt overraskende er ProvideObjectAttribute, der bestemmer navnet på vores generator.  Dvs. i dette eksempel er det “DotNinjasCodeGenerator”, der skal benyttes som navnet på vores custom tool i VS.

Projektet er nu klar til at kompilere, og der kommer ganske rigtigt en .vsix fil ud som resultat.  Desværre er det ikke nok.  Der mangler en dll og der mangler en .pkgdef fil med de nødvendige registry entries.  For mig var det lidt et mysterium, hvordan jeg fik disse filer genereret, men det kræver, at man redigerer direkte i .csproj filen.  Så start din favorit csproj editor (såsom notepad) og slet følgende linjer:

    <GeneratePkgDefFile>false</GeneratePkgDefFile>
    <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer>
    <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer>
    <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>
    <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory>
    <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>

GeneratePkgDefFile skal væk, så vi får vores .pkgdef fil, og CopyBuiltOutputToOutputDirectory forhindrer, at der bliver kompileret en dll.  Tilføj desuden følgende PropertyGroup i .csproj filen og gem den:

  <PropertyGroup>
    <RegisterOutputPackage>true</RegisterOutputPackage>
    <RegisterWithCodebase>true</RegisterWithCodebase>
  </PropertyGroup>

Efter reload af den nye projektfil, vil en kompilering give følgende output:

projectoutput

Man kan nu teste pakken ved at trykke F5 for debug, hvilket vil starte en ny instans af VS, hvor man kan afprøve vores custom tool.  Vi vil i stedet prøve vores vores custom tool i den virkelige verden.  Så start med at lukke alle instanser af VS og dobbeltklik på .vsix filen og tryk “Install”.  Dette vil installere vores custom tool som en VS Extension, og ved opspart af VS vil man ganske rigtigt kunne finde DotNinjasSingleFileGenerator som installeret extension.  Ønsker man at angive en lidt mere sigende beskrivelse end bare “Empty VSIX project”, kan det gøre ved at rette i source.extension.vsixmanifest filen i VS.

extension

For at teste DotNinjasSingleFileGenerator, opret et nyt C# projekt, tilføj en fil af en type, som normalt ikke benyttes i et C# projekt (f.eks. en .txt fil men ikke en .cs fil) og angiv DotNinjasCodeGenerator som custom tool, da det var DotNinjasCodeGenerator, der blev angivet som type til ProviderObjectAttribute.

customtoolinaction

Højreklik på din fil og vælg “Run custom tool”, hvilket vil resultere i en .cs fil indholdende den genererede kode.

Det er et meget positivt tiltag fra Apple, at de understøtter Nikon’s RAW format (dvs. .nef filer).  Det eneste, der er påkrævet, er Apples Camera Connection Kit til sølle 200 kr., og så er man kørende.  Sæt camera connector i iPad’en og forbind med USB kabel til kameraet, så importeres billederne til iPad’en.

Man skal dog lige være opmærksom på en lille detalje.  Billederne ligger i iPad’en som .nef filer, medmindre man i vanvare kommer til at trykke rotér-knappen (se skærmbillede).  Uden yderligere advarsel bliver billedet konverteret til .jpg under roteringen.  Det skal man lige være opmærksom på, hvis man har tænkt sig senere at flytte RAW billederne til PC til videre efterbehandling.

foto

Benytter man HTTP stacken fra Silverlight – herunder kald af WCF services over HTTP – sker kaldet asynkront.  Derfor skal man passe på, hvis man opdaterer brugerkontroller i sin callback funktion, da den kan blive kaldt på en anden tråd end den, der ejer brugerkontrollen.  Det er gamle nyheder.  Bruger man et pattern som f.eks. MVVM, hvor opdatering af ens view sker gennem binding til properties på en viewmodel, sker marshalling til UI tråden automatisk af Silverlight’s binding mekanisme.  I andre tilfælde er man nødt til selv at sørge for, at brugerinterfacet opdateres på den rigtige tråd f.eks. gennem Dispatcher.

Jeg har først fornylig opdaget, at callbacks fra WCF services i nogle tilfælde automatisk bliver flyttet til den kaldende tråd i Silverlight.  Det gælder også Silverlight til WP7.  Det sker i det tilfælde, hvor man benytter client proxy klasserne, der genereres, når man vælger “Add service reference” fra Visual Studio. 

Lad os sige, at jeg har en service kaldet Service1 med en metode DoWork.  Jeg har tilføjet en reference til servicen gennem VS, hvorved et namespace ServiceReference1 med en proxy klasse kaldet Service1Client er blevet oprettet.  Følgende kode er dermed fuldt lovlig:

label1.Text = Thread.CurrentThread.ManagedThreadId.ToString();

ServiceReference1.Service1Client service = new ServiceReference1.Service1Client();
service.DoWorkCompleted += (s, args) =>
    {
        // Dette kald er fuldt lovligt, og label1 og label2 vil vise samme thread id.
        label2.Text = Thread.CurrentThread.ManagedThreadId.ToString();
    };
service.DoWorkAsync();

Kigger man dybt nok i de genererede klasser, kan man se, at de klassen AsyncOperation benyttes til at flytte kaldet tilbage til den kaldende tråd.  Har man brug for at lave noget arbejde i sin callback fra sin service, må man undlade at bruge den genererede client proxy og f.eks. bruge ChannelFactory eller noget andet.  Forrige eksempel er funktionelt ækvivalent med nedenstående, hvor marshalling til UI tråden sker manuelt gennem brug af AsyncOperation.  Bemærk at AsyncOperation skal oprettes gennem AsyncOperationManager.CreateOperation:

AsyncOperation asyncOp;

void CallWcf()
{
    asyncOp = AsyncOperationManager.CreateOperation(1);
    ServiceReference1.IService1Channel channel 
        = new ChannelFactory<ServiceReference1.IService1Channel>("BasicHttpBinding_IService1")
        .CreateChannel();
    channel.BeginDoWork(DoWorkCallback, channel);
}

void DoWorkCallback(IAsyncResult result)
{
    ServiceReference1.IService1Channel channel = result.AsyncState as ServiceReference1.IService1Channel;
    channel.EndDoWork(result);

    asyncOp.PostOperationCompleted(new SendOrPostCallback(o =>
        {
            label2.Text = (string) o;
        }), Thread.CurrentThread.ManagedThreadId.ToString());
}

I stedet for AsyncOperation kunne man også vælge at bruge SynchronizationContext eller Dispatcher direkte.  Faktisk er AsyncOperation blot en wrapper omkring SynchronizationContext, og i Silverlight er SynchronizationContext en instans af DispatcherSynchronizationContext, som igen er en wrapper omkring Dispatcher.  Så Dispatcher er den rigtige helt her.

Som jeg har nævnt før, og som alle nok efterhånden har hørt, så kan WP7 ikke synkroniseres med Outlook.  Det kom som lidt af en overraskelse for mig.  Jeg havde helt klart en forventning om, at Zune ville kunne synkronisere Outlook.  Efter det første chok havde lagt sig, begyndte jeg naturligvis at søge efter andre metoder.

WP7 synkroniserer helt perfekt med OWA, Windows Live og GMail.  Min første tanke var derfor at bruge firmaets OWA.  Desværre arbejder jeg i et firma, hvor OWA er et fy-ord, og man skal gennem VPN, Citrix og alt muligt, for at komme i nærheden af sin mailbox.

Næste bud på listen var Windows Live.  Mærkeligt nok var jeg ikke i stand til finde en mulighed for at synkronisere Outlook til Windows Live.  Der er mulighed for at få sin Windows Live kalender vist i Outlook, men det er den forkerte vej.  Hvis nogen kender en måde at synkronisere mellem Outlook og Windows Live, så lad mig endelig høre.

Sidste mulighed var GMail.  Google har deres egen Google Calendar Sync.  Desværre kunne jeg ikke få den til at virke overhovedet.  Den kom med en underlig fejl, og jeg har Google mistænkt for at have skrinlagt komponenten.  Ærgerligt.  Efter noget søgen faldt jeg over Ogg Sync.  Ogg Sync kan ifølge eget udsagn synkronisere både kalender og kontakter, hvilket var, hvad jeg havde brug for.  Jeg købte Ogg Sync (for $30 tror jeg), og synkronisering af Outlook kalender med GMail fungerede perfekt.  Men desværre kan man ikke bruge samme licens til synkronisering af kalender og kontakter.  Om det er en bug i Ogg Sync vides ikke, for support svarer ikke på mails, og der er intet nævnt på web sitet.  Så pas på med Ogg Sync!

Efter lidt mere søgning fandt jeg GO Contact Sync Mod, som kan synkronisere Outlook kontakter med GMail.  Det fungerer næsten perfekt.  Der er dog nogle problemer med at synkronisere kontakter, som bare har et firmanavn.  Men det er småting.

Efter møge og besvær, begyndte Google Calendar Sync på magisk vis også at virke, så Ogg Sync er sendt på pension, og jeg er endt med følgende setup:

  • Google Calendar Sync til at synkronisere kalender med GMail.
  • GO Contact Sync Mod til at synkronisere kontakter med GMail.

Det er lidt noget rod at skulle have flere “services” kørende bare for at kunne synkronisere.  Men der er også fordele ved, at Zune ikke kan synkronisere på desktoppen.  Sålænge min computer bare er tændt på arbejdet, vil Exchange på arbejdet være opdateret med de aftaler, jeg ligger ind i min WP7 – og omvendt.  Det er faktisk genialt, og nu hvor det bare spiller, synes jeg klart det er en bedre løsning en synkronisering med Outlook. 

Samtidig er min WP7 synkroniseret med en OWA fra mit eget firma.  I WP7’s kalender bliver aftaler fra de forskellige kalendere vist med forskellige farver, så man har fuldstændig styr på det.

Er der andre, som har erfaringer med synkroniseringen, og som måske har en smartere løsning, så vil jeg gerne høre det.

Siden april 2010 har jeg overladt dotninjas.dk fuldstændig i ne0san’s og pandasan’s kompetente hænder.  Men jeg har fornylig anskaffet mig en LG Optimus 7, der kører med Windows Phone 7, og sådan et indkøb er et oplagt emne til en blog, for der skal lidt arbejde til for at få det til at køre optimalt.  Mere om det nedenfor og forhåbentlig i nogle følgende indlæg.

Jeg vil egentlig ikke bruge så meget tid på at anmelde selve telefonen.  Den er fin og lækker i designet (men nok ikke “iPhone 4 fin og lækker”) med en dejlig stor skærm, og selve WP7 OS er et frisk pust ovenpå iOS og alle iOS klonerne, der er derude.  Den kan ikke multitaske og andet sjov, men det fungerer rigtig godt med tiles og de såkaldte panorama views, og når apps følger WP7’s design guide kaldet Metro er tingene meget lækre at arbejde med.

Dette var min anmeldelse af WP7 og LG Optimus 7.  Nu videre til opsætning, så man kan få lov til at bruge sine surt optjente penge i marketplace.

Et af de største problemer med WP7 i Danmark er, at den ikke officielt er kommet til Danmark.  Det betyder, at man som dansker ikke umiddelbart har adgang til marketplace, hvilket er Microsoft’s svar på iTunes App Store.  Men fortvivl ej – der er (vistnok flere) måder at omgå det på.  Følgende er, hvad jeg gjorde for at få adgang til marketplace i UK:

  1. Opret en Windows Live konto, hvor du angiver UK som dit hjemland.
  2. Opret en en Zune konto, hvor du angiver en rigtig adresse i UK.  Jeg brugte min storesøster’s adresse, idet hun tilfældigvis bor i England.  Denne adresse benyttes som billing address, men jeg har ikke oplevet, at der rent faktisk bliver sendt noget fysisk brev til adressen fra Zune.
  3. Tilknyt et kreditkort til den nyoprettede Zune konto.
  4. Download og installer Zune softwaren.  Zune benyttes til at synkronisere musik, video og billeder med telefonen.  Zune synkroniserer ikke med Outlook, og der er ingen umiddelbar måde at gøre det på.  Det vil jeg måske skrive om på et andet tidspunkt. 
  5. Zune klienten benytter de regionale settings på computeren, og så længe de er sat til Danmark, vil Zune ikke tillade login og adgang til marketplace via din pc.  Gå derfor i kontrolpanelet og vælg “Skift grænsefladesprog”.  Gå til fanen “Placering” og vælg “Storbritannien”.    Udklip
  6. Start Zune op igen og du vil opdage en “Sign in” knap øverst til højre.

Så er det tid til at sætte selve telefonen op.  Jeg kan ikke helt huske fremgangsmåden her, men jeg mener, det var noget i retning af følgende:

  1. Vælg “Settings”.
  2. Vælg “email & accounts”. 
  3. Vælg “add an account” og klik “Windows Live”.  Jeg mener at kunne huske, at WP7 kommer med en advarsel om, at den første oprettede Windows Live konto vil være den, der fremover er tilknyttet telefonen.  Derfor skal du bruge den Windows Live UK konto, du oprettede tidligere.

Det var det.  Det tog mig lidt tid at rode rundt mellem for at få det til at spille, men i virkeligheden er det meget få trin, der skal til.  Så vidt jeg kan se, er det desværre nødvendigt at oprette en ny Windows Live konto, hvor man angiver UK som land, da det land, der optræder i Zune profilen, er styret af ens Windows Live login.  ´

Diabetikere bør (i hvert fald i perioder) føre dagbog over blodsukkermålinger som minimum foretaget før og efter hvert hovedmåltid og umiddelbart før sengetid.  Samtidig skal man tælle kulhydrater og løbende holde styr på sammenhængen mellem kulhydratindtaget og antallet af insulinenheder, man injicerer.

I anledning af halvårsdagen for konstateringen af min type 1 diabetes, har jeg arbejdet på et website til online registrering af blodsukkermålinger samt af indtag af insulin og kulhydrater.  Udover at give mig selv og andre diabetikere et centralt sted at føre dagbog, er det også mit første rigtige “live” ASP.NET MVC 2 og .NET 4.0 projekt.

Bayer Diabetes lever bl.a. af at lave blodsukkerapparater.  Hvert år udbyder de en sum penge til projekter, som på en eller anden måde gør livet nemmere for diabetikere, og mit website er blevet valgt som en af tre finalister til at modtage hjælp fra deres fond.  Den endelige vinder findes ved afstemning, hvor alle interesserede kan stemme.

Derfor beder jeg dig, kære læser, om at gå ind på og stemmemit bidrag.

Plz, plz, plz

P.S. Mit website er endnu ikke online, men du kan se et par skærmbilleder herunder.

simplewins1 simplewins2 simplewins3

Extension methods har vist sig yderst bekvemme.  Det mest kendte eksempel er naturligvis det væld af extension methods, man finder i LINQ.  De giver en mere præcis og kort notation.  Jeg synes dog, at det kan tage overhånd, fordi folk har tendens til at bruge extension methods til at “dekorere” typer med metoder, som egentlig ikke hører hjemme på typen.  Når frameworks som Rhino Mocks og testdelen af MVC Contrib bruger extension methods på strenge og andre ret grundlæggende typer, kan jeg acceptere det, fordi brugen af test frameworks er så præcist afgrænset.  Men når folk f.eks. begynder at putte extension methods for query string parsing og andet godt på alle strenge i et projekt, mener jeg, at man er gået over grænsen.  Man skal stadig følge OO-reglerne for godt design af en type.

Min egen brug af extension methods har derfor været yderst begrænset.  Jeg har dog defineret to meget nyttige extension methods til WPF typen DependencyObject: GetChild og GetParent.

Metoderne kan benyttes til rekursivt at vandre op eller ned i det visuelle træ for et DependencyObject.  De returnerer det første objekt, de støder på i træet, som er af den type, der er angivet som typeparameter til metoden. 

Koden ser ud som følger.  GetChild har jeg tyvstjålet fra et sted på nettet, hvor man kan finde et utal af nærmest identiske implementationer af metoden.

public static class VisualHelpers
{
    public static T GetChild<T>(this DependencyObject referenceVisual) where T : DependencyObject
    {
        DependencyObject child = null;
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(referenceVisual); i++)
        {
            child = VisualTreeHelper.GetChild(referenceVisual, i) as DependencyObject;
            if (child != null && child is T)
            {
                break;
            }

            child = GetChild<T>(child);
            if (child != null && child is T)
            {
                break;
            }
        }
        return child as T;
    }

    public static T GetParent<T>(this DependencyObject referenceVisual) where T : DependencyObject
    {
        if (referenceVisual == null)
            return null;
        if (referenceVisual is T)
            return referenceVisual as T;
        var parent = VisualTreeHelper.GetParent(referenceVisual);
        return parent.GetParent<T>();
    }
}

Så hvad kan man bruge metoderne til?  F.eks. kan man bruge GetChild til at style textbox-delen af en combobox.  Det er der nemlig ikke mulighed for “out-of-the-box”.  Det kan gøres ved at nedarve fra ComboBox og lave en override af OnApplyTemplate.  I OnApplyTemplate benyttes GetChild til at finde combobox’ens ContentPresenter og ændre dens ContentTemplate:

public class ComboBoxEx : ComboBox
{
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        var selectionBoxHost = GetChild<ContentPresenter>(this);
        if (selectionBoxHost != null)
        {
            selectionBoxHost.ContentTemplate = ...;
        }
    }

}