How To Refactor And Build Better Microsoft Silverlight Applications
TweetAs with all development technologies if you asked two developers how to solve a problem it’s unlikely both would come up with the same solution.
It’s no surprise then the same solution to a problem using Silverlight and WPF applications can be implemented in a number of ways.
In a series of posts I want to show how we can refactor a Silverlight application which “works” to be a lot cleaner, easier to maintain and adheres to good development practices.
Although I’m using Silverlight many of the changes and suggestions would also apply to a WPF application.
So what’s the application?
The application adds two numbers together.

Yes that’s it. Click here to see a live demo of the calclator.
Using a simple example (as opposed to a huge complex monster) allows us to focus on refactoring and making improvements rather than trying to understand what the application is doing and wading through hundreds of lines of code.
So what’s wrong with it?
The XAML is shown below
<Grid x:Name="LayoutRoot" Background="White" Height="100" Width="350">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Grid.Column="0" Text="0" Height="25" TextAlignment="Right" x:Name="tbFirstValue"/>
<TextBlock Grid.Column="1" Text="+" Height="25" TextAlignment="Center"/>
<TextBox Grid.Column="2" Text="0" Height="25" TextAlignment="Right" x:Name="tbSecondValue"/>
<TextBlock Grid.Column="3" Text="=" Height="25" TextAlignment="Center"/>
<TextBlock Grid.Column="4" Text="0" Height="25" TextAlignment="Left" x:Name="tbResult"/>
<Button Grid.Row="1" Grid.ColumnSpan="5" Margin="0,5,0,0" Content="Calculate" x:Name="btnCalculate" Click="btnCalculate_Click" />
</Grid>
and this is what the code behind looks like
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void btnCalculate_Click(object sender, RoutedEventArgs e)
{
tbResult.Text = (Convert.ToInt32(tbFirstValue.Text) + Convert.ToInt32(tbSecondValue.Text)).ToString();
}
}
As it stands the application:
- Contains code in the code behind file. OK it’s not so much of an issue for this example and is down to individual preference, but this can quickly become a nightmare to maintain during the life of an application
- Isn’t unit testable – we can’t test two values can be added together
- Has no separation of concerns – the code behind shouldn’t be responsible for adding the numbers together
- A lot of repetition for the styling of the input boxes – if we wanted to make the input boxes bigger we would have to make multiple changes, ideally changes should only need to be made in one place.
- No validation – if the user enters alphabetical characters into application and hits the calculate button the application crashes
So how are we going to improve it?
By taking an agile approach the aim at the end of each refactoring is to have a releasable application i.e. be able to add two numbers together. Therefore each refactoring will involve small incremental changes.
After each refactoring exercise we will review the application to see what improvements have been made and if any new problems have been introduced…
-
Step 1: Implement the MVVM (Model-View-ViewModel) pattern to reduce the amount of code in the Silverlight code behind file and follow some best practices for Silverlight development
Review: The code behind file looks much cleaner now, thanks to the use of commands and the INotifyChanged interface, but should the ViewModel be responsible for calculating the two numbers together? -
Step 2: Remove the responsibility and concerns for calculating numbers in the ViewModel
Review: This refactoring has allowed us to reduce the responsibility of the ViewModel and unit test we can add two numbers together, but we are tightly coupled to the calculator class. -
Step 3: Using SOLID design principles and MEF to remove tight coupling
Review: The application is now made up of loosely coupled components which give us a good platform to build upon. But without any validation of what a user enters for a number the application isn’t very stable. -
Step 4: Validating what the user has entered using exceptions
Review: If the users input is not valid they are now are shown an error message. This is all very well but the user is still able to click the calculate button even if they have entered invalid values to add together. -
Step 5: Validating what the user has entered using exceptions and disabling the calculate button if there are any validation errors
Review: The user is only able to click the calculate button when there are no validation errors. Even though throwing exceptions for validation works, some developers aren’t so keen on doing this so what are the alternatives? -
Step 6a: Validating what the user has entered using IDataErrorInfo
Review: By implementing the IDataErrorInfo interface we are able to do validation without throwing exceptions. -
Step 6b: Validating what the user has entered using INotifyDataErrorInfo
Review: By implementing the INotifyDataErrorInfo interface we are able to do validation without throwing exceptions.
Pingback: Cursus MVVM / MEF / SOLID voor Silverlight « Jan Keijzer’s Weblog