Scott Hanselman

Learning WPF with BabySmash - Speech Synthesis

June 11, 2008 Comment on this post [16] Posted in BabySmash | Windows Client | WPF
Sponsored By

image NOTE: If you haven't read the first post in this series, I would encourage you do to that first, or check out the BabySmash category. Also check out http://windowsclient.net/ for more developer info on WPF.

BACKGROUND: This is one of a series of posts on learning WPF. I wrote an application for my 2 year old using WPF, but as I'm a Win32-minded programmer, my working app is full of Win32-isms. It's not a good example of a WPF application even though it uses the technology. I'm calling on community (that's you, Dear Reader) to blog about your solutions to different (horrible) selections of my code. You can get the code http://www.codeplex.com/babysmash. Post your solutions on your blog, in the comments, or in the Issue Tracker and we'll all learn WPF together. Pick a single line, a section, or subsystem or the whole app!

There's been a flood of interesting content coming in around BabySmash, including WPF Expert Karl Shifflett who found the original code so nasty that he rewrote it from scratch! I'll do a whole post analyzing his code and how I can refactor my own.

Before that, I wanted to share one little tidbit that isn't explicit to WPF but rather to the .NET Framework 3.0. Most folks think of .NET 3.0 (myself included) as being WPF (Windows Presentation Foundation), WCF (Windows Communication Foundation) and WF (Windows Workflow). However, there's a bunch of miscellaneous goodness in in .NET 3.0 that I always forget about.

In BabySmash I wanted to have the letters and shapes spoken when they appear on the screen, and lazily, rather than recording the sounds, I thought to myself, "self, why not text to speech?"

I know there's TTS (Text to Speech) stuff in Windows as COM Objects, so I went into Add Reference in Visual Studio, found the component and added it. A .NET wrapper got created that makes the COM object look like .NET and off I went.

Turns out that .NET 3.0 includes System.Speech, an official component that does all this for me. I yanked out the COM stuff and put System.Speech in, and got the added benefit of a Cancel method to stop any current speech. This is particularly helpful for BabySmash because when kids hit the keyboard fast the speech system slowed down as words to say queued up. It couldn't say the words as fast as they were said.

objSpeech = new SpeechSynthesizer();
objSpeech.SpeakAsyncCancelAll();
objSpeech.Rate = -1;
objSpeech.Volume = 100;
objSpeech.SpeakAsync(s);

//REMOVED COM STUFF
//objSpeech.WaitUntilDone(Timeout.Infinite);
//objSpeech.Speak(s, SpeechVoiceSpeakFlags.SVSFlagsAsync);

Additionally, by using the COM Component, I got into trouble where Vista has version 5.3 and XP had version 5.0. This made it difficult for developers using XP to build the program. This goes away when using the .NET assembly.

Hopefully I'll get to do more "refactoring via subtraction" as I dig deeper into .NET 3.x.

Also, thanks to Gina at LifeHacker for her post on BabySmash! The comments are pretty good, but the best interaction was these two comments:

"Wow, 15 years of progress with Windows and this is all we have to show for it? No wonder Windows is teh suckage."

Very supportive! ;) And a response from another random commenter:

"Wow, I've seen people take cheap shots at Windows, but this is the winner! A dad who writes a program for his son suddenly contributing to windows being 'teh suckage' ..."

Bam. Baby! Smash!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

The Weekly Source Code 28 - iPhone with ASP.NET MVC Edition

June 10, 2008 Comment on this post [12] Posted in ASP.NET | ASP.NET MVC | Source Code
Sponsored By
image I've been getting more and more interested in how folks extend their applications using plugins and things. In my new ongoing quest to read source code to be a better developer, Dear Reader, I present to you twenty-eighth in a infinite number of posts of "The Weekly Source Code."

The iPhone 2.0 was announced today and even though I am trying to come up with a better solution to show it off than Northwind, I was moved when I saw Aaron's "Rock the iPhone with ASP.NET MVC" post. I've had this same post in my "to blog" queue for at least two months, but now he's done it himself and beat us all to it. Kudos to Aaron.

I'm primarily an ASP.NET developer and I browse on mobile devices all the time, and have been doing so for years. I made DasBlog and this website sniff and apply a mobile template almost two years ago. If you visit hanselman.com on a BlackBerry or Windows Mobile or a number of other tiny devices, you'll get the same site with a mobile skin. I'll need to do something for the iPhone as well.

Al Pascual added iPhone "support" to Community Server late last year by redirecting iPhone users to his RSS Feed, giving those users a cleaner experience than they would have gotten ordinarily.

Steve Orr has a good article on iPhone Development pointing out a number of important details like the viewport meta tags that give the iPhone clues on how to scale the page as well as his Advanced iPhone Development article with a smidge more detail (not really advanced).

