WPF (Windows Presentation Foundation) is confusing, to me at least. It's one of those things that is so completely different from how things were done in the past that it's not only hard to just pick up, but it's hard to tell what's the right way to do things. Often, there's a half-dozen ways to do something but no way to know which is the right. By right, I mean, most robust, most reliable, most future-proof, most supported, most compatible. It's powerful, but the power isn't immediately discoverable.
A Little Personal History
I'm a Win32 guy from way back. To be honest, I'm a Win16 guy. I cut my teeth when Hello World was 92 lines of C code on Windows 3.0, not counting the dialog resources. I worked on internationalizing applications in '95 when thunking (translating between 16 and 32-bit) was all the rage. We had an educational security application that needed to run in DOS, Windows 3.1 and Windows 95, and they wanted to re-use as much code as possible, so I #ifdef'd my way to glory.
I not only understand Win32, but I understand it's historical context and many of the design decisions behind it. Certainly not as much as Raymond Chen, but I devoured Petzold's legendary book, as did we all, and when MFC came out, I understand exactly what it abstracted and what it didn't.
WinForms is a managed layer over Win32 Windowing, and not only does Win32 sneak out occasionally in your code, but so does (of course) many other Win32 APIs.
OK, fast forward to this past weekend. My 2.5 year old and 6 month olds are constantly smashing on the various computers around the house. There's two Macs and four PCs in various locations. I've got a great free toddler-specific application called AlphaBaby installed on the Mac that makes sounds, shows letters and numbers while ostensibly protecting the machine (the software at least) from the kids. When I see my kids playing with it with such enthusiasm, I always think of the Incredible Hulk when he says "Hulk! Smash!" so this is "Baby! Smash!"
I always wanted a Windows version of AlphaBaby, and some Googling didn't find me what I wanted, although I knew it must be out there. (Of course, now many of you have shared with me your favorite toddler programs. Someone should re-write KidPix for the PC. THAT was a great application.)
My wife was watching some horrific movie on Lifetime (seriously, Patrick Swayze was in it and it wasn't Dirty Dancing) so I figured I'd spend a few hours and bang out a little application for the boys. Then I realized that this was a fine opportunity to learn WPF. I understand the general ideas behind WPF and Chris Sells has explained some aspects of it to me. Suffice it to say, WPF is a complete re-imagining of how Windows Client Developer should work - hence it's relative inaccessibility to folks like me who are deeply routed in PeekMessage. ;)
File|New WPF Application and some poking around got me some progress, but I was really trying to make the code clean. I had two goals: make it work and do it well.
Then, it hit me. "Make it work' usually is the most important goal, so I accepted that I don't know WPF and just made it work using techniques I already knew. About 6 hours later, a lot of hacking, some sample wav files of kids laughing, a quick web search for free "Web 2.0 templates," register a domain, upload, and I had http://www.babysmash.com and a workable ClickOnce WPF application that's about 60% of where I want it.
Here's some of the "features." Note that some of these are technical features. Some are kind of obscure, but as a whole, I think these represent a good representative mix of the kinds of things that Windows Client Programmers might find themselves trying to figure out. I think it's a decent sample because it's complex without being oppressively so, it's easy to get one's head around the requirements, it has a lot of possibilities, and it touches all over the platform (BCL, WinForms, Win32, WPF).
- Silly Graphics
- Options Dialog and Settings saved in Isolated Storage
- Keyboard Hooks - Disabled Windows Key, ALT-TAB, Ctrl-Esc
- ClickOnce (is it viable? sure makes auto-updating easy)
- Kiosk Mode (Full Screen)
- Multi-monitor Support (almost)
- Multimedia, Playing audio files
- Text to Speech
Future Features Ideas
- 3D? Animations? Gradients?
- Code Signing Certificate to avoid warning
- Mouse drag/painting support
- Better/more Multimonitor options
- Simultaneously add shapes on n monitors
- Optional install as Screensaver?
- When .NET 3.5 is released, support the smaller .NET 3.5 Client Install
- Add custom WAV files (my voice?) for letters/shapes, rather than TTS
- Maybe let parents record their own voices
- Does it work on non-English Keyboards/Systems?
- Does it make sense/is it useful with non-Latin languages? Chinese, Arabic?
- Logging/phone-home when exceptions happen
- About Box (woohoo!)
Did I mention the code sucks deeply?
it also has some "hidden features." By hidden features, I mean, the code sucks. Deeply. Which is kind of the whole point. After I abandoned my "do it well" goal and focused on "make it work," I made ridiculous progress. Of course, I'm not sure the fate of my immortal soul after writing this code, but calling back to last week's post on how none of us really knows what we're doing, why not use this as a learning experience?
I decided to not only hack this together, but also to consciously not do a refactoring pass. This is the code exactly and horribly as written the first time, comments, dead code, duplicated blocks, and random copy-pastes from
the bathroom wall of code CodeProject and MSDN. Don't judge me too harshly because you KNOW you've written this kind of crap yourself, Dear Reader. The kind of code that makes you need a shower later, but it works and it solved the problem.
Problems as I see them
The code is on CodePlex, and this is my own stream-of-consciousness review of the code and functionality.
- No real design
- No separations of concerns between objects
- Utils class (isn't there always a Utils class?) is schizophrenic
- Unclear to me how to convert the low level Win32 calls to WPF-eese.
- I think there's easier way to have "stroked" (outined) characters.
- Not modular - no way to add new shapes and have sounds associated with them.
- ConfigurationManager/IsolatedStorage - feels like I shouldn't have had to do that.
- Audio - used Win32 APIs. Do the WPF managed API have the same perf?
- If you slam on the keyboard too fast, like wipe your hand over it, it can crash in un-managed code. Is this the global keyboard hook gone wrong or something more sinister?
- Need to capture Alt-Space
- Relationship between Main Window, Options, and configuration details is shaky at best. There should be a pattern for chunks of config state that might change during the process.
- The random letter/shape positioning has some hard-coded fudge factors that feel gross.
- No tests - how would you test something like this?
- No thought to threading issues (do I have any?)
- Multimonitor is cheesy, and the Windows on other monitors don't update unless they have focus. How can dispatch (Replay/Tivo?) keystrokes/events too n windows at once so they all update?
- Multimonitor - I'd like to detect on window being closed, then close them all.
- Splash screen?
- No tracing, debugging, exception handling, logging.
- I keep calling into System.Windows.Forms. Are there known WPF limitations on keyboard handling?
- If you hit enough keys fast enough, you have to wait for the "queue" to empty. Can I solve that and drop keys if something fills to fast? My own key queue?
- Not localized - magic strings.
- I'm not really using XAML. Is there a better, more declarative way to do some of this?
I'd like to try two things here. First, what I'd like to do is have a group code review. Leave your thoughts in the CodePlex Issue Tracker and code in <pre> tags. Be sure to read them first so you aren't duplicating effort. Of course, even better, feel free to blog your improvements and diffs or email me diff files (firstname at lastname.com).
Second, I'm going to hunt down WPF programmers. Not just Microsoft Employees, although I'll go find the WPF team and ask them, but I want to talk people who have succeeded and failed at WPF applications and get their ideas on refactorings, structure, design, and correctness. Help me (us) understand how it works and if it's better.
Then (hopefully) over a series of blog posts and maybe a podcast or two, we'll have two applications, before and after, that we can look at. One will be my initial hacked-together-but-functional application, and one will be a completely refactored, group-vetted, expert-reviewed sample that represents (theoretically) a well-written WPF application. The app will implement all the features above and more, and each "feature" will be a little best-practice that someone can use in their app. This way we'll have not just a random sample - with credit to Rob Conery's MVC Storefront application and his transparent design process - but two, before and after, and at some point I'm hoping my brain will break out of my Win32 thinking and I'll better understand WPF idioms.
I will tag the posts on my blog in the BabySmash category. I'll also be trying some other mostly unrelated experiments, as I'll use Git for local, offline source control, but I'll be pushing my major commits from Git to TFS (Team Foundation Server) at CodePlex as well as using TFS for issue tracking.
Have at it, the source is up on CodePlex, you can download a ZIP.