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 = ...;
        }
    }

}

I WPF benyttes INotifyPropertyChanged hyppigt ifm. binding af view model til view.  De fleste er nok stødt på en variant af følgende standardimplementation:

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged(string propertyName)
{
    PropertyChangedEventHandler handler = this.PropertyChanged;
    if (handler != null)
    {
        var e = new PropertyChangedEventArgs(propertyName);
        handler(this, e);
    }
}

I ens view model kaldes OnPropertyChanged i get’eren i de properties, der indgår i binding til view:

public string SomeProperty
{
    get { return _someProperty; }
    set
    {
        if (_someProperty != value)
        {
            _someProperty = value;
            OnPropertyChanged("SomeProperty");
        }
    }
}
 

Det er naturligvis vigtigt, at parameteren til OnPropertyChanged er det samme som navnet på ens property.  Jeg ved ikke, hvor mange gange, jeg har glemt at opdatere det navn, når jeg har ændret navn på min property.  Man opdager hurtigt fejlen, men hvor det dog irriterende.  Så jeg har lavet en alternativ og meget simpel version af OnPropertyChanged, som benytter expression trees.  Den ser således ud:

protected virtual void OnPropertyChanged<T>(Expression<Func<T>> e)
{
    var member = (MemberExpression) e.Body;
    OnPropertyChanged(member.Member.Name);
}

 

Den nye overload kaldes med en lambda:

OnPropertyChanged(() => SomeProperty);

Hvis jeg fremover ændrer navnet på min property, vil VS2008 automatisk opdatere i min lambda.  Lamdaudtrykket skal have den form, som er vist ovenfor, ellers får man en exception.

Desværre løser det ikke alle problemer med renames, da jeg stadigvæk selv skal huske at opdatere i XAML-filen.

Jeg har eksperimenteret lidt med at streame film, musik og billeder fra min PC’er til min PS3.  Jeg har indtil videre forsøgt mig med tre forskellige produkter som media server.  Hver især har sine fordele.  Det drejer sig om følgende produkter: Microsoft Windows Media Player, PS3 Media Server samt WinAmp Remote (også kaldet Orb).

Microsoft Windows Media Player

Opsætningen er nem direkte fra Media Player’s “Stream” menu, og PS3 har ikke nogen problemer med at finde Media Player.  Jeg har ikke haft nogen problemer med at streame hverken film, musik eller billeder via Media Player så længe man holder sig til de gængse formater som avi, jpg og wma/mp3.  Media Player streamer ikke “låste” musikfiler fra f.eks. TDC Play.

PS3 Media Server

PS3 Media Server er til fri download, og det er utroligt fleksibelt.  Desværre går den øgede fleksibilitet ud over brugervenligheden.  Jeg må erkende, at jeg ikke ikke forstår halvdelen af de opsætningsmuligheder, der findes.  PS3 Media Server streamer film, musik og billeder uden problemer.  Den kender flere formater end Windows Media Player herunder Nikons RAW NEF billedformat, hvilket er ganske sejt.  Jeg ved dog ikke, om det kan anbefales, eftersom den genererer thumbnails af billederne, hvilket tager en rum tid, hvis en folder indeholder et par hundrede billeder af 10+ MB hver.

Streaming af film kræver at VLC Media Player er installeret.  Film kan encodes på forskellige måder, hvis man har forstand på sådan noget.

PS3 Media Server kan også streame YouTube film og efter sigende internet TV.  Jeg fik det til at virke for YouTube en kort overgang, men var en stakket frist.  Det skyldes enten, at jeg opgraderede til Windows 7, eller at jeg opgraderede VLC Media Player.

PS3 Media Server kan ikke håndtere filer fra TDC Play.

WinAmp Remote

WinAmp Remote er en del af den sædvanlige WinAmp download (man skal huske at tilvælge Remote i installationen).  Sjovt nok virker selve WinAmp ikke på min Windows 7 boks (til trods for WinAmps “Windows 7 Compliant” statement), men WinAmp Remote virker fint.  Selve brugerinterfacet til at konfigurere WinAmp Remote er sindssygt ringe.

Streaming af film fungerer ok, men kvaliteten er ret dårlig.  Dette kan måske konfigureres, men jeg har ikke fundet ud af hvordan.  WinAmp Remote afspiller YouTube film uden problemer, men jeg mistænker den for at gøre det med nedsat kvalitet.

Musikfiler afspilles uden problemer.  Det gælder også TDC Play!  Det skulle også virke med iTunes.

En anden sej ting ved WinAmp Remote er, at hvis man opretter et login, kan det streame musik fra ens PC til en browser hvor som helst.  F.eks. kan man via sin iPhone afspille musik hjemmefra.

Konklusion

Hvis man vil kunne det hele, må man nok ty til en kombination af ovenstående.  PS3 Media Server kan næsten det hele, men det er svært at få konfigureret rigtigt.  WinAmp Remote er dårligt til film, men med muligheden for at afspille filer fra TDC Play, har man adgang til alverdens musik gratis (hvis man vel at mærke er TDC kunde). 

