Scott Hanselman

Learning WPF with BabySmash - Keeping it DRY with XAML Styles

June 12, '08 Comments [8] 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!

BabySmash is full of shapes and stuff and those shapes a similar look. They have the same stroke (outline color) and stroke thickness (outline width) for example.

There are a lot of places in my code where I not only repeat the assignment of these styles.

Shape = Shape = new Path()
{
Data = MakeCharacterGeometry(nameToDisplay),
Fill = fill,
Stroke = Brushes.Black,
StrokeThickness = 5,
Height = 400
};

This kind of object initializer code is copy/pasted all over. It's not DRY. (Don't Repeat Yourself) First I started looking for object oriented ways to solve this issue. I figured I'd put in some base class that would do the work, or make a Utility (gasp!) class to do this tedium.

Ideally I wanted this stuff in one place (hence DRY) and I wanted to be able to apply it to my shapes. Later, I realized I also wanted to occasionally apply these properties to some shapes and not others. At that point, my object-oriented idea started to fall down.

I felt (and still feel) like most of the Shape stuff for BabySmash belongs in the XAML markup, and folks on CodePlex agreed. Sherwin Rice kindly pointed me towards Styles.

He suggested storing these properties in named bundles of styles in the XAML. This gives me the arbitrary flexibility I needed.

<Style x:Key="circle" TargetType="Ellipse">
<Setter Property="Width" Value="400"/>
<Setter Property="Height" Value="400"/>
<Setter Property="StrokeThickness" Value="5"/>
<Setter Property="Stroke" Value="Black"/>
</Style>

However, as I started moving most of my shape's details over into XAML, things started repeating again. I was trading one kind of "markup" (the C# kind) for another. Poop. Well, turns out you can base styles on styles, so I was able to keep it DRY again.

<Style x:Key="BabySmashBaseStyle" TargetType="Shape">
<Setter Property="StrokeThickness" Value="5"/>
<Setter Property="Stroke" Value="Black"/>
</Style>

<Style x:Key="trapezoid" TargetType="Path"
BasedOn="{StaticResource BabySmashBaseStyle}">
<Setter Property="Data" Value="F1 M 257.147,126.953L 543.657,126.953L 640.333,448.287L 160.333,448.287L 257.147,126.953 Z"/>
</Style>

<Style x:Key="star" TargetType="BabySmash:Star"
BasedOn="{StaticResource BabySmashBaseStyle}">
<Setter Property="NumberOfPoints" Value="5"/>
<Setter Property="Width" Value="400"/>
<Setter Property="Height" Value="400"/>
</Style>

These all live in <Application.Resources> and I can apply them as I like:

Shape s = new Ellipse();
s.Style = Application.Current.Resources[Name] as Style;

I appear to have just touched the surface of Styles, but I'm continuing to dig in. WPF is starting to make sense to me. Just a smidge.

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 twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Learning WPF with BabySmash - Speech Synthesis

June 11, '08 Comments [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 twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

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

June 9, '08 Comments [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 twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Hanselminutes Podcast 116 - Distributed Caching with Microsoft's Velocity

June 8, '08 Comments [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 twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Learning WPF with BabySmash - Configuration with DataBinding

June 3, '08 Comments [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 twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

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