Så er det igen tid til at komme ud af byen. Påsken er en god anledning til at finde dit kamera frem og ta' med dotninja'erne ud i Dyrehaven

Torsdag d. 1. april 2010 (Skærtorsdag)

Alle er (som altid) velkomne til at joine med deres ynglings kamera (eller bare et de har lånt), det er ikke en aprilsnar (Aprilsnar vil generelt være bandlyst under hele arrangementet). For de interesserede kan der blive tale om at returnere til Frederiksberg for et måltid mad efter arrangement.

Skriv en kommentar om du kommer og om du vil være interesseret i mad. Mødested/tid aftaler vi nærmere når vi nærmer os.

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

}

Indspireret af en artikel om Effect Sketches, og med hjælp fra Reflector og Graphviz, har jeg forsøgt at få et overblik over min kode. Spørgsmålet er så om det lykkedes, her er en klasse:

Kan man gætte hvad det skal forestillet? Evt. med inspiration i artiklen? Efter at jeg har set på det et stykke tid giver det god mening for mig. Jeg kan f.eks. se at yearFraction ikke er helt færdig implementeret -- men sourcekoden er også taget fra beta.

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.

I forbindelse med opgraderingen af Dotninjas har kommentarerne brugt deres ninja-skill til at forsvinde. Grundet finanskrisen kan der gå lidt tid før end vi får dem tilbage… Men der arbejdes på sagen.

Update: Kommenater skulle nu være tilbage… go nuts!

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.

Allerførst et ønske om en glædelig jul og et godt nytår :-)

 Dernæst et lille tip om at du kan erhverve dig en licens til Linqpad for kun 100kr ($19) frem til 31. december - den kan installeres på op til 3 maskiner samtidigt og er en varig licens til fremtidige versioner.

Hvis du ikke allerede kender Linqpad så er det et glimrende tidspunkt at teste det. Personligt bruger jeg den til mange af de situationer, hvor man liiige laver et lille testprojekt for at teste en stump kode - og ikke mindst til linq queries, entity framework forespørgsler, regex patterns etc.

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.

Tilmeld dig her

 Ja, yderligere introduktion af Scott Guthrie behøves vel ikke, men har du sovet under en sten de sidste mange år, så start med at se dokumentaren om Visual Studio på channel 9. Dokumentaren kan i øvrigt også anbefales selvom du kender både ScottGu og Visual Studio :-)

 Ses vi?

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