Welcome to this four part series on building a WPF and Silverlight application with Prism. The previous parts are
Building a Composite WPF and Silverlight Application with Prism – Part 1
Building a Composite WPF and Silverlight Application with Prism – Part 2
In this third tutorial, we will create a Digg search view using the Model-View-View-Model (MVVM) pattern, create the Digg search service, and finally demonstrate how to use dependency injection to “inject” views into the View Models.
This tutorial is in both C# and Visual Basic, but when creating projects in Visual Studio, the images I may use may be C# templates for example, but you should be able to do exactly the same in Visual Basic and vica-versa. It avoids the repetition of posting two images with “Open C# Silverlight Application” and “Open Visual Basic Application”, when the Visual Studio templates are the same – bar the language. I will however, post code samples in both languages
Implementing the View using MVVM
The first thing to do is to separate the View from the View Model. We do this because
- View Models are far much more testable
- It works well with re-styling
Add another class called DiggSearchResultsViewModel. The View Model typically has all the properties that you want to bind to. Within this View Model we will have a DiggStory object, so add another class called DiggStory

In this DiggStory add the following properties
Visual Basic
Public Class DiggStory
Public Class DiggStory
Private _Id As Integer
Public Property Id() As Integer
Get
Return _Id
End Get
Set(ByVal value As Integer)
_Id = value
End Set
End Property
Private _Title As String
Public Property Title() As String
Get
Return _Title
End Get
Set(ByVal value As String)
_Title = value
End Set
End Property
Private _Description As String
Public Property Description() As String
Get
Return _Description
End Get
Set(ByVal value As String)
_Description = value
End Set
End Property
Private _NumDiggs As Integer
Public Property NumDiggs() As Integer
Get
Return _NumDiggs
End Get
Set(ByVal value As Integer)
_NumDiggs = value
End Set
End Property
Private _HrefLink As Uri
Public Property HrefLink() As Uri
Get
Return _HrefLink
End Get
Set(ByVal value As Uri)
_HrefLink = value
End Set
End Property
Private _Thumbnail As String
Public Property Thumbnail() As String
Get
Return _Thumbnail
End Get
Set(ByVal value As String)
_Thumbnail = value
End Set
End Property
Private _UserName As String
Public Property UserName() As String
Get
Return _UserName
End Get
Set(ByVal value As String)
_UserName = value
End Set
End Property
End Class
End Class
C#
using System;
namespace NewsAggregator.Digg
{
public class DiggStory
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int NumDiggs { get; set; }
public Uri HrefLink { get; set; }
public string Thumbnail { get; set; }
public string UserName { get; set; }
}
}
A list of these stories is now required, so in the DiggSearchResultsViewModel create the following collection, and add a dummy story in the constructor of the form (note we only need the title here). The ObvervableCollection provides a way to track changes make to the collection.
Visual Basic
Imports System.Collections.ObjectModel
Public Class DiggSearchResultsViewModel
Public Sub New()
Stories = New ObservableCollection(Of DiggStory)()
Dim story As New DiggStory()
story.Title = “I am here, Digg it”
Stories.Add(story)
End Sub
Private _Stories As ObservableCollection(Of DiggStory)
Public Property Stories() As ObservableCollection(Of DiggStory)
Get
Return _Stories
End Get
Private Set(ByVal value As ObservableCollection(Of DiggStory))
_Stories = value
End Set
End Property
End Class
C#
using System.Collections.ObjectModel;
namespace NewsAggregator.Digg
{
public class DiggSearchResultsViewModel
{
public DiggSearchResultsViewModel()
{
Stories = new ObservableCollection<DiggStory>();
Stories.Add(new DiggStory(){Title = “I am here, Digg it”});
}
public ObservableCollection<DiggStory> Stories
{
get;
private set;
}
}
}
Connecting the Model with the View Model
To connect these together we will use constructor injection again. In the DiggSearchResultsView controls constructor add the ViewModel and set the DataContext object of the control to the view model.
Visual Basic
Partial Public Class DiggSearchResultsView
Inherits UserControl
Public Sub New(ByVal viewModel As DiggSearchResultsViewModel)
InitializeComponent()
Me.DataContext = viewModel
End Sub
End Class
C#
namespace NewsAggregator.Digg
{
public partial class DiggSearchResultsView : UserControl
{
public DiggSearchResultsView(DiggSearchResultsViewModel viewModel)
{
InitializeComponent();
this.DataContext = viewModel;
}
}
}
In the XAML for the control, delete the TextBlock with the message in it, and add a ListBox instead
Click F5 to run the program and you should have the following