I was wondering how easy it'd be to make an iPhone application with ASP.NET MVC, and it turns out it's pretty easy with a combination of things. First, the IUI project at Google Code is a series of Javascript and PNG assets to make a basic iPhone apps. It's really quite elegant, what the iui team has done, based on work from Joe Hewitt. Joe has a fine introductory blog post that explains some of the concepts.

You can use Safari for to simulate an iPhone, or Firefox with the User Agent Switcher and some customization.

For example, here's the source for the page pictured above. Notice the one CSS and one JS file that are included.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>iUI Prefs Demo</title>
<meta name="viewport" content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>
<style type="text/css" media="screen">@import "../iui/iui.css";</style>
<script type="application/x-javascript" src="../iui/iui.js"></script>
</head>

<body>
<div class="toolbar">
<h1 id="pageTitle"></h1>
<a id="backButton" class="button" href="#"></a>
</div>

<form id="settings" title="Settings" class="panel" selected="true">
<h2>Playback</h2>
<fieldset>
<div class="row">
<label>Repeat</label>
<div class="toggle" onclick=""><span class="thumb"></span><span class="toggleOn">ON</span><span class="toggleOff">OFF</span></div>
</div>
<div class="row">
<label>Shuffle</label>
<div class="toggle" onclick="" toggled="true"><span class="thumb"></span><span class="toggleOn">ON</span><span class="toggleOff">OFF</span></div>
</div>
</fieldset>

<h2>User</h2>
<fieldset>
<div class="row">
<label>Name</label>
<input type="text" name="userName" value="johnappleseed"/>
</div>
<div class="row">
<label>Password</label>
<input type="password" name="password" value="delicious"/>
</div>
<div class="row">
<label>Confirm</label>
<input type="password" name="password" value="delicious"/>
</div>
</fieldset>
</form>
</body>
</html>

Notice the little toggle switches? They are in a div with class="toggle" and a click event handler gets hooked up to those in Javascript:

addEventListener("click", function(event)
{
var div = findParent(event.target, "div");
if (div && hasClass(div, "toggle"))
{
div.setAttribute("toggled", div.getAttribute("toggled") != "true");
event.preventDefault();
}
}, true);

Based on the toggle state, the switch renders as appropriate:

.toggle {
border: 1px solid #888888;
-webkit-border-radius: 6px;
background: #FFFFFF url(toggle.png) repeat-x;
font-size: 19px;
font-weight: bold;
line-height: 30px;
}

.toggle[toggled="true"] {
border: 1px solid #143fae;
background: #194fdb url(toggleOn.png) repeat-x;
}

The iPhone HTML page above has multiple forms, explicit control over divs - all things that MVC is better suited for than WebForms. Aaron explains in his blog post how he just brings IUI into his content folder, but the trick was that IUI expects HTML fragments to be returned from Ajax calls, not JSON.

Aaron's Views are then Partial Views intended for consumption from built-in Ajax calls by IUI, so the product's list page looks just like this. Note the markup is all alone.

<ul id="products" title="Products">
<% foreach (Product p in ViewData.Model) { %>
<li><%= Html.ActionLink(p.ProductName, "Index", "Products", new { id = p.ProductID }) %></li>
<% } %>
</ul>

Very cool and very easy.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Hanselminutes Podcast 116 - Distributed Caching with Microsoft's Velocity

June 08, 2008 Comment on this post [6] Posted in ASP.NET | ASP.NET Dynamic Data | ASP.NET MVC | Podcast | Programming
Sponsored By

image My one-hundred-and-sixteenth podcast is up. One of the big announcements coming out of TechEd this week was Microsoft' "Velocity" product, a distributed in-memory cache. I sit down with two of the architects of the project and try to get the real story. Check out Dare's excellent analysis of Velocity as well as the MSDN Page for Velocity where you can download the CTP1 and try it on your sites today.

Also check out memcached (Win32 port) and the unofficial memcached .net library as well as the ASP.NET providers for memcached as well as the providers on CodePlex. Hopefully we'll see some detailed benchmarks soon comparing Velocity and memcached. I'll also personally be looking at the Velocity APIs giving my feedback to the team for future CTPs.

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

If you have trouble downloading, or your download is slow, do try the torrent with µtorrent or another BitTorrent Downloader.

Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.

Telerik is our sponsor for this show.

Telerik's new stuff is pretty sweet, check out the ONLINE DEMO of their new ASP.NET AJAX suite. RadGrid handles sorting, filtering, and paging of hundreds of thousands of records in milliseconds, and the RadEditor loads up to 4 times faster and the navigation controls now support binding to web services on the client.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)

Enjoy. Who knows what'll happen in the next show?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Learning WPF with BabySmash - Configuration with DataBinding

