Archive for June, 2009

INotifyPropertyChanged is Obsolete addendum

Wednesday, June 24th, 2009

Please check you mailbox for the latest issue of CoDe Magazine. In it, you will find my article "INotifyPropertyChanged is Obsolete." A few things have changed since I penned those words, and now INotifyPropertyChanged is more obsolete than ever.

The article describes how to use Update Controls to replace WPF data binding. Rather than using the built-in "{Binding}" XAML extension, it tells you to use my "{u:Update}" custom extension. While this custom markup extension is still supported, it is no longer the preferred way to use Update Controls.

Blend-friendly updating
Thanks to some feedback from Paul Stovell, Microsoft MVP for Client Application Development, Update Controls now works with the "{Binding}" XAML extension. This makes it more Blend friendly. But before you give your object to the DataContext, you need to wrap it:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        DataContext = ForView.Wrap(new PersonViewModel(new Person()));
    }
}

The wrapper implements INotifyPropertyChanged for you. It infers dependencies from your code and figures out when to fire PropertyChanged events. To help it with that inference, you need to mark your independent properties.

public class Person
{
    private string _firstName;
    private string _lastName;

    #region Independent properties
    // Generated by Update Controls --------------------------------
    private Independent _indFirstName = new Independent();
    private Independent _indLastName = new Independent();

    public string FirstName
    {
        get { _indFirstName.OnGet(); return _firstName; }
        set { _indFirstName.OnSet(); _firstName = value; }
    }

    public string LastName
    {
        get { _indLastName.OnGet(); return _lastName; }
        set { _indLastName.OnSet(); _lastName = value; }
    }
    // End generated code --------------------------------
    #endregion

    public string FullName
    {
        get
        {
            return FirstName + " " + LastName;
        }
    }
}

The Ctrl+D, G shortcut still works. Select the fields and hit the shortcut to generate the independent properties.

Update Controls can see right through the intermediate view model. There is no bookkeeping code required. Even the dependency of Title upon FirstName and LastName is inferred.

public class PersonViewModel
{
    private Person _person;

    public PersonViewModel(Person person)
    {
        _person = person;
    }

    public string FirstName
    {
        get { return _person.FirstName; }
        set { _person.FirstName = value; }
    }

    public string LastName
    {
        get { return _person.LastName; }
        set { _person.LastName = value; }
    }

    public string FullName
    {
        get { return _person.FullName; }
    }

    public string Title
    {
        get { return "Person - " + FullName; }
    }
}

Silverlight 3
I have also ported Update Controls to Silverlight. It only works with Silverlight 3, so it is in Beta until the official release. However, it is fully operational and works just as well as the WPF version. The "{u:Update}" custom extension is not supported in Silverlight, however. You have to use the new ForView.Wrap() mechanism.

I hope you find Update Controls to be as useful as I have. You can download the source code provided in the article from CoDe Magazine, or try some demos that I've created since then:

  • Commuter - Multi-threaded iTunes synchronization sample.
  • QuickWriter - Data binding through linq queries. Uses the {u:Update} extension rather than the new ForView.Wrap().
  • UpdateControls.XAML.Test - The test app bundled with the source code.
  • Addendum Demo - The code included above.

Also be sure to watch the videos to see it in action.

Wet floor effect batch transform using WPF

Saturday, June 13th, 2009

My wife just took some new photos for her scrapbooking website. I have a coverflow effect on that page, so I needed to apply the wet floor transform to 64 images. Rather than do it all by hand, I wrote some WPF code to do it for me:

