Test Driving a WPF App – Part 3: Switching to new window

This is the third part in what I hope to be a four-part series about creating a WPF application using TDD to drive the functionality and MVVM to keep everything testable as we go. The idea is to use view models to split away functionality from the view and have it available for testing while not losing any capabilities along the way. If you haven’t read the first two parts,  you may want to do that.

In this part, we’ll see what it takes to have a user gesture, in our case, either a double-click on a row in our ListView or clicking a button, take us to a different page in our application.We’ll write the code to get you to the other window in this post and leave the corresponding code to get back for readers as an exercise, should they want to confirm they understand how this all works. So, here we go.

The end result

At the conclusion of this process, we’re going to be able to start from the opening page, showing players and their numbers:

image

and navigate to this second page:

image

About this time, I’m feeling pretty good about my screen design abilities as well, as these pages are beautiful! (One thing I’ve learned about myself over the years is that I have very little taste when it comes to designing UIs :))

Step 1 – Writing the command logic to command the pages to switch

As before, when we’re adding new behavior based on a user gesture, we start by writing the ICommand class. We do this because it fleshes out both the details of that class, but also because it drives us down the road of creating the interface that the command class is going to use. This interface is going to be eventually implemented by the view model class, the TeamStatControlViewModel in our case. We don’t “officially” know that at this point, though, so we work through this interface, which represents one of the growing number of roles our view model fulfills. Fo more information on this style of development, read this paper by Steve Freeman.

Let’s begin by writing our first test for the command class. This is the test that invokes the behavior that the role we’re collaborating with makes available to us. In keeping with my experiment of creating lots of little fixtures, each of which represents a complete, but individual, behavior of my system, I’ve created a fixture called InterPageNavigationFixtures. My idea is that this fixture will hold all tests that document how navigating between pages works. Our first test:

   1: [Fact]

   2: public void SwitchToPlayerViewStartedWhenViewPlayerCommandExecutes()

   3: {

   4:     var viewPlayerSwitcher = new Mock<IViewPlayerModeSwitcher>();

   5:     ViewPlayerModeSwitchCommand cmd = new ViewPlayerModeSwitchCommand(viewPlayerSwitcher.Object);

   6:  

   7:     cmd.Execute(null);

   8:  

   9:     viewPlayerSwitcher.Verify(s => s.SwitchToPlayerViewMode());

  10: }

Like the other test we created for a command, we define the interface that represents the role we’re going to use, IViewPlayerModeSwitcher, and then our command class. The rest of the test ensures that the correct behavior happens when we invoke the command’s Execute method.

To make this test compile, we have to create some things, including the interface, the command, and some very minimal method signatures in both.

In the ViewModels project, we add the interface:

   1: namespace ViewModels

   2: {

   3:     public interface IViewPlayerModeSwitcher

   4:     {

   5:         void SwitchToPlayerViewMode();

   6:     }

   7: }

as well as the skeleton of the command:

   1: namespace ViewModels

   2: {

   3:     public class ViewPlayerModeSwitchCommand

   4:     {

   5:         public ViewPlayerModeSwitchCommand(IViewPlayerModeSwitcher modeSwitcher)

   6:         {

   7:         }

   8:  

   9:         public void Execute(object parameter)

  10:         {

  11:         }

  12:     }

  13: }

Run test, test fails, implement minimal behavior to make test pass:

   1: public class ViewPlayerModeSwitchCommand

   2: {

   3:     private readonly IViewPlayerModeSwitcher modeSwitcher;

   4:  

   5:     public ViewPlayerModeSwitchCommand(IViewPlayerModeSwitcher modeSwitcher)

   6:     {

   7:         this.modeSwitcher = modeSwitcher;

   8:     }

   9:  

  10:     public void Execute(object parameter)

  11:     {

  12:         modeSwitcher.SwitchToPlayerViewMode();

  13:     }

  14: }

At this point, the test passes, and we can invoke the switching behavior that will be implemented in our view model shortly.

The last thing I was to do here before moving on is to expose the command through the view model so the view can bind to it. A simple test, much like others we’ve seen before:

   1: [Fact]

   2: public void ViewModelExposesSwitchToPlayerViewModeCommandAsICommand()

   3: {

   4:     TeamStatControlViewModel viewModel = new TeamStatControlViewModel(null, null, null);

   5:  

   6:     Assert.IsAssignableFrom<ViewPlayerModeSwitchCommand>(viewModel.PlayerViewMode);

   7: }

   8:  