June 03, 2008 Comment on this post [19] Posted in BabySmash | Windows Client | WPF
Sponsored By

NOTE: If you haven't read the first post in this series, I would encourage you do to that first, or check out the BabySmash category. Also check out http://windowsclient.net/ for more developer info on WPF.

BACKGROUND: This is one of a series of posts on learning WPF. I wrote an application for my 2 year old using WPF, but as I'm a Win32-minded programmer, my working app is full of Win32-isms. It's not a good example of a WPF application even though it uses the technology. I'm calling on community (that's you, Dear Reader) to blog about your solutions to different (horrible) selections of my code. You can get the code http://www.codeplex.com/babysmash. Post your solutions on your blog, in the comments, or in the Issue Tracker and we'll all learn WPF together. Pick a single line, a section, or subsystem or the whole app!

The first blogger to take me up on this was Jason Kemp, and his resulting post called "Redoing the Options Dialog in BabySmash" is a model for how to expand the knowledge of the community.

Jason said "I’ve taken a look at the code and, boy, it needs work." Heh. I warned you. He decided to tackle two related areas, the Options Dialog's XAML layout and the ConfigurationManager settings subsystem. We'll tackle the underlying Configuration for this post.

WPF Configuration Done Wrong/Poorly

BabySmash stores configuration info, and I wanted to use IsolatedStorage, where the system picks a file- and path-name and you write to it. I search the web and IsolatedStorage made easy was an attractive first hit. I copy/pasted the code and hooked it up through the app and it worked great.

It was pretty cheesy though. First, it was a big custom class written in 2004 that did a bunch of XML work to save your settings. But, it was hidden in a class and worked, so I didn't sweat it.

The part that really smelled bad was the juggling back and forth between three places. The options were in a file, they were in instance variables, and they were in the controls in the Options dialog. This is how it was down in Win32. It was straightforward moving the values around but tedious.

The options dialog loaded and saved the settings using the class...

private void OK_Click(object sender, RoutedEventArgs e)
{
config.Write("ClearAfter", txtClearAfter.Text);
config.Write("ForceUppercase", chkForceUppercase.IsChecked.ToString());
config.Write("Sounds", cmbSound.Text);
config.Persist();
this.Close();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
config = IsolatedStorage.ConfigurationManager.GetConfigurationManager("BabySmash");
txtClearAfter.Text = config.ReadInteger("ClearAfter", 20).ToString();
chkForceUppercase.IsChecked = config.ReadBoolean("ForceUppercase", true);
cmbSound.Text = config.Read("Sounds", "Laughter");
}

Then later in the main Window I'd load it into local variables:

private void LoadConfigSettings()
{
//TODO: This is duplicated elsewhere
config = IsolatedStorage.ConfigurationManager.GetConfigurationManager("BabySmash");
clearAfter = config.ReadInteger("ClearAfter", 20);
forceUppercase = config.ReadBoolean("ForceUppercase", true);
sounds = (Sounds)Enum.Parse(typeof(Sounds), config.Read("Sounds", "Laughter"));
}

This is a common (anti) pattern, but it's easy. Jason made it cleaner:

WPF Configuration Done Better

You should check out Jason's explanation for the deep details. First, he had me use the Settings Tab of the Project Properties. It works with ClickOnce apps, which was why I'd over-engineered the first solution. I assumed it wouldn't work, rather than trying it first. Doh!

image

This designer will generated  Settings.Designer.cs file that is a basic class that fronts your storage. It's from here that we'll get/set our configuration settings and it'll appear in the BabySmash.Properties namespace.

Previously, my Options Dialog was full of what I call "left-hand/right-hand" code. This is the kind of code you go a=b and then b=a, moving data back and forth between "hands." My original code is monkey code. Boring, dull, repetitive and error-prone.

How does he make it cleaner? We use Data Binding in the XAML for our dialog. I've bolded the interesting bits.

