Sunday, 24 May 2009

Tech Day #5 – My First Look at WPF

So, I wanted to do something a bit fresher and possibly more interesting for TD#5. I have been cramming a lot for my next MS exam of late, so wanted to just have some fun and not really think about anything :)

I therefore scanned my Tech ToDo list and and through I would go for WPF. Silverlight is quite high on my list, so I thought it would be good to get a high-level look at WPF in general (since I have not yet worked with XAML).

So, let’s rock and roll!

Getting Started/Initial Setup

Of course I already have VS 2008, but if not, go and download the VS2008 Express Edition in your language of choice (VB or C#).

I also use O’Reilly Safari, so I had a look on there for any WPF books. I came across Pro WPF in C# 2008 (Matthew MacDonald), so I added that to my library and began reading while nomming down on my breakfast.

As always, there is some sample code for the tech day, not much for this one that is really functional since I am really playing with some basic ideas.

Let’s crack on and see what I learned!

Default Files

Once I had created the WPF application I thought I would take a look at what the project template had churned out for us. These really look very similar to a basic WinForms application. You get an “App.xaml” which is the same idea as “Program.cs” (essentially the bootstrap for the application). This will be the entry point for the application which will spin up the code that we implement.

Obviously there are some different references in our project. Remember that WPF is a completely different rendering engine to WinForms. While you will see that some of the concepts are similar (Brushes and the like) it is actually a completely different code set and hierarchy. Similar to WebForms vs. MVC – we still have the power of the .NET Framework itself, just a different way or processing things high up in the UI tier.

Layout

In Pro WPF in C#' 2008 (from now on “the book”) it was very clear from the outset that the layout model is very different to what we are used to in the WinForms world. Layout in WPF is designed to be flexible and flowing. This means (for the most part) “bye bye Top and Left”. Rather than defaulting to a fixed size/co-ordinate system WPF works around we use a more web-like document flow where we specify sizes and margins and let the rendering engine sort it out.

XAML Markup

I thought I would be worried once I sat down with the XAML since I thought “great, another language to learn”. Thankfully, I was incorrect in this assumption. Those of us that have worked/work with ASP.NET should be pretty comfortable with XAML since we are used to the way markup is parsed and used to spin up actual objects that do things. Those from WinForms may find it a little alien at first, but you should get the hang of it pretty quick. Just think “new” when you see “<”. For example:

   1: <someTag value="this is some txt" />

Could equate to the following C#:

   1: var st = new someTag()
   2:  { Value = "this is some text."; }

Once you get your head around the fact that markup can actually do stuff (as opposed to pretty dumb HTML) it' becomes pretty straight forward.

Attached Properties

I also found that a fair few of the controls follow the Attached Properties model. Normally, in order to make controls work together you may need to start messing with code at both ends as well as alter the way in which the controls are initialised.

For example, lets say we had a control that adds sprinkles to other controls, we may need to controls to it to get the sprinkles added to it:

   1: <Sprinkler Name="mySprinkles" Type="HundredsThousands">
   2:  <myDonut Name="nomnom" Type="Ring" />
   3: </Sprinkler>

While this may look fine, what if we were producing a page that had 1000 donuts that all needed sprinkles? We would suddenly need to start creating lots of spinklers which could be expensive. Not to mention the fact that we may actually want to put the donuts in a DonutBox!

Now, lets think about this, of course the sprinkler needs to know about the donut(s) we want to sprinkle, but do we need to “put the donuts in the sprinkler” (logically rather than physically)?

Consider the following:

   1: <Sprinkler Name="mySprinkles" Type="HundredsThousands" />
   2:  
   3: <myDonut Name="nomnom" Type="Ring" Sprinkler="mySprinkles" />
   4: <myDonut Name="homer" Type="Ring" Sprinkler="mySprinkles" />
   5: <myDonut Name="omgiatethebag" Type="Filled" Filling="Jam" Sprinkler="mySprinkles" />

See the difference? We just added a “Sprinkler” property to the donut classes that takes a Sprinkler, right?

Wrong!

The properties are actually added to the donut controls by the Sprinkler. This is very similar to the Extender Provider model employed in WinForms (e.g. the ToolTip Component). This enables us to create controls that extend other classes/controls in a non-obtrusive and clean way. Attached Properties are an improvement over Extender Providers since the methods called are static you don’t need to actually have an instance of the class floating around.

What’s really cool is that you would then think that the Sprinkler is somehow maintaining a list of the donuts it is sprinkling, right? Possibly, but it doesn’t need to because the donuts themselves actually store the property value. This is because all controls derive from DependencyObject.

User Controls

I was really pleased to see that User Controls are just as quick and easy to create in WPF. These are much the same as Win/WebForms User Controls with the obvious difference being that the designer works with XAML.

One thing I would note is that since the layout of WPF applications is a bit more dynamic and free-flowing, I would suggest testing your controls thoroughly in a number of different host forms to ensure everything looks good when resizing etc.

Animations

Even though I have not really produced much in the way of an “application” at this point (as in something that performs a function/fulfils a purpose) I didn’t really feel like WPF would be that much more of a mystery to me. I am more than capable of Google’ing for specific details on certain controls etc. I therefore thought I would spend a bit of time playing with some of the cooler things that WPF has to offer. Animation is obviously a big part of this.

When I first started looking at animation, I did get a bit confused I think this may be because a few of the articles I came across were really not very good at breaking things down. I will do my best to explain the animation to you in a way that means you don’t have to go back to Google like I did :)