and we implement this by simply adding an auto-property to our view model called PlayerViewMode.

Part 2 – Making the view model change application modes

In this next, the view model will begin to switch modes in the application between the Player Summary mode and View Player mode. I’m intentionally calling these modes here because I don’t want the view model to know about windows and views and other UI-ish specifics. As far as it is concerned, the application has two modes in which it can work, and someone else is going to be able to translate from the information about modes and transitions to swapping out views and controls. This is purely an example of separating concerns, where the view model only cares about the modes of the application and another class, more than likely in the same assembly as the view logic, will know how to act upon modes switching.

Starting down this road, we’ll write a test that drives out the interface for how we’ll tell something outside of the view model that a mode is changing. We’ll be talking to a role I chose to call the ModeController, commanding it through its PlayerViewMode. Here is the test:

   1: [Fact]

   2: public void ModeControllerCommandedToSwitchToPlayerViewByViewModel()

   3: {

   4:     var modeController = new Mock<IModeController>();

   5:     TeamStatControlViewModel viewModel = new TeamStatControlViewModel(null, modeController.Object, null);

   6:  

   7:     viewModel.SwitchToPlayerViewMode();

   8:  

   9:     modeController.Verify(nc => nc.PlayerViewMode(It.IsAny<Player>()));

  10: }

Since the TeamStatControlViewModel needs access to the mode controller, we had to modify its constructor to take an argument of that type. We call the method we’re trying to drive through the tests, SwitchToPlayerViewMode, and then comes our assertion. Our assertion is a bit interesting here – maybe I’m thinking too far ahead, but I’m considering how the view that is going to eventually be rendered is going to work. Its going to need to know about the player that was selected, so it can show it. In fact, that player is probably going to become its DataContext. To give the new view, which will be rendered by someone else at some other time, access to the player, I’m passing it along here. I’m not writing the code that will actually cause it to be passed yet, as you can see by the It.IsAny<Player> argument in the Verify statement, but I’m declaring that an object of that type is going to be passed to it when the system runs for real. Let’s implement this small step:

First, the IModeController, which sits in the ViewModel sassembly:

   1: namespace ViewModels

   2: {

   3:     public interface IModeController

   4:     {

   5:         void PlayerViewMode(Player player);

   6:     }

   7: }

and then the minimal code in the view model:

   1: public class TeamStatControlViewModel: IApplicationExitAdapter, IViewPlayerModeSwitcher

   2: {

   3:     private readonly IApplicationController applicationController;

   4:     private readonly IModeController modeController;

   5:     private readonly IPlayerRepository playerRepository;

   6:  

   7:     public TeamStatControlViewModel(

   8:         IApplicationController applicationController, 

   9:         IModeController modeController, 

  10:         IPlayerRepository playerRepository)

  11:     {

  12:         this.applicationController = applicationController;

  13:         this.modeController = modeController;

  14:         this.playerRepository = playerRepository;

  15:         ApplicationExit = new ApplicationExitCommand(this);

  16:         PlayerViewMode = new ViewPlayerModeSwitchCommand(this);

  17:     }

  18:  

  19:     public void SwitchToPlayerViewMode()

  20:     {

  21:         modeController.PlayerViewMode(new Player());

  22:     }

  23: }

Please not that I’m only showing the interesting bits here, and removing code that didn’t change from the last time we looked at this class. The bits that did change are an additional parameter to the constructor of this class and the addition of the SwitchToPlayerMode method. Since that method came from the IViewPlayerModeSwitcher, I also went ahead and implemented the interface here. (Note – Since this isn’t a lesson in TDD, I skipped the step of how I used the AddMethodParameter refactoring to slowly add the new constructor and move away from the old one. I didn’t do this in one big step, I did it in small steps, using the tests to guide me to what to change next.)

The next step is to get the player that is selected in the ListView and pass that along in the call, instead of passing an empty player. Here is the test used to drive that:

   1: [Fact]

   2: public void SamePlayerPassedToModeControllerAsSetInViewModel()

   3: {

   4:     var modeController = new Mock<IModeController>();

   5:     TeamStatControlViewModel viewModel = new TeamStatControlViewModel(null, modeController.Object, null);

   6:     Player player = new Player();

   7:     viewModel.SelectedPlayer = player;

   8:  

   9:     viewModel.SwitchToPlayerViewMode();

  10:  

  11:     modeController.Verify(nc => nc.PlayerViewMode(player));

  12: }