<Window x:Class="BabySmash.Options"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BabySmash.Properties"
xmlns:l="clr-namespace:BabySmash"
Title="Baby Smash! - Options" Height="188" Width="268" ShowInTaskbar="False" Topmost="True"
WindowStartupLocation="CenterScreen" WindowStyle="ThreeDBorderWindow" ResizeMode="NoResize">
<Window.Resources>
<local:Settings x:Key="settings" />
</Window.Resources>
<Grid DataContext="{StaticResource settings}">
...Snip...
<Label Height="23" Margin="10,20,0,0" Grid.ColumnSpan="2">Clear after x Shape</Label>
<TextBox Text="{Binding Path=Default.ClearAfter}"
Height="23" Grid.Column="1" Margin="15,20,7,0"/>
<Label Height="23" Grid.Row="1" Margin="10">Sounds</Label>
<ComboBox
SelectedValue="{Binding Path=Default.Sounds}"
SelectedValuePath="Content"
Grid.Column="1" Grid.Row="1" Height="23" Margin="15,0,7,0">
<ComboBoxItem>None</ComboBoxItem>
<ComboBoxItem>Laughter</ComboBoxItem>
<ComboBoxItem>Speech</ComboBoxItem>
</ComboBox>
<CheckBox Grid.Row="2" Grid.Column="1" Margin="15,0,0,0"
IsChecked="{Binding Path=Default.ForceUppercase,Mode=TwoWay}" >
Force Uppercase
</CheckBox>
<StackPanel Orientation="Horizontal" Grid.Row="3" Grid.ColumnSpan="2" HorizontalAlignment="Right">
<Button Name="okButton" IsDefault="True" Margin="0,7,10,7" Padding="30,0,30,0" Click="OK_Click" >OK</Button>
<Button IsCancel="True" Margin="5,7,7,7" Padding="15,0,15,0" Click="Cancel_Click" >Cancel</Button>
</StackPanel>
</Grid>
</Window>

This chunk of XAML took me a while to get, then it clicked. First, you have to associate an XML namespace with a CLR namepace. That's this line that associates "local:" with the Properties namespace from the Settings class that was created earlier by the properties dialog.

xmlns:local="clr-namespace:BabySmash.Properties"

Next, we tell the Grid that's laying out the controls that we have some static data we'll be using and we call it "settings" because that's the class name.

<Grid DataContext="{StaticResource settings}">

Ok, make sense so far. Remember the dialog looks like this:

image

Removing the layout code, the data binding for these three controls looks like this. The Settings object has a Default Property with a ClearAfter property, and there's also Sounds and ForceUppercase properties.

<TextBox Text="{Binding Path=Default.ClearAfter}"/>
<ComboBox SelectedValue="{Binding Path=Default.Sounds}" SelectedValuePath="Content">
    <ComboBoxItem>None</ComboBoxItem>
    <ComboBoxItem>Laughter</ComboBoxItem>
    <ComboBoxItem>Speech</ComboBoxItem>
</ComboBox>
<CheckBox IsChecked="{Binding Path=Default.ForceUppercase,Mode=TwoWay}">Force Uppercase</CheckBox>

The one that Jason didn't do that flummoxed me for a bit was the ComboBox. The settings were being saved, but the dialog loaded, the loaded value wasn't selected in the ComboBox. I assume that the SelectedValue would be the text in each item. Then I realized that as Combos can have Content, Tags, and other bits of data hanging on them, the system needed to know the "Path" to a value to use when setting SelectedValue. The text is stored in ComboBoxItem.Content, so I set SelectedValuePath.Content.

For an alternative view this is how it would be done if I was using Tags and storing these numbers rather than strings in my config:

<ComboBox SelectedValue="{Binding Path=Default.Sounds}" SelectedValuePath="Tag">
    <ComboBoxItem Tag="0">None</ComboBoxItem>
    <ComboBoxItem Tag="1">Laughter</ComboBoxItem>
    <ComboBoxItem Tag="2">Speech</ComboBoxItem>
</ComboBox>

At this point, ALL the code in the Options.xaml.cs goes away except a call to Save. There's no left-hand/right-hand code, just markup that is associating the controls with the data they should be bound to. It's a subtractive refactoring and the whole thing gets cleaner, simpler, and DRYer (Don't Repeat Yourself). All the local variables in the main window also go away, as I can just access the property for Settings.Default.ForceUppercase or whatever and it's all centralized.

private void OK_Click(object sender, RoutedEventArgs e)
{
Properties.Settings.Default.Save();
this.Close();
}

Coding via subtraction/deletion is my favorite kind of coding. Here's how Jason explained the whole process:

The Windows.Resources element adds a Settings object to the window’s ResourceDictionary. All resource dictionary items must have a key to refer to. We set the DataContext on the Grid to the settings object declared in the resources. It’s a StaticResource which means that WPF loads it once and stops listening to updates to that resource. The DataContext is essentially associating the Settings object with the Grid. The  Binding on the TextBox’s Text property is BindingExpression whose Path points to the Default.ClearAfter property of the Settings object. That’s a little advanced, but hey, it’s "real world." And that’s all you have to do to get the settings to show up in the right controls. Notice there is no procedural code.

Related Links

Technorati Tags: ,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Introducing BabySmash - A WPF Experiment

June 02, 2008 Comment on this post [39] Posted in BabySmash | Learning .NET | Windows Client | WPF
Sponsored By

screenshot1 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.

BabySmash

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.)

Learning WPF

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.

Features

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).

Current Features

  • 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
  • Localized?
    • 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?

The Experiment

image 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.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.