The Storyboard

Imagine you are creating a reel of film. You have a ton of smaller film strips spliced up and you want to tape them all together in one kick-ass movie. Think of the Storyboard as the thing you are taping them to.

The Storyboard orchestrates a sequence of animations for you. Animation sequences are added to a Storyboard and then you (as the director of course) shout “Action!” and the sequence starts rolling.

So, before we do anything, we must create a Storyboard:

   1: var storyboard = new Storyboard();

Now we take a break and grab a coffee after all that code.

Animate What?

Let’s say for sake of argument our animation of choice is the good old “fade out”. Traditionally in WinForms we would probably add a Timer to the form and then tell it to reduce a Controls opacity on each Tick, but surely there is a cleaner, smarter approach with WPF?

Indeed there is. Opacity is a property of the “double” type. So what we really need is something to wrap up the Timer and reduction of a double value over time. Enter the DoubleAnimation. This is exactly what he doctor ordered and exactly how animation is done in WPF. We manipulate the properties of controls over time to animate it.

For example:

   1: var animation = new DoubleAnimation()
   2: {
   3:     From = 1.0,
   4:     To = 0,
   5:     Duration = new TimeSpan(0, 0, 1)
   6: };

Creates a animation that can be used to alter a double value from 1.0 to 0.0 over one second.

Sounds Great – I Can Still See the Control Though?

Of course, we have not actually bound it to a property of a control yet! All we created was the logic of the “fade” (reduction in opacity over time).

   1: Storyboard.SetTargetName(animation, myCtl.Name);
   2: Storyboard.SetTargetProperty(animation, new PropertyPath(OpacityProperty));
   3: storyboard.Children.Add(animation);

We have a few things to note here:

  • There are two calls to static Storyboard methods.
  • There is then a single call to an instance method.
  • We have introduced something called a PropertyPath.

The PropertyPath is basically a roadmap to a property value. What we are doing in the code is pretty simple:

  1. Setting the high-level target for the animation to the Control by using it’s Name property.
  2. Setting the actual property for manipulation by creating a PropertyPath.
  3. Adding the animation to our main storyboard instance.

MAKE IT SO!

Right, we have now actually create all that is required to fade out a control. All we need to do is fire off the process with this (rather large) code snippet:

   1: storyboard.Begin();

The storyboard will then do it’s job and execute the animations it contains.