There are only small differences between this test and the one before it. The main one is that I’m defining a new property on the view model, SelectedPlayer. A ListView sets this property every time the selected item changes in its view, and this property will allow our view model to be informed about which player is selected at any time. Now we can pass along that player in the PlayerViewMode call, and all is well. Here are the minor changes in TeamStatControlViewModel:

   1: public Player SelectedPlayer { get; set; }

   2:  

   3: public void SwitchToPlayerViewMode()

   4: {

   5:     modeController.PlayerViewMode(SelectedPlayer);

   6: }

Part 3 – Hooking up what we have

Despite the fact that I write a lot of tests for my code, I still like to see it work every now and then! This would be one of those times. We’re at the point now where we should be able to wire up everything we have and get to the point where the mode controller would be able to swap out controls, were we to have implemented that last piece of functionality. Let’s hook up what we got and see what works.

Let’s start by making a couple of quick changes in the XAML. We’ll add a SelectedItem attribute and binding in the ListView and we’ll hook up the ViewPlayer button to the command:

   1: <ListView IsSynchronizedWithCurrentItem="True" Grid.Row="1" VerticalAlignment="Stretch" 

   2:     SelectedItem="{Binding SelectedPlayer}" ItemsSource="{Binding Players}">

   1: <Button Command="{Binding PlayerViewMode}" Grid.Column="0" Content="View Player Stats" 

   2:         HorizontalAlignment="Right" Style="{StaticResource NormalButton}"/>

That should be enough to have the user gestures be sent to our view model.

The view model itself is complete, but we need to make some class implement the IModeController. Again, I think the best place, at least for now, is the App class, so we’ll put that interface there – I do suspect that we’ll eventually pull that out of the App class and make it its own class, so that we can test it, but we haven’t had that need yet. Someday, maybe…

That leaves some very minor changes in App.xaml.cs. We add the interface to the class definition, implement the PlayerViewMode method, and tell the container that App implements the IModeController interface (line 8), which allows it to pass an object of that type to the view model’s constructor.

 

   1: public partial class App : Application, IApplicationController, IModeController

   2: {

   3:     private readonly IUnityContainer container = new UnityContainer();

   4:  

   5:     private void Application_Startup(object sender, StartupEventArgs e)

   6:     {

   7:         container.RegisterInstance<IApplicationController>(this);

   8:         container.RegisterInstance<IModeController>(this);

   9:  

  10:         container.RegisterType<IPlayerRepository, PlayerRepository>(new ContainerControlledLifetimeManager());

  11:         container.RegisterType<TeamStatControlViewModel>(new ContainerControlledLifetimeManager());

  12:         container.RegisterType<TeamStatControl>(new ContainerControlledLifetimeManager());

  13:  

  14:         container.RegisterType<MainWindow>(new ContainerControlledLifetimeManager());

  15:         

  16:         Current.MainWindow = container.Resolve<MainWindow>();

  17:         Current.MainWindow.Content = container.Resolve<TeamStatControl>();

  18:         Current.MainWindow.Show();

  19:     }

  20:  

  21:     public void ExitApplication()

  22:     {

  23:         Current.Shutdown();

  24:     }

  25:  

  26:     public void PlayerViewMode(Player player)

  27:     {

  28:         ;

  29:     }

  30: }

At this point, we’re good to try it out, so start up the application, click on a name, press View Player, and… OK, still nothing happens because we don’t have another window yet. I tested this by putting a breakpoint on line 28 and debugging it to see that I made it to here, which I did, and that the correct player was being sent here. I know my tests told me that this should work, but in the immortal words of Ronald Reagan, “Trust, but verify”.

Part 4 – Showing the other view

We’re in the home stretch now. Let’s show the other view. First, let’s just assume that I created another view. You can see it way back at the top – its the second window you see, with a big name and number in it. Once you’re at this point, all you need to do to enable the app to switch between the main view and this view is to implement the App.PlayerViewMode method correctly. I couldn’t find a way to test this, since I’m manipulating the UI directly, so I just wrote the code:

   1: public void PlayerViewMode(Player player)

   2: {

   3:     Current.MainWindow.Content = null;

   4:  

   5:     Current.MainWindow.Content = container.Resolve<PlayerDetailControl>();

   6:     ((PlayerDetailControl) Current.MainWindow.Content).DataContext = player;

   7: }