public class WetFloor
{
    public static void Render(string source, string target, int targetWidth)
    {
        using (Stream input = File.OpenRead(source))
        {
            // Load the source jpg.
            JpegBitmapDecoder sourceJpg = new JpegBitmapDecoder(input, BitmapCreateOptions.None, BitmapCacheOption.Default);
            if (sourceJpg.Frames.Count != 1)
                throw new Exception(String.Format("I don't know how to handle {0} frames.", sourceJpg.Frames.Count));
            BitmapFrame sourceBitmap = sourceJpg.Frames[0];

            // Create a render target scaled to 400 pixels at 150% the aspect ratio.
            double scale = (double)targetWidth / sourceBitmap.PixelWidth;
            RenderTargetBitmap renderTarget = new RenderTargetBitmap(
                targetWidth, (int)Math.Round(scale * (float)sourceBitmap.PixelHeight * 1.5),
                sourceBitmap.DpiX, sourceBitmap.DpiY,
                PixelFormats.Pbgra32);

            // Paint a rectangle with the source bitmap.
            Rectangle original = new Rectangle()
            {
                Fill = new ImageBrush(sourceBitmap),
                Width = scale * sourceBitmap.Width,
                Height = scale * sourceBitmap.Height
            };

            original.Arrange(new Rect(new Size(scale * sourceBitmap.Width, scale * sourceBitmap.Height)));
            renderTarget.Render(original);

            // Paint a black rectangle.
            Rectangle shade = new Rectangle()
            {
                Fill = new SolidColorBrush(Colors.Black),
                Width = scale * sourceBitmap.Width,
                Height = scale * sourceBitmap.Height
            };

            shade.Arrange(new Rect(new Point(0, scale * sourceBitmap.Height), new Size(scale * sourceBitmap.Width, scale * sourceBitmap.Height)));
            renderTarget.Render(shade);

            // Paint an inverted rectangle with the source bitmap.
            Border reflection = new Border()
            {
                Background = new ImageBrush(sourceBitmap),
                Width = scale * sourceBitmap.Width,
                Height = scale * sourceBitmap.Height,
                RenderTransform = new ScaleTransform(1.0f, -1.0f, scale * sourceBitmap.Width / 2, scale * sourceBitmap.Height / 2),
                OpacityMask = new LinearGradientBrush()
                {
                    StartPoint = new Point(0, 1),
                    EndPoint = new Point(0, 0),
                    GradientStops = new GradientStopCollection(new GradientStop[]
                    {
                        new GradientStop()
                        {
                            Offset = -0.3f,
                            Color = Colors.Black
                        },
                        new GradientStop()
                        {
                            Offset = 0.5f,
                            Color = Colors.Transparent
                        }
                    })
                }
            };

            reflection.Arrange(new Rect(new Point(0, scale * sourceBitmap.Height), new Size(scale * sourceBitmap.Width, scale * sourceBitmap.Height)));
            renderTarget.Render(reflection);

            // Render the final product.
            JpegBitmapEncoder png = new JpegBitmapEncoder();
            png.Frames.Add(BitmapFrame.Create(renderTarget));
            using (Stream fs = File.Create(target))
            {
                png.Save(fs);
            }
        }
    }
}

Enjoy.

Multi-threaded view model using Update Controls

Wednesday, June 10th, 2009

Multithreaded programming doesn't have to be hard. You just need to learn a few patterns.

Here's a common problem. How do you notify the UI thread that something has changed on a background thread? The INotifyPropertyChanged contract requires that the PropertyChanged event be fired on the UI thread. There are ways of marshalling that event from the background to the foreground, but wouldn't it be nice if you didn't have to?

Update Controls takes care of this problem for you. You make sure your code is thread-safe, and Update Controls will make sure that dependents are updated, no matter what thread they are on.

A thread-safe model
For Update Controls to work properly -- and indeed for your entire application to work properly -- you need your data model to be thread safe. That means that anything that can change is protected with a lock. If it can change (and you are using Update Controls), then it should have an Independent sentry. Just be sure to call OnGet and OnSet inside the lock.

public class Playlist
{
    private int _id;
    private string _name;

    public Playlist(int id)
    {
        _id = id;
    }

    public int ID
    {
        get { return _id; }
    }

    #region Independent properties
    // Generated by Update Controls --------------------------------
    private Independent _indName = new Independent();

    public string Name
    {
        get
        {
            lock (this)
            {
                _indName.OnGet();
                return _name;
            }
        }
        set
        {
            lock (this)
            {
                _indName.OnSet();
                _name = value;
            }
        }
    }
    // End generated code --------------------------------
    #endregion
}

When dealing with collections, we need to be a little careful. If you return an IEnumerable that traverses a collection, that IEnumerable will leave the lock. This will cause problems, as another thread can come in and modify the collection while you are traversing it. To be safe, we need to create a copy of the collection within the lock.

public class MusicLibrary
{
    private List<Playlist> _playlists = new List<Playlist>();

    #region Independent properties
    // Generated by Update Controls --------------------------------
    private Independent _indPlaylists = new Independent();

    public Playlist GetPlaylist(int playlistID)
    {
        lock (this)
        {
            // This method changes the list.
            _indPlaylists.OnSet();

            // See if we already have this one.
            Playlist playlist = _playlists.Where(p => p.ID == playlistID).FirstOrDefault();
            if (playlist != null)
                return playlist;

            // If not, create it.
            playlist = new Playlist(playlistID);
            _playlists.Add(playlist);
            return playlist;
        }
    }