Add a DataTemplate to the ListBox to format the Stories (or story in this case). You will see that there is quite a bit of XAML, but most of it is to do with styling
<UserControl x:Class=”NewsAggregator.Digg.DiggSearchResultsView”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml” >
<UserControl.Resources>
<Style x:Key=”StoriesList” TargetType=”ListBox”>
<Setter Property=”Margin” Value=”5″/>
<Setter Property=”Grid.Row” Value=”1″/>
</Style>
<Style x:Key=”DiggPanel” TargetType=”StackPanel”>
<Setter Property=”Margin” Value=”10″/>
<Setter Property=”Width” Value=”55″/>
<Setter Property=”Height” Value=”55″/>
<Setter Property=”Background”>
<Setter.Value>
<LinearGradientBrush EndPoint=”0.5,1″ StartPoint=”0.5,0″>
<GradientStop Color=”#FFFFF098″/>
<GradientStop Color=”#FFFFF9D4″ Offset=”1″/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
<Style x:Key=”NumDigsBlock” TargetType=”TextBlock”>
<Setter Property=”HorizontalAlignment” Value=”Center”/>
<Setter Property=”FontSize” Value=”18″/>
<Setter Property=”FontWeight” Value=”Bold”/>
<Setter Property=”Foreground” Value=”DarkSlateGray”/>
</Style>
<Style x:Key=”NumDigsSubBlock” TargetType=”TextBlock”>
<Setter Property=”HorizontalAlignment” Value=”Center”/>
<Setter Property=”FontSize” Value=”14″/>
<Setter Property=”Foreground” Value=”DarkSlateGray”/>
</Style>
<Style x:Key=”ThumbNailPreview” TargetType=”Image”>
<Setter Property=”Margin” Value=”7,7,5,5″/>
<Setter Property=”Height” Value=”55″/>
</Style>
<Style x:Key=”TitleBlock” TargetType=”TextBlock”>
<Setter Property=”FontSize” Value=”12″/>
<Setter Property=”TextAlignment” Value=”Left”/>
<Setter Property=”VerticalAlignment” Value=”Center”/>
</Style>
</UserControl.Resources>
<Grid>
<ListBox x:Name=”StoriesList” Style=”{StaticResource StoriesList}” ItemsSource=”{Binding Stories}”>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation=”Horizontal”>
<!– Yellow Digg Panel with NumDiggs–>
<StackPanel Style=”{StaticResource DiggPanel}” >
<TextBlock Text=”{Binding NumDiggs}” Style=”{StaticResource NumDigsBlock}” />
<TextBlock Text=”diggs” Style=”{StaticResource NumDigsSubBlock}” />
</StackPanel>
<!– Story Thumbnail Preview –>
<Image Source=”{Binding ThumbNail}” Style=”{StaticResource ThumbNailPreview}” />
<!– Story Title–>
<TextBlock Text=”{Binding Title}” Margin=”5″ Style=”{StaticResource TitleBlock}”/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
If you F5 to run the program you should get