A couple things to note here. First, I didn’t register the type, PlayerDetailControl, with the container. The Unity container will do its best to create any object that hasn’t been registered with it by calling whatever constructor it can find that matches up with arguments it knows how to build. In our case, the class has a default constructor, so it knew how to build it. Since we didn’t register it, Unity will just create a new instance of the control for me every time I ask for it. I can then assign the player to be its DataContext, and we’re done. That turned out to be very easy :)

At this point, we should be able to select a player, click on the View Player button, and the other view should appear. Success! But we’re not quite finished yet… sorry! Two more points to make

Part 5 – Double clicking and CanExecute for the command

Before we finish, I want to cover two other things. The first is how to enable double click behavior for the ListView. There isn’t a simple way to attach a command behavior to the ListView in the version of WPF I’m using. I know that AttachedBehaviors are part of the new WPF version that’s in development now, but I don’t have that, so you’re stuck with this explanation :)

Since there wasn’t a way to attach a command to the double-click event, I had to put code into the code-behind page, which is the first time I’ve had to do that yet! I wired up an event handler to the double-click event through a small change in the XAML for the ListView:

   1: <ListView IsSynchronizedWithCurrentItem="True" Grid.Row="1" VerticalAlignment="Stretch" 

   2:     MouseDoubleClick="ListView_MouseDoubleClick"  SelectedItem="{Binding SelectedPlayer}" ItemsSource="{Binding Players}">

and added a trivial handler in the TeamStatControl.xaml.cs file:

   1: private void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)

   2: {

   3:     TeamStatControlViewModel viewModel = (TeamStatControlViewModel) DataContext;

   4:     viewModel.PlayerViewMode.Execute(null);

   5: }

All this method does is to call the Execute method of the same command that the button runs. Trivial code. So now we can double-click on a player in the list and the right thing happens.

But what happens if no player is selected (OK, I couldn’t figure out a way to make this happen for the real list view, but it seemed like an interesting example and thought experiment!). We need to implement the CanExecute behavior for the ViewPlayerModeSwitchCommand. As usual, we do this through tests.

In our first test, we confirm that the command cannot execute if no player is selected:

   1: [Fact]

   2: public void ViewModelFunctionalityIsNotOnlyAvailableWhenNoPlayerIsSelected()

   3: {

   4:     TeamStatControlViewModel viewModel = new TeamStatControlViewModel(null, null, null);

   5:     ICommand playerViewModeCommand = viewModel.PlayerViewMode;

   6:  

   7:     Assert.False(playerViewModeCommand.CanExecute(null));

   8: }

and we implement CanExecute as simply as we can to make the test pass:

   1: public bool CanExecute(object parameter)

   2: {

   3:     return false;

   4: }

Our  next test is a little more interesting, as it requires that the CanExecute returns true if a player is selected:

   1: [Fact]

   2: public void ViewModelFunctionalityIsAvailableWhenPlayerIsSelected()

   3: {

   4:     TeamStatControlViewModel viewModel = new TeamStatControlViewModel(null, null, null);

   5:     ICommand playerViewModeCommand = viewModel.PlayerViewMode;

   6:     viewModel.SelectedPlayer = new Player();

   7:  

   8:     Assert.True(playerViewModeCommand.CanExecute(null));

   9: }

The only real modification here is that we use the SelectedPlayer method of the view model to set the player that would be selected through the real view. We expect that the command class is able to query this SelectedPlayer property through its reference to the view model using the IViewPlayerModeSwitcher interface. Well, that’s what we’d expect, anyhow…

   1: public bool CanExecute(object parameter)

   2: {

   3:     return modeSwitcher.SelectedPlayer != null;

   4: }

The SelectedPlayer property isn’t available through that interface, so as our final step along this long and tortuous path, we add it to the interface, our implementation code compiles, our tests all run, and we celebrate by going home early for the day.

Conclusion

I hope people are finding this case study to be useful. I found a lot of different resources on the web about doing bits and pieces of your WPF/MVVM application test-first, but I still had a lot of unanswered questions. First and foremost on that list was how to do simple navigation like I’m doing. By putting together most of a whole application, I hope its more clear to readers how the different pieces fit together.

I do plan on doing one more step along the way, probably posted tomorrow morning (8/25/2009). I want to show how to handle pop-up windows, including windows dialog boxes as well as custom WPF windows. I had to deal with this on my own project, and it took a bit of thinking to figure out how to do it in a testable way.

Until tomorrow!

– bab

This entry was posted in Uncategorized. Bookmark the permalink.

One Response to Test Driving a WPF App – Part 3: Switching to new window

  1. nymphets says:

    very useful article!

Comments are closed.