    public void DeletePlaylist(Playlist playlist)
    {
        lock (this)
        {
            _indPlaylists.OnSet();
            _playlists.Remove(playlist);
        }
    }

    public IEnumerable<Playlist> Playlists
    {
        get
        {
            lock (this)
            {
                _indPlaylists.OnGet();
                // Return a copy of the list so that it can be accessed in a
                // thread-safe manner.
                return new List<Playlist>(_playlists);
            }
        }
    }
    // End generated code --------------------------------
    #endregion
}

With this, your model is safe to access from multiple threads. So let's create one.

Create a thread to communicate with external systems
The UI thread is for the user. If you use it to communicate with external systems, it might hang. Then the user will have to decide whether to "Continue Waiting" or "Switch To...". Switch to what, a Mac? But seriously, it's better if you spawn a new thread to do all of your communication with external systems.

Commuter communicates with iTunes. So I created a separate thread to synchronize between iTunes and my data model. It wakes up every 10 seconds and refreshes the playlists.

public class ITunesSynchronizationService
{
    private MusicLibrary _musicLibrary;
    private Thread _thread;
    private ManualResetEvent _stop;
    private bool _first = true;

    private string _lastError = string.Empty;
    private Independent _indLastError = new Independent();

    public ITunesSynchronizationService(MusicLibrary musicLibrary)
    {
        _musicLibrary = musicLibrary;
        _thread = new Thread(ThreadProc);
        _thread.Name = "iTunes synchronization service";
        _stop = new ManualResetEvent(false);
    }

    public void Start()
    {
        _thread.Start();
    }

    public void Stop()
    {
        _stop.Set();

        // Give it 30 seconds to shut down.
        _thread.Join(30000);
    }

    public string LastError
    {
        get
        {
            lock (this)
            {
                _indLastError.OnGet();
                return _lastError;
            }
        }

        private set
        {
            lock (this)
            {
                _indLastError.OnSet();
                _lastError = value;
            }
        }
    }

    private void ThreadProc()
    {
        while (ShouldContinue())
        {
            try
            {
                // Connect to iTunes.
                iTunesApp itApp = new iTunesAppClass();

                // List all of the playlists.
                List<Playlist> oldPlaylists = _musicLibrary.Playlists.ToList();
                foreach (IITUserPlaylist itPlaylist in itApp.LibrarySource.Playlists.OfType<IITUserPlaylist>())
                {
                    string name = itPlaylist.Name;
                    Playlist playlist = _musicLibrary.GetPlaylist(itPlaylist.playlistID);
                    if (name != null && playlist.Name != name)
                        playlist.Name = name;
                    oldPlaylists.Remove(playlist);
                }

                // Delete all that are no longer in iTunes.
                foreach (Playlist playlist in oldPlaylists)
                    _musicLibrary.DeletePlaylist(playlist);

                LastError = string.Empty;
            }
            catch (Exception ex)
            {
                LastError = ex.Message;
            }
        }
    }
    private bool ShouldContinue()
    {
        // Don't wait the first time.
        if (_first)
        {
            _first = false;
            return true;
        }

        // Wake up every 10 seconds, or when the thread should stop.
        return !_stop.WaitOne(10000);
    }
}

Notice the LastError property. This is a thread-safe way for the service to communicate its status with the user. This property is bound to a TextBlock on the UI.

The iTunes synchronization service makes changes directly to the data model. Because the data model is thread-safe, the UI thread and the background thread can share this resource. Update Controls will notify the UI thread whenever the background thread changes the data model.

Try it out
Download the code. When you run Commuter, it will launch iTunes. Even as iTunes launches, the Commuter user interface is responsive. If you are fast enough, you can open the Playlist drop-down before it is populated. But even if COM is faster than the mouse, you can try a few experiments.

Select a playlist in Commuter. Then switch to iTunes and rename that playlist. Within 10 seconds you'll see the selected playlist's name change.

Bring iTunes to the front, but leave Commuter visible underneath it. Place your mouse on the drop-down. Press Ctrl+N to create a new playlist, then click the mouse to bring Commuter to the front and open the drop-down. Within 10 seconds, "untitled playlist" will appear in the list.

Multi-threaded programming still requires some care, but Update Controls can at least take care of updating the UI when the background thread changes the data model.

A kindergarten example of historic modeling