Adding the Digg Service
First add an interface that will be a contract for the Digg service, call this IDiggService. This Interface will contain one method called BeginSearch that will do two things
- It will start the search using the query parameter
- It will provide a call-back mechanism for when the search is completed
This is because requests like web service calls in Silverlight must always be carried out asynchronously.
Visual Basic
Public Interface IDiggService
Sub BeginSearch(ByVal query As String, ByVal SearchCompleteCallback As Action(Of IEnumerable(Of DiggStory)))
End Interface
C#
using System;
using System.Collections.Generic;
namespace NewsAggregator.Digg
{
public interface IDiggService
{
void BeginSearch(string query, Action<IEnumerable<DiggStory>> SearchCompleteCallback);
}
}
You can now either implement the Service or View, but I will start with the view first.
Implementing the View
In the constructor for the DiggSearchResultsViewModel add a reference to IDiggService. Here we will then perform a dummy query for “baseball”. When this query is complete it will return some stories (hopefully) pertaining to baseball. These then need to be added to a list in the OnSearchComplete method. The only thing you need to do to view the stories returned from the list is to add them to the Stories already defined.
Visual Basic
Imports System.Collections.ObjectModel
Public Class DiggSearchResultsViewModel
Private _Stories As ObservableCollection(Of DiggStory)
Private diggService As IDiggService
Public Property Stories() As ObservableCollection(Of DiggStory)
Get
Return _Stories
End Get
Private Set(ByVal value As ObservableCollection(Of DiggStory))
_Stories = value
End Set
End Property
Public ReadOnly Property HeaderInfo() As String
Get
Return “Digg Search Results”
End Get
End Property
Public Sub New(ByVal diggService As IDiggService)
Stories = New ObservableCollection(Of DiggStory)()
Dim dummyStory As New DiggStory()
dummyStory.Title = “I am here, Digg it”
Stories.Add(dummyStory)
Me.diggService = diggService
Me.diggService.BeginSearch(“baseball”, AddressOf OnSearchComplete)
End Sub
Private Sub OnSearchComplete(ByVal newStories As IEnumerable(Of DiggStory))
Me.Stories.Clear()
For Each diggStory In newStories
Me.Stories.Add(diggStory)
Next
End Sub
End Class
C#
using System.Collections.ObjectModel;
using System.Collections.Generic;
namespace NewsAggregator.Digg
{
public class DiggSearchResultsViewModel
{
private IDiggService diggService;
public DiggSearchResultsViewModel(IDiggService diggService)
{
Stories = new ObservableCollection<DiggStory>();
Stories.Add(new DiggStory(){Title = “I am here, Digg it”});
this.diggService = diggService;
this.diggService.BeginSearch(“baseball”, OnSearchComplete);
}
private void OnSearchComplete(IEnumerable<DiggStory> newStories)
{
this.Stories.Clear();
foreach (var diggStory in newStories)
{
this.Stories.Add(diggStory);
}
}
public ObservableCollection<DiggStory> Stories
{
get;
private set;
}
}
}
Implementing the Service
In the Digg project add a reference to System.XML.Linq because I am calling a web service and am getting an XML document back (the RSS feed).
Add a new class called DiggService to the project that implements IDiggService
Visual Basic
Option Explicit On
Option Strict On
Imports System.Xml.Linq
Public Class DiggService
Implements IDiggService
Private searchCompleteCallback As Action(Of IEnumerable(Of DiggStory))
Public Sub BeginSearch(ByVal query As String, ByVal SearchCompleteCallback As Action(Of IEnumerable(Of DiggStory))) Implements IDiggService.BeginSearch
Me.searchCompleteCallback = SearchCompleteCallback
‘ Construct Digg REST URL
Dim diggUrl As String = String.Format(“http://services.digg.com/stories/topic/{0}?count=20&appkey=http%3A%2F%2Fscottgu.com”, query)
‘ Initiate Async Network call to Digg
Dim diggService As New WebClient()
AddHandler diggService.DownloadStringCompleted, AddressOf diggService_DownloadStringCompleted
diggService.DownloadStringAsync(New Uri(diggUrl))
End Sub
Sub diggService_DownloadStringCompleted(ByVal sender As Object, ByVal e As DownloadStringCompletedEventArgs)
searchCompleteCallback(BuildStories(e))
End Sub
Private Function BuildStories(ByVal e As DownloadStringCompletedEventArgs) As IEnumerable(Of DiggStory)
If e.[Error] IsNot Nothing Then
Return New List(Of DiggStory)(New DiggStory() {New DiggStory() With {.Title = e.[Error].Message}})
End If
Dim xmlStories As XDocument = XDocument.Parse(e.Result)
Dim stories = From story In xmlStories.Descendants(“story”) _
Where story.Element(“thumbnail”) IsNot Nothing AndAlso _
Not story.<thumbnail>.Value.EndsWith(“.gif”) _
Select New DiggStory() With _
{ _
.Id = CInt(story.@id), _
.Title = story.<title>.Value.Trim(), _
.Description = story.<description>.Value.Trim(), _
.ThumbNail = story.<thumbnail>.@src, _
.HrefLink = New Uri(story.@link), _
.NumDiggs = CInt(story.@diggs), _
.UserName = story.<user>.Value.Trim() _
}
Return stories
End Function
End Class
using System;
using System.Net;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Linq;
namespace NewsAggregator.Digg
{
public class DiggService : IDiggService
{
public void BeginSearch(string query, Action<IEnumerable<DiggStory>> SearchCompleteCallback)
{
// Construct Digg REST URL
string diggUrl = String.Format(“http://services.digg.com/stories/topic/{0}?count=20&appkey=http%3A%2F%2Fscottgu.com”, query);
// Initiate Async Network call to Digg
WebClient diggService = new WebClient();
diggService.DownloadStringCompleted += (sender, e) => SearchCompleteCallback(BuildStories(e));
diggService.DownloadStringAsync(new Uri(diggUrl));
}
private IEnumerable<DiggStory> BuildStories(DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
{
return new List<DiggStory> { new DiggStory() { Title = e.Error.Message } };
}
XDocument xmlStories = XDocument.Parse(e.Result);
var stories = from story in xmlStories.Descendants(“story”)
where story.Element(“thumbnail”) != null &&
!story.Element(“thumbnail”).Attribute(“src”).Value.EndsWith(“.gif”)
select new DiggStory
{
Id = (int)story.Attribute(“id”),
Title = ((string)story.Element(“title”)).Trim(),
Description = ((string)story.Element(“description”)).Trim(),
ThumbNail = (string)story.Element(“thumbnail”).Attribute(“src”).Value,
HrefLink = new Uri((string)story.Attribute(“link”)),
NumDiggs = (int)story.Attribute(“diggs”),
UserName = (string)story.Element(“user”).Attribute(“name”).Value,
};
return stories;
}
}
}
In the DiggModule register this search query in the UnityContainer. To acheive this, create a private IUnityContainer variable called container and add this to the constructor. You can then register this in the Initialize method. The ContainerControlledLifetimeManager class just indicates that the service should be a Singleton.
Visual Basic
Imports Microsoft.Practices.Composite.Modularity
Imports Microsoft.Practices.Composite.Regions
Imports Microsoft.Practices.Unity
Public Class DiggModule
Implements IModule
Private regionManager As IRegionManager
Private container As IUnityContainer
Public Sub New(ByVal regionManager As IRegionManager, ByVal container As IUnityContainer)
Me.regionManager = regionManager
Me.container = container
End Sub
Public Sub Initialize() Implements Microsoft.Practices.Composite.Modularity.IModule.Initialize
Me.container.RegisterType(Of IDiggService, DiggService)(New ContainerControlledLifetimeManager())
Me.regionManager.RegisterViewWithRegion(“ResultsRegion”, GetType(DiggSearchResultsView))
End Sub
End Class
C#
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;
namespace NewsAggregator.Digg
{
public class DiggModule : IModule
{
private IRegionManager regionManager;
private IUnityContainer container;
public DiggModule(IRegionManager regionManager, IUnityContainer container)
{
this.regionManager = regionManager;
this.container = container;
}
#region IModule Members
public void Initialize()
{
this.container.RegisterType<IDiggService, DiggService>(new ContainerControlledLifetimeManager());
this.regionManager.RegisterViewWithRegion(“ResultsRegion”, typeof(DiggSearchResultsView));
}
#endregion
}
}
If you run the project you should a view with live data

If you look carefully at the tab at the top of the shell you will see that there is not title for the shell so a way is needed to communicate title or header information back to the Shell
Header Information
There is a convention to follow when providing this information, and that is for the View Model to provide this information to the Shell Owner can look for this information. Add a new Property into the DiggSearchResultsViewModel called HeaderInfo.
Visual Basic
Imports System.Collections.ObjectModel
Public Class DiggSearchResultsViewModel
Private _Stories As ObservableCollection(Of DiggStory)
Private diggService As IDiggService
Public Property Stories() As ObservableCollection(Of DiggStory)
Get
Return _Stories
End Get
Private Set(ByVal value As ObservableCollection(Of DiggStory))
_Stories = value
End Set
End Property
Public ReadOnly Property HeaderInfo() As String
Get
Return “Digg Search Results”
End Get
End Property
Public Sub New(ByVal diggService As IDiggService)
Stories = New ObservableCollection(Of DiggStory)()
Dim story As New DiggStory()
story.Title = “I am here, Digg it”
Stories.Add(story)
Me.diggService = diggService
Me.diggService.BeginSearch(“baseball”, AddressOf OnSearchComplete)
End Sub
Private Sub OnSearchComplete(ByVal newStories As IEnumerable(Of DiggStory))
Me.Stories.Clear()
For Each diggStory In newStories
Me.Stories.Add(diggStory)
Next
End Sub
End Class
C#
using System.Collections.ObjectModel;
using System.Collections.Generic;
namespace NewsAggregator.Digg
{
public class DiggSearchResultsViewModel
{
private IDiggService diggService;
public DiggSearchResultsViewModel(IDiggService diggService)
{
Stories = new ObservableCollection<DiggStory>();
Stories.Add(new DiggStory(){Title = “I am here, Digg it”});
this.diggService = diggService;
this.diggService.BeginSearch(“baseball”, OnSearchComplete);
}
public string HeaderInfo
{
get { return “Digg Search Results”; }
}
private void OnSearchComplete(IEnumerable<DiggStory> newStories)
{
this.Stories.Clear();
foreach (var diggStory in newStories)
{
this.Stories.Add(diggStory);
}
}
public ObservableCollection<DiggStory> Stories
{
get;
private set;
}
}
}
Open up the Shell.xaml and add the following markup in the tab regions section. Note in WPF this tab property is available to databind to directly, but this is not available in Silverlight.
<UserControl xmlns:basics=”clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls” x:Class=”NewsAggregator.Shell.Shell”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:Regions=”clr-namespace:Microsoft.Practices.Composite.Presentation.Regions;assembly=Microsoft.Practices.Composite.Presentation”>
<UserControl.Resources>
<Style x:Key=”TopGrid” TargetType=”Grid”>
<Setter Property=”Background” Value=”#FF5C7590″ />
</Style>
</UserControl.Resources>
<Grid Style=”{StaticResource TopGrid}”>
<Grid.RowDefinitions>
<RowDefinition Height=”auto”/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<ContentControl Regions:RegionManager.RegionName=”SearchRegion” Grid.Row=”0″ Margin=”2″ />
<basics:TabControl Regions:RegionManager.RegionName=”ResultsRegion” Grid.Row=”1″ Margin=”3″>
<Regions:TabControlRegionAdapter.ItemContainerStyle>
<Style TargetType=”basics:TabItem”>
<Setter Property=”HeaderTemplate”>
<Setter.Value>
<DataTemplate>
<TextBlock Text=”{Binding HeaderInfo}” />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Regions:TabControlRegionAdapter.ItemContainerStyle>
</basics:TabControl>
</Grid>
</UserControl>
If you run the program you should have

That wraps up this penultimate tutorial, in the fourth and final tutorial we will add a searchbox, and show how this can be added in a decoupled fashion.
The complete source code for the 4 part series is available here (Download the WPF Silverlight Prism Folder)