Jeg er blevet overbevist om, at mit næste projekt skal være et WPF projekt. WPF har et noget mere rent snit til design fladen, og jeg synes at databindings kontrollerne giver helt nye og spænnende muligheder.
Som du måske har læst tidligere, så har jeg lavet multitrådede applikationer, og haft en lille udfordring i at opdaterer formelementer fra andre tråde end UI tråden. Jeg fandt en elegant måde at foretage opdateringen på, men hvad nu med WPF kontroller?
For det første, så lever de i et helt andet namespace, og har en helt anden opbygning. En Label har ikke en property der hedder Text, men en der hedder Content. Det er jo simpelt nok at komme uden om – det er jo kun et navn. Alle indholdskontroller har en fælles baseklasse: ContentControl, som jeg tager udgangspunkt i, og laver en Extension på. Men hvad nu med de metoder og egenskaber man tidligere brugte til den slags: InvokeRequired, BeginInvoke, Invoke o.s.v.
De findes slet ikke. Jeg blev nød til at starte Reflector op, for at finde ud af det her. Det viser sig at al interaktion med kontrollerne går igennem et Dispatcher objekt. Det er her man skal tage udgangspunkt i sine krydstrådsopdateringer. InvokeRequired findes dog heller ikke på Dispatcher objektet, men man har adgang til arbejdstråden via egenskaben Thread. Man kan altså lave et check der nogenlunde svarer til InvokeRequired:
// WindowsForms:
if (ctrl.InvokeRequired)
// ....
// WPF:
if (ctrl.Dispatcher.Thread != Thread.CurrentThread)
// ..
Måske lidt underligt at den egenskab ikke er kommet med i API’et. Ellers er resten af koden til min extension nærmest identisk med den tidligere:
public static class ControlExtension
{
public static void SetContent(this ContentControl control, object value)
{
if (control.Dispatcher.Thread == Thread.CurrentThread)
control.Content = value;
else
control.Dispatcher.Invoke(new Action(() => control.Content = value));
}
}
That’s it.