Jeg har ikke forsøgt mig med TVersity, så jeg aner ikke, hvad det kan. 

Jeg vil også nævne VidZone, som er et nyt program fra PlayStation Store, der giver adgang til et pænt udvalg af gratis musikvideoer.

Det er nok ikke ukendt for de fleste, at Windows kontroller kun må tilgås af den tråd, som oprettede kontrollen.  Sådan er det også i WPF.  Heldigvis kan man sige.  Det er svært at forestille sig et brugerinterface, der kan opdateres af flere tråde.

Forleden lavede jeg en WPF applikation med et antal arbejdertråde, som hver især kommunikerede status tilbage til brugerinterfacet. Statusfelterne i brugerinterfacet gjorde brug af binding til properties på de bagvedliggende viewmodels.  Der var en viewmodel og et view til hver tråd, så der var ikke brug for synkronisering imellem trådene, men pludselig gik det op for mig, at disse properties blev opdateret af en anden tråd end min GUI tråd!   

Eftersom det hele virkede upåklageligt, måtte det være WPF binding, der gjorde min marshalling for mig.  Det måtte undersøges.

Til at illustrere har jeg lavet en lille WPF applikation med et vindue (view) indeholdende en textbox og en button.  Indholdet af textbox’en er bundet op vha. binding til en property (SomeData) på en klasse, som implementerer INotifyPropertyChanged.  Klassen ser således ud:

class ViewModel : INotifyPropertyChanged
{
    int _someData = 0;