One thing I really like about the above code (not sure if you noticed) is that there are no direct references required. Animations are very generic so we could easily wrap up the “FadeOut” code into something like:

   1: public static void FadeOutControl(FrameworkElement control)
   2: {
   3:     // Fades Out a Control Over 1s.
   4:     var storyboard = new Storyboard();   
   5:     var animation = new DoubleAnimation()
   6:     {
   7:         From = 1.0,
   8:         To = 0.0,
   9:         Duration = new TimeSpan(0, 0, 1)
  10:     };
  11:     
  12:     Storyboard.SetTargetName(animation, control.Name);
  13:     Storyboard.SetTargetProperty(animation, new PropertyPath(OpacityProperty));
  14:     storyboard.Children.Add(animation);
  15:     storyboard.Begin();
  16: }

We can then simply fade out control using:

   1: FadeOutControl(myBtn);
   2: FadeOutControl(somePanel);
   3: // etc...

Excellent!

Animations and Multithreading

After completing the above I thought I would see what it would be like to have a control fade out and then actually be removed (so controls reflow and reposition to fill its space).

This then ran me in to a problem which I think is important so I will quickly cover it here:

Animations run in a separate thread to the main application.

And rightly so, if they did not, your UI would freeze up while a control fades out. Sexy UI’s are great provided they do not come at a productivity cost. Fade outs are cool but would quickly piss me off if I lost minutes every day waiting for a control to fade out slowly.

In wanting to complete my goal I added code like:

   1: FadeOutControl(myCtl);
   2: myPanel.Children.Remove(myCtl);

Which seems fine at a quick glance, but now think about the animation running in a separate thread. When this is executed, the animation will begin, but the main thread of execution will continue, removing the control from the Window. So, the nice animation will start but then the control quickly disappears.

This can be solved by hooking up to the Storyboard.Completed event like so:

   1: storyboard.Completed += 
   2:  new EventHandler(storyboard_Completed);

You can then remove the control from the Window in the event handler:

   1: void storyboard_Completed(object sender, EventArgs e)
   2: {
   3:   FadeToGonePanel.Children.Remove(myCtl);
   4: }

Jobs-a-goodun!

In Summation

I had a lot of fun with this tech day, was nice to be working with something fresh while being rebellious and doing something that is to do with my next exam :) I personally have the feeling that the WPF team took a lot of lessons learned from WinForms and then brought some great stuff to the table to bring about a real nice platform to work on.

My only (at time of writing!) peeve is with the designer, why-oh-why is there no Events list in the properties Window?! If I just want to browse the Events I need to either go to MSDN or scan through a massive Intellisense list! Silly!

I hope those of you getting started with WPF in your own Tech Days/study got something of use out of this post!

Oh, I almost forgot. You may be thinking “not a lot of code for a whole day, Rob” – you would be correct.. I actually started playing with a new WPF-based project – more may follow soon :)

Links:

1 comment:

  1. Sounds like you made a good start. Remember that the learning curve is large, but there are plenty of resources out there to help you. Make sure you get the basics (dependency properties, attached properties, markup extensions, the way the layout containers work) before rushing into the more fun stuff (animations!)

    I haven't read the MacDonald WPF book, but I've been disappointed at how often the Silverlight one has lead me down a blind alley or told me untruths because it was written before RTM and things appear to have changed :-(

    You might want to have a look at the slide decks and code samples from the excellent WPF course given at Heathrow last weekend. Available for download here: http://karlshifflett.wordpress.com/2009/05/20/thank-you-london-uk-and-european-developers/

    Also look at the stuff from InnerWorkings. Microsoft gave a ton of these away for free a few months back (first come, first served) and I've only just started looking at the WPF and Silverlight course (wow! This was a deal - it gave you ALL the WPF and Silverlight drills) and I'm totally sold on their method of training - which is to teach from within the Visual Studio IDE. The training material effectively comes in the form of web links (MSDN and ScottGu blog entries!) and a set of coding challenges which they have an automated system to grade you on.

    I've been so impressed with what I've seen so far I'm going to buy their MVC and AJAX modules before InnerWorkings change the format and go for bigger combined packages that cost more instead of allowing purchase of individual drills (it all changes in June apparently). http://www.innerworkings.com/catalog

    ReplyDelete