CoasterBuzz Feed: A Silverlight 3 story
A few weeks ago, I decided to jump in and do a "science project" with Silverlight 3. My prior experiences with Silverlight involved a simple DeepZoom viewer that happened to use a Web service as its tile source, and a file uploader that cut up multiple files and sent them to the server. The latter is being used as a part of a plugin to the forum on CoasterBuzz, and it has served me very well. With the out-of-browser option on Silverlight 3, I figured it was a perfect chance to give my audience another reason to stalk the site, using a small "feed" app that they could run on the desktop.
I had a few design goals in mind up front. I wanted to iterate quickly and just ship something, to club members first. (CoasterBuzz Club is the paid, ad-free experience for the hardcore roller coaster nerds who form the core of the audience.) I was less worried about coding things "right" than I was making something function. I had already learned my proper lessons around dependency properties and data binding and layout and all of the things that made Silverlight cool, so I'd worry about polish once it worked. That's the benefit of a small project. Secondary goals included the OOB deployment and some trivial use of the new 3D projection just because.
As with most projects, and especially a relatively small one like this, I started by just sketching and scribbling. UI, architecture, data, whatever... just get it out of my head and organized.
The nice thing about paper is that it's pretty cheap, so whatever you decide isn't worth your time, you scribble out or throw away. I started with a simple sketch of what I thought the UI should look like. I wanted to syndicate new forum topics and posts, news items and additions to the photo database. Each one would simply appear shortly after the event on the client, and clicking on any particular item would take you there on the site.
At this point, I got a little into the weeds with data structures and implementation details, even though I wasn't ready for that. Ironically, my motivation for feeling out UI first is so that I don't end up placing constraints on myself that are founded in implementation details. I stepped back and got to thinking about architecture instead. This is all pretty simple stuff here. No need to over-think it.
Right away, I saw some potential for reuse, and at that point already started to toss away the just-get-it-working mentality. I suspect that's the result of having worked on various frameworks before. I've worked on projects where you would record all kinds of data, often not even knowing how or if it would be used, so making this kind of generic, both in terms of the data coming in and the way it was consumed, seemed like a good idea.
I wanted to have some kind of publisher that could accept input from anything, and at that point decided to throw any kind of strongly-typed data out the window. Yes, this eliminated any kind of compile-time assurance that I was recording the right things, but I saw no compelling reason to do anything other than record some kind of event type name, a time stamp, and a spoke table of various properties (user names, post ID's, whatever). If I came up with a compelling reason, I'd worry about it at some refactoring point. So the deployed service simply sent a List<EventItem> collection, and each EventItem had an event type indicated by a string, a time stamp and a Dictionary<string, string> for the spoke data. Easy to serialize, easy to digest, very flexible.
I also wanted the publisher to not worry about what it was recording the data to. So it simply takes a collection of IPublisher objects that do whatever they do. The obvious one is to record to a database, but I figured that I could just as easily publish to Facebook or Twitter. In fact, those will probably be among the next things I do.
I still felt like I wanted to contain a few things in one convenient package though, for reuse. So I did a Visual Studio project that included the publisher, a publisher interface implementation to the database, and a fairly abstract WCF service that could spit out whatever was in the database. I started to consider server-side user options and maybe even specific authenticated user data, but decided to leave that out for now. Just focus on event publishing! The pieces that landed in this project are surrounded by the dotted line in the sketch.
The first attempt was just to get the app pulling and displaying data. That actually went pretty fast. By using a UserControl as the DataTemplate of an ItemsControl, I could have the UserControl act as a factory to load the appropriate UserControl for an item type. Simple switch/case, no problem. The ItemsControl is bound to an ObservableCollection fed by calls to the Web service.
With this basic functionality in place, I felt like the hardest work was done. Next I wanted to have a small control panel that might allow the user to filter the kind of items displayed, but for now figured that version number and status "lights" about connectivity were adequate. And what better place to try projection?
This is where the tooling inside of Visual Studio started to annoy me. I don't need a drag-and-drop design surface, but it's nice to see that when you tweak some XAML, the result is there to see. Rebuilding every time is a pain in the ass I'd rather not endure. I finally gave in and used the Expression Blend 3 RC. Admittedly, that made trying out the animation of the projection transitions a lot easier to see.
The important lesson learned about animation is that it simply holds the last position, and is in fact still "running" when it's done. The various display properties have not actually changed, and if you call Stop() on the animation, you'll see this is the case. You need a little extra plumbing to set objects to their end state, especially if you intend to reverse the animation.
My last step was to engage in the OOB installation. I used Tim Heuer's video on implementing out-of-browser, along with the connectivity and update checking. Wow did Microsoft get this right. My only complaint is that there's no particularly simple way to debug when you're out of the browser like this. It's cumbersome to rebuild, relaunch, see the update, relaunch again, then attach the debugger to the host process. To combat this, I built in a button that would launch the proper app while still in the browser, but only if it was running from localhost.
Once the club members had their chance to hack away at it for awhile, I did find some problems. One of them was that the WCF service, when using the binary binding, has an 8k message limit. The client was choking when it started up and had to receive a large number of feed items.
One of the things that I like to do with new features is a short video that explains stuff. I did this with the "day in pictures" forum that allows you to upload photos into the forum via a Silverlight app (video here) and it went over pretty well. I did it for the feed as well. Notice that YouTube is hosting much higher quality these days?
In any case, the "finished" product is live. I say "finished" because I'm sure I'll add filtering for the various event types at some point. Getting these bits out of the way, I feel much better equipped for more ambitious projects when they arise.