    public int SomeData
    {
        get { return _someData; }
        set
        {
            _someData = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("SomeData"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

 

I koden til vinduet sættes binding op.  Når brugeren trykker på knappen, startes en tråd op, som opdaterer SomeData property, hvorved textbox’ens indhold opdateres.  Det er her magien sker, og WPF tager over og på en eller anden måde sørger for, at min binding sker på den rigtige tråd.  Koden til vinduet ser således ud:

 

public partial class Window1 : Window
{
    ViewModel _viewModel;
    Thread _t;

    public Window1()
    {
        InitializeComponent();

        _viewModel = new ViewModel();
        gridLayout.DataContext = _viewModel;

        textBoxSomeData.SetBinding(TextBox.TextProperty, new Binding("SomeData"));
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        _t = new Thread(new ThreadStart(ThreadStart));
        _t.IsBackground = true;
        _t.Start();
    }

    void ThreadStart()
    {
        for (int i = 0; i &lt; 100; i++)
        {
            _viewModel.SomeData = i;
            Thread.Sleep(100);
        }
    }
}

 

Sætter man et breakpoint i ThreadStart funktionen og begynder at debugge sig vej et stykke ind i Microsoft’s kode, rammer man på et tidspunkt en intern klasse ved navn MS.Internal.Data.ClrBindingWorker.  Denne klasse har en metode OnSourcePropertyChanged.  Inden i den metode finder man (blandt så meget andet) følgende kode:

 

if (Dispatcher.Thread == Thread.CurrentThread)
{ 
    PW.OnPropertyChangedAtLevel(level);
} 
else 
{
    // otherwise invoke an operation to do the work on the right context 
    SetTransferIsPending(true);
    Dispatcher.BeginInvoke(
        DispatcherPriority.DataBind,
        new DispatcherOperationCallback(ScheduleTransferOperation), 
        new object[]{o, propName});
} 

 

Af denne kode fremgår det, at WPF selv finder ud af, at der sker binding på tværs af tråde, og derfor benyttes Dispatcher.BeginInvoke og korrekt marshalling sker.

Det er smukt.  Endnu engang stor respekt for WPF binding.

Sidste uge var der Dotninjas Photowalk ved Vallø Slot. Til trods for dårligt vejr og et lidt sølle fremmøde, havde vi 3 ninjaer en fremragende tur, der blev afsluttet med frokost på en café i Køge.

Billeder blev der heldigvis også taget lidt af.  Her er et udpluk fra mit eget galleri.

 DSC_0552DSC_0570   DSC_0586 DSC_0599 DSC_0594

Vi er nok mange, der kan blive enige om, at iTunes er lige så dårligt, som iPhone er fed. Forleden kom jeg dråben, der fik mit bæger til at flyde over, og iTunes blev afinstalleret.

Det var i forbindelse med, at jeg forsøgte at opgradere min Windows Vista Ultimate til Windows 7 Ultimate.  Inden installationen blev jeg advaret af Windows 7 installationsprogrammet om, at iTunes måske ikke ville fungere korrekt efter opgraderingen, men det valgte jeg at se stort på.  "Der kan vel ikke ske noget ved, at det ligger der", tænkte jeg.

Installationen forløb glat.  Efter opgraderingen var slut, og maskinen genstartede første gang, fik jeg den frygtede Blue Screen of Death.  Heldigvis er Windows 7 cool nok til at rulle tilbage til den oprindelige installation, når den ikke kan genstarte.  Sejt.

Jeg fjernede et par programmer såsom MagicDisk, og prøvede igen.  Samme resultat.

Jeg fjernede et par andre programmer og hev stikkene til printere og andet løst hardware ud.  Samme resultat.

Husk at hver gang gik der 1-2 timer, før maskinen genstartede.

Jeg afinstallerede iTunes.  Det virkede!

iTunes kommer ikke tilbage på min hjemmemaskine foreløbig.  Til gengæld er jeg indtil videre glad for Windows 7.

Dette er ikke en entry om noget smart .NET eller en ny gadget.  Dette er en entry om den fantastiske  hest, som i sin tid lagde navn til mit doninjas nick – nemlig hesten Tarzan.

For to dage siden, måtte vi desværre sige farvel til Tarzan.  Han nåede lige at blive 18 år, og vi var de lykkelige ejere i de sidste 17 år.

Tarzan var noget helt særligt.

Han var en ballademager men en charmetrold.

Han var trodsig men altid villig til at lytte.

Han var egenrådig men altid glad for at se os.

Farvel Tarzan – du var den bedste, og du er savnet.

DSC_0642    DSC_0343DSC_0545DSC_0131DSC_0484

UPDATE: I stand corrected.  Belært af Mark og Rasmus (se kommentarer nedenfor) har jeg opdateret mit kodeeksempel med nyeste Rhino Mocks funktionalitet.  Tak for det.

Jeg ser flere og flere referencer til AAA på nettet (se f.eks. RasmusKL og Roy Osherove).  Jeg har indtil nu været ganske tilfreds med Record/Playback metoden for Rhino Mocks, men jeg kan sagtens se pointen bag AAA-stilen, så det er tid til at skifte vane.

Jeg har indtil fornylig brugt Rhino Mocks v3.4.  Det viser sig, at der lidt udfordringer i at følge AAA med den måde, Rhino Mocks 3.4 fungerer.  Problemet ligger i måden, man sætter expectations op.

Antag vi har følgende interface ITest, som vi ønsker at mocke:

public interface ITest
{
    int DoStuff(int a);
}

 

Vi kan sætte expectations op på følgende måde:

ITest testMock = mocks.CreateMock<ITest>();
Expect.Call(testMock.DoStuff(11)).Return(42);

Expectations bør ligge i Arrange-blokken, men vi forventer, at DoStuff bliver kaldt med værdien 11, hvilket faktisk er en Assert.  Ønsker man at sætte yderliger constraints op på parametre, skal det også ske i Record fasen, som hører under Arrange-blokken, men constraints bør ligge i Assert-blokken.

Rhino mocks v3.5 giver en helt ny API, som gør, at vi nu kan skrive vores test på en helt anden måde (tak til Rasmus):

// Arrange
ITest testMock = MockRepository.GenerateStub<ITest>();
testMock.Stub(x =>; x.DoStuff(Arg<int>.Is.Anything)).Return(42);

// Act
testMock.DoStuff(11);

// Assert
testMock.AssertWasCalled(x => x.DoStuff(Arg.Is(11)));

Testen er nu delt op i de tre AAA-blokke som ønsket.

De fleste som har programmeret Windows applikationer med et brugerinterface ved, at man ikke kan tilgå vinduer fra andre tråde end den tråd, der har oprettet vinduet, og som står for at pumpe beskedkøen til vinduet.

I klassisk Win32 er det ganske simpelt at overholde denne regel, da man med PostMessage nemt kan placere en besked i et vindues beskedkø, selvom man kalder fra en anden tråd.  PostMessage returnerer øjeblikkeligt.  SendMessage er en lidt anden historie.  Såvidt jeg husker, bør man ikke kalde den fra en anden tråd, da man kan risikere, at man bliver blokeret i længere tid indtil tråden, der ejer vinduet, vågner op og får pumpet nogle beskeder.  Hvis SendMessage kaldes fra den ejende tråd, bliver Windows proceduren kaldt direkte, hvilket effektivt svarer til at placere en besked forrest i beskedkøen og pumpe med det samme.

I .NET og WinForms benyttes Control.InvokeRequired, Control.Invoke og Control.BeginInvoke.

Jeg har altid tænkt på Control.Invoke som svarende til SendMessage og Control.BeginInvoke som svarende til PostMessage.  I praksis passer det også meget godt, men det er ikke helt det samme der sker.  Faktisk bliver PostMessage kaldt af både Invoke og BeginInvoke.

Når man benytter Invoke og BeginInvoke, registrerer WinForms en ny besked i Windows og benytter PostMessage til at anbringe beskeden i beskedkøen for vinduet (i Win32 er der i princippet ingen forskel på kontroller og vinduer – alt er vinduer).  Forskellen på Invoke og BeginInvoke er, at Invoke venter indtil vinduets beskedkø har behandlet beskeden, mens BeginInvoke returnerer med det samme.

Resultatet er, at der for Invoke godt kan gå længere tid, inden funktionen returnerer, hvis der allerede ligger en milliard beskeder i køen, eller vinduet af den ene eller anden grund ikke lige pumper beskedkøen. 

Medmindre man virkelig har brug for at vente, vil BeginInvoke derfor være at foretrække.