Tuesday, June 9th, 2009

Historic Modeling may be a new concept, but you are probably already familiar with a popular example.

A person is a fact. The person exists. Time may pass, and perhaps she'll die, but the fact that she existed will always be true.

Old Lady 1

The same is true of an animal.

Old Lady 2

A person might consume an animal. Usually the reason is sustenance, but we can't be sure. We can document the fact that a person swallowed an animal for a reason.

Old Lady 3

Any of a number of reasons can be given. Maybe we just don't know. Or maybe the person swallowed the animal to catch an animal that they had previously swallowed. The fact that they swallowed an animal in the past is a prerequisite for the reason to swallow another. The prior swallow must come before the reason.

Old Lady 4

From model to instance
To demonstrate the model, let's create some instances of these facts.

  • There was an old lady.
  • There was a fly.
  • There was an old lady who swallowed a fly. I don't know why she swallowed the fly.
  • There was an old lady who swallowed a spider. She swallowed the spider to catch the fly.

Continuing in this manner, we construct a graph of fact instances.

old-lady-5.png

This recitation of facts could go on. Until she dies, of course.

Structure of a WPF view

Friday, June 5th, 2009

Commuter View in Blend The Commuter View Model interface is written. The next step is to hook it to a view.

I like to think of the view as just a convenience. If the user could access properties directly, all they would need is the View Model interface. Thinking about the View Model this way helps me ensure that it is complete and targeted.

So I created the interface as a way of describing what would be on the view. A designer given the interface and a wireframe could build a view. Not having a designer handy, I did it myself. Here's how.

Create a mock
The first step was to create a mock implementation of the View Model interface. In truth, it's not just one interface; it's a set of interrelated interfaces. So I have interrelated mocks. Most of them are simple data access objects: collections of properties with no behavior. Create a read/write property for every property of the interface, even if the interface property is read-only. So for the IPodcastEpisode interface:

public interface IPodcastEpisode
{
    string Name { get; }
    string PodcastName { get; }
    string DurationAsString { get; }
}

I created a PodcastEpisodeSample class:

public class PodcastEpisodeSample : IPodcastEpisode
{
    public string Name { get; set; }
    public string PodcastName { get; set; }
    public string DurationAsString { get; set; }
}

The root class has a little more to it. It has to initialize itself and its children in its constructor. I don't like putting code in a constructor, but you don't have another chance to set your properties.

public class CommuterViewModelSample : ICommuterViewModel
{
    public CommuterViewModelSample()
    {
        // Set up some sample data.
        List<IPlaylist> playlists = new List<IPlaylist>();
        playlists.Add(new PlaylistSample() { Name = "Favorites" });
        PlaylistSample commutePlaylist = new PlaylistSample() { Name = "Commute" };
        playlists.Add(commutePlaylist);
        playlists.Add(new PlaylistSample() { Name = "Energy Songs" });
        Playlists = playlists;
        SelectedPlaylist = commutePlaylist;

        List<IPodcast> podcasts = new List<IPodcast>();
        podcasts.Add(new PodcastViewModel(new Podcast() { Name = ".NET Rocks!", Selected = false, Rank = 0 }));
        podcasts.Add(new PodcastViewModel(new Podcast() { Name = "Hanselminutes", Selected = true, Rank = 3 }));
        podcasts.Add(new PodcastViewModel(new Podcast() { Name = "Deep Fried Bytes", Selected = true, Rank = 4 }));
        podcasts.Add(new PodcastViewModel(new Podcast() { Name = "They Might Be Giants", Selected = true, Rank = 5 }));
        Podcasts = podcasts;

        List<IPodcastEpisode> queue = new List<IPodcastEpisode>();
        queue.Add(new PodcastEpisodeSample() { Name = "5-A", PodcastName = "They Might Be Giants", DurationAsString = "9:30" });
        queue.Add(new PodcastEpisodeSample() { Name = "JavaFX and the Web's Four Virtual Machines", PodcastName = "Hanselminutes", DurationAsString = "40:24" });
        PodcastEpisodeSample deepFriedBytesEpisode = new PodcastEpisodeSample() { Name = "New Ideas for the Web with Thomas Krotkiewski", PodcastName = "Deep Fried Bytes", DurationAsString = "53:15" };
        queue.Add(deepFriedBytesEpisode);
        Queue = queue;
        SelectedEpisode = deepFriedBytesEpisode;

        TotalDurationAsString = "1:43:09";
    }

    public IEnumerable<IPlaylist> Playlists { get; set; }
    public IPlaylist SelectedPlaylist { get; set; }
    public IEnumerable<IPodcast> Podcasts { get; set; }
    public IEnumerable<IPodcastEpisode> Queue { get; set; }
    public IPodcastEpisode SelectedEpisode { get; set; }
    public string TotalDurationAsString { get; set; }
    public ICommand Skip { get; set; }
}

You can hook up the mock in Blend. In the Window's properties, press the "New" button next to DataContext. Select the CommuterViewModelSample class from the solution. Now Blend will show the data from your mock object while you edit the view.

Overall structure: Grid
The structure of the Window itself is defined by a Grid. Grid is the best container for a window, because it supports "star" sizing. You can be specific about the size of some components, and then leave the rest of the real estate to be carved up proportionally. A Grid is the default layout root that Blend puts into a window, but if you have something else there (like a StackPanel for example), you can right-click on it and select "Change Layout Type".

Select the Grid in the object pane (where you right-clicked to change the layout type) and expand the layout properties. Hit the ellipsis button to set the RowDefinitions property. For Commuter I added 8 rows. I set the height of all but one to "Auto"; the remaining one I left at "1 Star". This one row will resize with the windows, and the others will be the height of their contents.

I populated the rows with controls, alternating between TextBlocks for labels and other controls for inputs. To tell you the truth, it was easier to drop into XML for this part. If you've set the row sizes to "Auto", then there is no space into which to drop controls. And if you haven't you spend a lot of time resetting Margin, HorizontalAlignment, and VerticalAlignment after you drag a control into the right row. Here's the structure so far.

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="Commuter.Window1"
    Title="Commuter" Height="420" Width="540" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:Commuter="clr-namespace:Commuter">
	<Window.DataContext>
		<Commuter:CommuterViewModelSample/>
	</Window.DataContext>
    <Grid>
    	<Grid.RowDefinitions>
    		<RowDefinition Height="Auto"/>
    		<RowDefinition Height="Auto"/>
    		<RowDefinition Height="Auto"/>
    		<RowDefinition Height="Auto"/>
    		<RowDefinition Height="Auto"/>
    		<RowDefinition Height="*"/>
    		<RowDefinition Height="Auto"/>
    		<RowDefinition Height="Auto"/>
    	</Grid.RowDefinitions>
	</Grid>
</Window>

Non-resizeable list: ListBox of Grid
The Podcasts list contains for each podcast a selection box, rating buttons, and the name of the podcast. The selection box and rating buttons are all fixed width. The only thing that varies is the width of the name. This sounds like a job for Grid again. The buttons can assume the width of their contents ("Auto"), while the name can consume all remaining space ("1 Star").

I dropped a ListBox into the appropriate row and set its Height property to a number I liked. Dragging the handle didn't do the right thing, because Blend couldn't tell whether I wanted to resize the row or not. When the property is set explicitly, it's obvious what you intend.

The next step was to bind the ItemsSource to the Podcasts property of the View Model. Click on the little gray dot to the right of the ItemsSource property and select "Data Binding". You'll want the "Explicit Data Context" tab, though I'm not sure why it's called that. You're using the DataContext inherited from the parent Window. Open up the View Model object and select the collection you want to show. I chose the Podcasts property.

Now you need to define how each item in the list looks. Right-click on the ListBox and select "Edit Control Parts (Template)", "Create Empty". Give the template an appropriate name (like PodcastsTemplate), leave "This Window" selected, and hit "OK". This creates a data template resource within the Window itself. If you had created it in the Application, it could be shared among windows. Since I don't see a need for that, I kept it local.

I'm not sure why this is called a "data template". It seems that it should be called a "view template". It's not a template for the data: that's defined by the bound interface property. Anyway...

Back Button From here, you can use a Grid layout just like you did for the Window, but set the ColumnDefinitions instead of the RowDefinitions. When you're done, click the button in the picture on the right to go back to the window. I can't tell you how long it took me to discover this thing.

You may notice that each of the rows is squashed at this point. Set the HorizontalContentAlignment property to "Stretch" to fix this.

Here's the resulting XAML.

<DataTemplate x:Key="PodcastsTemplate">
    <Grid Height="18">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="16"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="38"/>
            <ColumnDefinition Width="38"/>
            <ColumnDefinition Width="38"/>
            <ColumnDefinition Width="38"/>
            <ColumnDefinition Width="38"/>
        </Grid.ColumnDefinitions>
        <CheckBox Width="13" VerticalAlignment="Center" Grid.Column="0"/>
        <TextBlock Width="Auto" Text="{Binding Path=Name, Mode=Default}" Grid.Column="1" VerticalAlignment="Center"/>
        <CheckBox Grid.Column="2" Template="{DynamicResource CarCheckBox}" IsChecked="{Binding Path=Rank1, Mode=Default}"/>
        <CheckBox Grid.Column="3" Template="{DynamicResource CarCheckBox}" IsChecked="{Binding Path=Rank2, Mode=Default}"/>
        <CheckBox Grid.Column="4" Template="{DynamicResource CarCheckBox}" IsChecked="{Binding Path=Rank3, Mode=Default}"/>
        <CheckBox Grid.Column="5" Template="{DynamicResource CarCheckBox}" IsChecked="{Binding Path=Rank4, Mode=Default}"/>
        <CheckBox Grid.Column="6" Template="{DynamicResource CarCheckBox}" IsChecked="{Binding Path=Rank5, Mode=Default}"/>
    </Grid>
</DataTemplate>
...
<ListBox ItemsSource="{Binding Path=Podcasts, Mode=Default}" ItemTemplate="{DynamicResource PodcastsTemplate}" HorizontalContentAlignment="Stretch" Height="76" Grid.Row="3"/>

Resizable list: ListView
The list of episodes contains three string columns. It makes sense to let the user individually resize these, unlike the single text field among a bunch of fixed-width buttons. So I decided on a ListView for this control.

You'll probably have to click the chevrons on the bottom of the toolbar to find the ListView tool. It's not one that's visible out-of-the-box. Set the ItemsSource just as before, But this time, rather than defining a data template, you need to set the View. Expand the "View" property hiding in "Miscellaneous". Click on the "Columns" button to add three columns. Set the Header property and bind the DisplayMemberBinding.

Here's one frustrating part of Blend. Even though we've bound the ItemsSource, Blend presents us with the properties of the CommuterViewModelSample. It should be able to recognize that the ItemsSource is a collection of IPodcastEpisode and give us the properties of that interface. Since it can't we just have to manually enter a path expression. Check the box at the bottom and enter the property name.

Use custom path expression

<ListView ItemsSource="{Binding Path=Queue, Mode=Default}" SelectedItem="{Binding Path=SelectedEpisode, Mode=Default}" Grid.Row="5" HorizontalContentAlignment="Stretch">
    <ListView.View>
        <GridView>
            <GridViewColumn Width="300" Header="Name" DisplayMemberBinding="{Binding Name}"/>
            <GridViewColumn Width="140" Header="Podcast" DisplayMemberBinding="{Binding PodcastName}"/>
            <GridViewColumn Width="50" Header="Duration" DisplayMemberBinding="{Binding DurationAsString}"/>
        </GridView>
    </ListView.View>
</ListView>

Spacing: Style
By default, all of the controls in a grid are pushed right up next to each other. To add spacing, I defined a set of styles. Each style targets a control type in the grid, and sets the Margin property.

    <Window.Resources>
        <Style TargetType="{x:Type ComboBox}">
            <Setter Property="Margin" Value="3"/>
        </Style>
        <Style TargetType="{x:Type TextBlock}">
        	<Setter Property="Margin" Value="3"/>
        </Style>
        <Style TargetType="{x:Type ListBox}">
            <Setter Property="Margin" Value="3"/>
        </Style>
        <Style TargetType="{x:Type ListView}">
            <Setter Property="Margin" Value="3"/>
        </Style>
        <Style TargetType="{x:Type Button}">
            <Setter Property="Margin" Value="3"/>
        </Style>
    </Window.Resources>

Going back to Visual Studio
These patterns covered what I needed for this simple view. I suspect that they will cover most of my needs for data entry windows.

Overall, I find Blend to be a bit cumbersome to work in. It was great for the geometry of the car buttons, but it gets in the way when laying out a form. Dropping a control in the right place is not possible when the container size is Auto. Several extra properties get set on your behalf, which you then need to hunt down and reset. And even though the XAML that I need is small, the Blend UI hides many of the properties behind expanders, buttons, and tabs. I have to navigate several pages just to see what is immediately visible in a single line of XAML.

As a XAML editor, Blend is poor. It offers no intellisense the way that Visual Studio does. So now that I know the XAML that I need, I expect I'll be spending a lot more time in Visual Studio hand-crafting XAML.