Copying an object in C# and Visual Basic


Sometimes there are classes you find you use in every project. I remember once (shudder) I, rather inelegantly instantiated a class, and performed a manual copy of all the properties in the object (when the pressure was on, and I just needed to make the thing work).

I have also found myself spending hours chasing subtle and hard to debug issues where ICloneable has been used -  which, incidentally, is widely accepted as an interface one ought never use – or Object.MemberwiseClone. It turns out the only way to do this correctly, is to serialise the object, and then use the deserialised object, implemented here using an extension method.

Even though this is quite straightforward, I still am posting this however , because I find I need to use this functionality frequently, and often I no longer have access to the source, or it takes ages to find the class. The Visual Basic code sample is after the C# one.

C#

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
 
namespace ObjectCopierApp.Extensions
{
/// <summary>
/// Provides a method for performing deep copying of an object.
/// Binary Serialization is used to perform the copy.
/// </summary>
internal static class ObjectCopier
{
/// <summary>
/// Perform a deep copy of an object.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
internal static T Clone<T>(this T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
 
// Don’t serialize a null object, simply return the default for that object
if (ReferenceEquals(source, null))
{
return default(T);
}
 
IFormatter formatter = new BinaryFormatter();
 
using (var stream = new MemoryStream())
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
}
}
 
 

Visual Basic

Imports System.IO
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
 
Namespace ObjectCopierApp.Extensions
”’ <summary>
”’ Provides a method for performing deep copying of an object.
”’ Binary Serialization is used to perform the copy.
”’ </summary>
Friend NotInheritable Class ObjectCopier
Private Sub New()
End Sub
”’ <summary>
”’ Perform a deep copy of an object.
”’ </summary>
”’ <typeparam name="T">The type of object being copied.</typeparam>
”’ <param name="source">The object instance to copy.</param>
”’ <returns>The copied object.</returns>
<System.Runtime.CompilerServices.Extension()> _
Friend Shared Function Clone(Of T)(source As T) As T
If Not GetType(T).IsSerializable Then
Throw New ArgumentException("The type must be serializable.", "source")
End If
 
‘ Don’t serialize a null object, simply return the default for that object
If ReferenceEquals(source, Nothing) Then
Return Nothing
End If
 
Dim formatter As IFormatter = New BinaryFormatter()
 
Using stream = New MemoryStream()
formatter.Serialize(stream, source)
stream.Seek(0, SeekOrigin.Begin)
Return DirectCast(formatter.Deserialize(stream), T)
End Using
End Function
End Class
End Namespace

Calculate range/limit with C#


A recurring requirement in every day programming is determining whether a value exists with a certain range. Since it is exam time (for most students at present), I resolved to write a quick application demonstrating this requirement based around examination marks.

This quick demo is written in WPF, but should work in Silverlight or Windows Phone 7 with no alteration. The Range class though, can be used throughout .NET in general.

To start with, create a new WPF application in visual Studio and call it ExamApp. Add a new folder to the project called Range and in this declare a new interface called IRange with the following members

using System;
 
namespace ExamApp.Range
{
public interface IRange<T> where T : IComparable<T>
{
T Start { get; }
T End { get; }
bool InRange(T valueToFind);
}
}

Create a new class in the same folder called Range which implements the interface above

using System;
 
namespace ExamApp.Range
{
public class Range<T> : IRange<T> where T : IComparable<T>
{
private readonly T start;
private readonly T end;
 
public Range(T start, T end)
{
if (start.CompareTo(end) <= 0)
{
this.start = start;
this.end = end;
}
else
{
this.start = end;
this.end = start;
}
}
 
public T Start
{
get { return this.start; }
}
 
public T End
{
get { return this.end; }
}
 
public bool InRange(T valueToFind)
{
return valueToFind.CompareTo(Start) >= 0 && valueToFind.CompareTo(End) <= 0;
}
}
}
 

Add a new Folder to the project called Examinations and in this add a new class called Percentage. This Percentage class inherits off Range, and here you can create whatever logical range class you may desire for your own applications

using System;

using ExamApp.Range;
 
namespace ExamApp.Examinations
{
public class Percentage<T> : Range<T> where T : IComparable<T>
{
public Percentage(T start, T end) : base(start, end)
{
}
}
}
 

Add a new enumeration called Grade thus

namespace ExamApp.Examinations

{
public enum Grade
{
None = 0,
A = 1,
B = 2,
C = 3,
D = 4,
E = 5,
} ;
}
 

Add a new interface called ITest with the following members.

namespace ExamApp.Examinations

{
interface ITest
{
int ArtAndDesignMark { get; set; }
int ScienceMark { get; set; }
int MathsMark { get; set; }
}
}
 

Add a new folder called Converters to the project, and add the following

using System;

using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Data;
using System.Windows.Markup;
using ExamApp.Examinations;
 
namespace ExamApp.Converters
{
public class MarkToGradeConverter : MarkupExtension, IValueConverter
{
private static MarkToGradeConverter instance;
private static readonly Dictionary<Percentage<int>, Grade> Marks;
 
static MarkToGradeConverter()
{
Marks = new Dictionary<Percentage<int>, Grade>
{
{new Percentage<int>(80, 100), Grade.A},
{new Percentage<int>(75, 79), Grade.B},
{new Percentage<int>(60, 74), Grade.C},
{new Percentage<int>(55, 59), Grade.D},
{new Percentage<int>(0, 54), Grade.E},
};
}
 
public override object ProvideValue(IServiceProvider serviceProvider)
{
return instance ?? (instance = new MarkToGradeConverter());
}
 
#region Implementation of IValueConverter
 
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return GetGrade((int)value).ToString();
}
 
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
 
public static Grade GetGrade(int mark)
{
Percentage<int> key = Marks.Keys.FirstOrDefault(r => r.InRange(mark));
 
if (key != null)
{
Grade grade;
 
if (Marks.TryGetValue(key, out grade))
{
return grade;
}
}
return Grade.None;
}
 
#endregion
}
}
 

As you can see most of the logic is executed here. Add a new class to the project called Student with the following properties. Also note that this implements the ITest interface that was declared earlier

using ExamApp.Examinations;

 
namespace ExamApp
{
public class Student : ITest
{
public Student(string firstName, string surname, int artAndDesignMark, int scienceMark, int mathsMark)
{
this.FirstName = firstName;
this.Surname = surname;
this.ArtAndDesignMark = artAndDesignMark;
this.ScienceMark = scienceMark;
this.MathsMark = mathsMark;
}
 
public Student()
{
}
 
public string FirstName { get; set; }
public string Surname { get; set; }
 
#region Implementation of ITest
 
public int ArtAndDesignMark { get; set; }
public int ScienceMark { get; set; }
public int MathsMark { get; set; }
 
#endregion
 
}
}
 

We need a collection of students to show in a list so create the following class that inherits off ObservableCollection<T>

using System.Collections.ObjectModel;

namespace ExamApp
{
public class Students : ObservableCollection<Student>
{
public Students()
{
Add(new Student
{
FirstName = "John",
Surname = "Lennon",
ArtAndDesignMark = 81,
ScienceMark = 59,
MathsMark = 77
});
Add(new Student
{
FirstName = "Paul",
Surname = "McCartney",
ArtAndDesignMark = 88,
ScienceMark = 40,
MathsMark = 66
});
Add(new Student
{
FirstName = "Ringo",
Surname = "Starr",
ArtAndDesignMark = 88,
ScienceMark = 31,
MathsMark = 96
});
Add(new Student
{
FirstName = "George",
Surname = "Harrison",
ArtAndDesignMark = 100,
ScienceMark = 99,
MathsMark = 99
});
 
}
}
}
 

Now the data structures are complete, add the following .xaml markup

<Window x:Class="ExamApp.MainWindow"
xmlns:Converters="clr-namespace:ExamApp.Converters"
Title="MainWindow"
Width="525"
Height="350">
<Window.Resources>
<Converters:MarkToGradeConverter x:Key="MarkToGradeConverter" />
</Window.Resources>
<ListView x:Name="studentsListBox">
<ListView.View>
<GridView>
<GridViewColumn Width="120"
DisplayMemberBinding="{Binding Path=FirstName}"
Header="Name" />
<GridViewColumn Width="120"
DisplayMemberBinding="{Binding Path=Surname}"
Header="Surname" />
<GridViewColumn Width="40"
DisplayMemberBinding="{Binding Path=ArtAndDesignMark,
Converter={StaticResource MarkToGradeConverter}}"
Header="Art" />
<GridViewColumn Width="60"
DisplayMemberBinding="{Binding Path=ScienceMark,
Converter={StaticResource MarkToGradeConverter}}"
Header="Science" />
<GridViewColumn Width="50"
DisplayMemberBinding="{Binding Path=MathsMark,
Converter={StaticResource MarkToGradeConverter}}"
Header="Maths" />
</GridView>
</ListView.View>
 
</ListView>
 
</Window>
 

Add the following code to the constructor of the main window

using System.Windows;
 
namespace ExamApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.studentsListBox.ItemsSource = new Students();
}
}
}
 

You should have the following

Students

As you can see it is quite a simple and pretty reusable way to use ranges in your application.

The source code for this simple example is available here

Zoomable autosizing canvas in WPF


I have created a control for a project I am working on that uses a canvas to draw graph ticks for a custom scale, but ran into the limitation of the canvas control where child items are not scaled accordingly when the height (or width) of the canvas changes. Luckily, this is quite a straightforward problem to fix, but I just could not seem to find either the correct search terms to enter in Google or locating code samples in books that demonstrated how to resolve the issue. I have just knocked this code up this afternoon so it is not perfect, but should suffice should you require something similar.

Small

Large

In order to resolve the problem you will need to create a custom canvas and override both MeasureOverride and ArrangeOveride incorporating any custom logic you require in these methods.

using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
 
namespace AutosizingCanvasApp.Controls
{
public class TickCanvas : Canvas
{
// Vertical axis where the X value is never changed
private const double X = 0;
private Size initialSize;
 
// Override the default Measure method of Panel
protected override Size MeasureOverride(Size availableSize)
{
var canvasDesiredSize = new Size();
 
// In our example, we just have one child.
// Report that our canvas requires just the size of its only child.
 
foreach (UIElement child in InternalChildren)
{
child.Measure(availableSize);
canvasDesiredSize = child.DesiredSize;
}
 
return canvasDesiredSize;
}
 
protected override Size ArrangeOverride(Size finalSize)
{
if (initialSize.Height == 0)
{
initialSize = finalSize;
}
var ratio = finalSize.Height / initialSize.Height;
 
for (int index = 0; index < this.InternalChildren.Count; index++)
{
UIElement child = this.InternalChildren[index];
 
double y = ((Line)child).Y1;
 
child.Arrange(finalSize.Height > initialSize.Height
? new Rect(new Point(X, (ratio * y) – y), child.DesiredSize)
: new Rect(new Point(X, 0), child.DesiredSize));
}
return finalSize; // Returns the final Arranged size
}
}
}

In the .xaml I have a border with the custom canvas as a child that have some Line objects all affecting the Y axis position.

<Window x:Class="AutosizingCanvasApp.MainWindow"
xmlns:controls="clr-namespace:AutosizingCanvasApp.Controls"
xmlns:local="clr-namespace:AutosizingCanvasApp.Converters"
Title="mainWindow"
Width="300"
Height="768"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<local:BorderHeightValueConverter x:Key="BorderHeightValueConverter" />
<local:LinePositionConverter x:Key="LinePositionConverter" />
</Window.Resources>
 
<Border x:Name="mainBorder"
Width="94"
Height="{Binding ElementName=mainWindow,
Path=ActualHeight,
Converter={StaticResource BorderHeightValueConverter}}"
Margin="50"
BorderBrush="Black"
BorderThickness="2"
ToolTip="{Binding ElementName=mainBorder,
Path=ActualHeight}">
<controls:TickCanvas x:Name="myCanvas">
<Line x:Name="line1"
Stroke="Red"
StrokeThickness="2"
ToolTip="{Binding ElementName=line1,
Path=Y1}"
X1="0"
X2="60"
Y1="{Binding ElementName=mainBorder,
Path=ActualHeight,
Converter={StaticResource LinePositionConverter},
ConverterParameter=0.2}"
Y2="{Binding ElementName=mainBorder,
Path=ActualHeight,
Converter={StaticResource LinePositionConverter},
ConverterParameter=0.2}" />
<Line x:Name="line2"
Stroke="Blue"
StrokeThickness="2"
ToolTip="{Binding ElementName=line2,
Path=Y1}"
X1="0"
X2="60"
Y1="{Binding ElementName=mainBorder,
Path=ActualHeight,
Converter={StaticResource LinePositionConverter},
ConverterParameter=0.4}"
Y2="{Binding ElementName=mainBorder,
Path=ActualHeight,
Converter={StaticResource LinePositionConverter},
ConverterParameter=0.4}" />
<Line x:Name="line3"
Stroke="Green"
StrokeThickness="2"
ToolTip="{Binding ElementName=line3,
Path=Y1}"
X1="0"
X2="60"
Y1="{Binding ElementName=mainBorder,
Path=ActualHeight,
Converter={StaticResource LinePositionConverter},
ConverterParameter=0.6}"
Y2="{Binding ElementName=mainBorder,
Path=ActualHeight,
Converter={StaticResource LinePositionConverter},
ConverterParameter=0.6}" />
<Line x:Name="line4"
Stroke="Purple"
StrokeThickness="2"
ToolTip="{Binding ElementName=line4,
Path=Y1}"
X1="0"
X2="60"
Y1="{Binding ElementName=mainBorder,
Path=ActualHeight,
Converter={StaticResource LinePositionConverter},
ConverterParameter=0.8}"
Y2="{Binding ElementName=mainBorder,
Path=ActualHeight,
Converter={StaticResource LinePositionConverter},
ConverterParameter=0.8}" />
</controls:TickCanvas>
</Border>
</Window>

I also have a couple of converters that converts the height of the border as that changes when you resize the parent window and a couple of converters that reposition the line objects in the canvas. Be aware that the border itself has a thickness, so you will need to incorporate that in any sizing but has been omitted here so the lines may be a couple of pixels off.

using System;
using System.Globalization;
using System.Windows.Data;
 
namespace AutosizingCanvasApp.Converters
{
public class LinePositionConverter : IValueConverter
{
#region Implementation of IValueConverter
 
/// <summary>
/// Converts a value.
/// </summary>
/// <returns>
/// A converted value. If the method returns null, the valid null value is used.
/// </returns>
/// <param name="value">The value produced by the binding source.</param>
/// <param name="targetType">The type of the binding target property.</param>
/// <param name="parameter">The converter parameter to use.</param>
/// <param name="culture">The culture to use in the converter.</param>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((double) value)*double.Parse(parameter.ToString());
}
 
/// <summary>
/// Converts a value.
/// </summary>
/// <returns>
/// A converted value. If the method returns null, the valid null value is used.
/// </returns>
/// <param name="value">The value that is produced by the binding target.</param><param name="targetType">The type to convert to.</param><param name="parameter">The converter parameter to use.</param><param name="culture">The culture to use in the converter.</param>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
 
#endregion
}
}

using System;
using System.Globalization;
using System.Windows.Data;
 
namespace AutosizingCanvasApp.Converters
{
public class BorderHeightValueConverter : IValueConverter
{
#region Implementation of IValueConverter
 
/// <summary>
/// Converts a value.
/// </summary>
/// <returns>
/// A converted value. If the method returns null, the valid null value is used.
/// </returns>
/// <param name="value">The value produced by the binding source.</param>
/// <param name="targetType">The type of the binding target property.</param>
/// <param name="parameter">The converter parameter to use.</param>
/// <param name="culture">The culture to use in the converter.</param>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (((double)value)*0.7);
}
 
/// <summary>
/// Converts a value.
/// </summary>
/// <returns>
/// A converted value. If the method returns null, the valid null value is used.
/// </returns>
/// <param name="value">The value that is produced by the binding target.</param><param name="targetType">The type to convert to.</param><param name="parameter">The converter parameter to use.</param><param name="culture">The culture to use in the converter.</param>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
 
#endregion
}
}
 

The source code is for this example is available here.

WPF and Silverlight Timer


Users of Windows Forms will know that there are three timer objects you can use, namely the Windows Forms Timer, the System Timers Timer and finally the System Threading Timer, as the following example demonstrates;

Note: this tutorial is in both C# and Visual Basic, so look beneath the Visual Basic examples for the C# Version.

Visual Basic

Public Class Form1

 

    Dim timerCallBack As System.Threading.TimerCallback

 

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        ”Winforms Timer

        Dim wfTimer As System.Windows.Forms.Timer()

 

        ”Timers Timer

        Dim tTimer As System.Timers.Timer()

 

        ”Threading Timer

        timerCallBack = AddressOf PrintTime

        Dim thTimer As System.Threading.Timer = New System.Threading.Timer(Me.timerCallBack)

 

 

    End Sub

 

    Private Sub PrintTime(ByVal obj As System.Object)

        ”Print the time to screen

    End Sub

 

 

End Class

C#

using System.Windows.Forms;

 

namespace TimersExample

{

    public partial class Form1 : Form

    {

        System.Threading.TimerCallback timerCallBack;

 

        public Form1()

        {           

            InitializeComponent();

        }

        private void Form1_Load(object sender, System.EventArgs e)

        {

            // Winforms Timer

            System.Windows.Forms.Timer wfTimer = new Timer();

 

            //Timers Timer

            System.Timers.Timer tTimer = new System.Timers.Timer();

 

            //Threading Timer

            timerCallBack = new System.Threading.TimerCallback(PrintTime);

            System.Threading.Timer thTimer = new System.Threading.Timer(this.timerCallBack);

        }

        private void PrintTime(object obj)

        {

            //Print the time to screen

        }

 

    }

}

 

In both WPF and Silverlight there is a new timer (the fourth .NET framework timer) called the System.Windows.Threading.DispatcherTimer. To demonstrate how this works, look at the following simple code sample. In it, there is some XAML that declares a label, button and a button click and window loaded events (In Silverlight use the UserControl_Loaded event for the page instead of Window_Loaded).

 

<Window x:Class="WpfTimer.Window1"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;

   Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">

    <Grid>

 

        <StackPanel Orientation="Vertical">

        <Label Height="28" Width="70" Name="label1">Label</Label>

        <Button Height="23" Width="70" Name="button1" Click="button_Click">Button</Button>

 

        </StackPanel>

 

    </Grid>

</Window>

 

Note that in the Visual Basic XAML you have Window x:Class="Window1" at the top (in case you’re wondering why the XAML does not compile). In the code behind, you have the following code that instantiates a DispatcherTimer in the load event of the Window, and allows a user from starting and stopping the timer by toggling the button click event.

   

Visual Basic

Class Window1

    Dim timer As System.Windows.Threading.DispatcherTimer

    Public Event UpdateTime()

 

    Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)

        timer = New System.Windows.Threading.DispatcherTimer()

        timer.Interval = TimeSpan.FromSeconds(1)

        AddHandler timer.Tick, AddressOf timer_Tick

 

    End Sub

 

    Sub timer_Tick(ByVal sender As Object, ByVal e As EventArgs)

        Me.label1.Content = DateTime.Now.ToLongTimeString()

    End Sub

 

    Private Sub button1_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)

        If Not Me.timer.IsEnabled Then

            Me.timer.Start()

            Me.button1.Content = "Stop"

        Else

            Me.timer.[Stop]()

            Me.button1.Content = "Start"

        End If

    End Sub

 

 

 

 

End Class

 

C#

using System;

using System.Windows;

 

namespace WpfTimer

{

    /// <summary>

    /// Interaction logic for Window1.xaml

    /// </summary>

    public partial class Window1 : Window

    {

        System.Windows.Threading.DispatcherTimer timer;

 

        public Window1()

        {

            InitializeComponent();         

        }

        private void Window_Loaded(object sender, RoutedEventArgs e)

        {

            timer = new System.Windows.Threading.DispatcherTimer();

            timer.Interval = TimeSpan.FromSeconds(1);

            timer.Tick += new EventHandler(timer_Tick);

        }

 

        void timer_Tick(object sender, EventArgs e)

        {

            this.label1.Content = DateTime.Now.ToLongTimeString();

        }

 

        private void button_Click(object sender, RoutedEventArgs e)

        {

            if (!this.timer.IsEnabled)

            {

                this.timer.Start();

                this.button1.Content = "Stop";

            }

            else

            {

                this.timer.Stop();

                this.button1.Content = "Start";

            }

        }

 

 

    }

}

 

DispatcherTimer

Building a Composite WPF and Silverlight Application with Prism – Part 4


Welcome to the final part in 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

Building a Composite WPF and Silverlight Application with Prism – Part 3

The main focus of this tutorial will be introducing communication between modules. Thus far, we have focussed on getting the Digg module operational. In this instalment we will add a search module, and demonstrate how this separate module can interact with another in a loosely coupled fashion.

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

Adding the Search Module

Add a new Silverlight class library to the solution, and call this NewsAggregator.Search and delete the default class1 added to the project

SearchModule

Add references to the Prism libraries (in C# right click the References node in Solution Explorer and choose “Add Reference…” )

AddReferences

add a SearchModule class to this project that implements IModule and implement the interface.

Visual Basic

Imports Microsoft.Practices.Composite.Modularity

Public Class SearchModule

    Implements IModule

    Public Sub Initialize() Implements Microsoft.Practices.Composite.Modularity.IModule.Initialize

    End Sub

End Class

C#

using System;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Ink;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

using Microsoft.Practices.Composite.Modularity;

namespace NewsAggregator.Search

{

    public class SearchModule : IModule

    {

        #region IModule Members

        public void Initialize()

        {

            //throw new NotImplementedException();

        }

        #endregion

    }

}

Add a reference of the NewsAggregator.Search library to the NewsAggregator.Shell and add the SearchModule to the Bootstrapper. Note that this as in the previous example is setting a direct reference to the Shell. This is to hasten the tutorials, but typically you want to add this or any other module in a loosely coupled fashion was well.

Visual Basic

Imports Microsoft.Practices.Composite.UnityExtensions

Imports Microsoft.Practices.Composite.Modularity

Imports NewsAggregator.Digg

Imports NewsAggregator.Search

Public Class Bootstrapper

    Inherits UnityBootstrapper

    Protected Overrides Function CreateShell() As System.Windows.DependencyObject

        Dim shell As New Shell

        Application.Current.RootVisual = shell

        Return shell

    End Function

    Protected Overrides Function GetModuleCatalog() As Microsoft.Practices.Composite.Modularity.IModuleCatalog

        Dim catalog As New ModuleCatalog

        catalog.AddModule(GetType(DiggModule))

        catalog.AddModule(GetType(SearchModule))

        Return catalog

    End Function

End Class

C#

using System.Windows;

using Microsoft.Practices.Composite.UnityExtensions;

using Microsoft.Practices.Composite.Modularity;

using NewsAggregator.Digg;

using NewsAggregator.Search;

namespace NewsAggregator.Shell

{

    public class Bootstrapper : UnityBootstrapper

    {

        protected override DependencyObject CreateShell()

        {

            Shell shell = new Shell();

            Application.Current.RootVisual = shell;

            return shell;

        }

        protected override Microsoft.Practices.Composite.Modularity.IModuleCatalog GetModuleCatalog()

        {

            ModuleCatalog catalog = new ModuleCatalog();

            catalog.AddModule(typeof(DiggModule));

            catalog.AddModule(typeof(SearchModule));

            return catalog;

        }

    }

}

Implementing the Search Model View View Model

Adding the Model View

Add a UserControl to the NewsAggregator.Search library and call this SearchView

SearchView

In this UserControl add the following XAML

<UserControl x:Class=”NewsAggregator.Search.SearchView”

   xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;

   xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”&gt;

    <UserControl.Resources>

        <Style x:Key=”TitleBorder” TargetType=”Border”>

            <Setter Property=”CornerRadius” Value=”10″/>

            <Setter Property=”Background” Value=”#FFDEDEDE”/>

            <Setter Property=”Margin” Value=”0,0,5,0″/>

            <Setter Property=”Grid.Column” Value=”0″/>

        </Style>

        <Style x:Key=”TitleText” TargetType=”TextBlock”>

            <Setter Property=”FontSize” Value=”16″/>

            <Setter Property=”Foreground” Value=”#FF14517B”/>

            <Setter Property=”Margin” Value=”10,3,0,0″/>

        </Style>

        <Style x:Key=”SearchButton” TargetType=”Button”>

            <Setter Property=”Grid.Column” Value=”2″/>

        </Style>

    </UserControl.Resources>

    <Grid>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width=”*”/>

            <ColumnDefinition Width=”200″/>

            <ColumnDefinition Width=”100″/>

        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>

            <RowDefinition/>

        </Grid.RowDefinitions>

        <Border Style=”{StaticResource TitleBorder}”>

            <TextBlock Text=”NEWS SEARCH “ Style=”{StaticResource TitleText}” />

        </Border>

        <TextBox x:Name=”txtSearchTopic” Grid.Column=”1″ Padding=”1,3,1,1″ />     

        <Button Content=”Search” Style=”{StaticResource SearchButton}” />

    </Grid>

</UserControl>

which should result in the following user interface

SearchUI

Adding the View Model

When you click on search, normally you would be handle this by creating an event handler for the search button, but this is not easily testable. Implementing the MVVM pattern here will help with testing.

Add a new class library and call this SearchViewModel. In this library expose an ICommand with a private “setter” so this can be set in the constructor.

Note : Silverlight does not provide an commanding infrastructure here (but WPF does) so Prism has some commanding implementations that can be used. The easiest one to use is the Delegate command because we want to fire off a message on this ViewModel. In the constructor create a new SearchCommand that is a delegate command.Be cognisant that you have to pass in the type it will take (a search string in this case) and then point it to the method (the button click)

Now that the delegate command has been written, bind this method to the button, by adding binding in the XAML (again note that the commanding available in WPF is missing, but the Practices library has an implementation so include the namespace declaration at the top)

xmlns:Commands=”clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation”

Now add the binding to the search button

        <TextBox x:Name=”txtSearchTopic” Grid.Column=”1″ Padding=”1,3,1,1″ />     

        <Button Content=”Search” Style=”{StaticResource SearchButton}” Commands:Click.Command=”{Binding SearchCommand}” />

The SearchCommand binding is the one in the ViewModel that takes a search string as a parameter. In the XAML above you need to somehow pass whatever is entered in the TextBox into the command that is fired when the button is search button clicked. this is easily achieved in WPF but in Silverlight you can only bind to either the DataContext or static resources. To get this to work add a property called Title in the SearchViewModel, add and implement INotifyPropertyChanged as follows

Visual Basic

Imports Microsoft.Practices.Composite.Presentation.Commands

Imports System.ComponentModel

Public Class SearchViewModel

    Implements INotifyPropertyChanged

    Private _SearchCommand As ICommand

    Public Property SearchCommand() As ICommand

        Get

            Return _SearchCommand

        End Get

        Private Set(ByVal value As ICommand)

            _SearchCommand = value

        End Set

    End Property

    Public Sub New()

        Me.SearchCommand = New DelegateCommand(Of String)(AddressOf OnSearch)

    End Sub

    Private Sub OnSearch(ByVal title As String)

    End Sub

    Private _Title As String

    Public Property Title() As String

        Get

            Return Me._Title

        End Get

        Set(ByVal value As String)

            Me._Title = value

            OnPropertyChanged(“Title”)

        End Set

    End Property

#Region “INotifyPropertyChanged Members”

    Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

#End Region

#Region “INotifyPropertyChanged Members”

    Protected Sub OnPropertyChanged(ByVal propertyName As String)

        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))

    End Sub

#End Region

End Class

C#

using System.Windows.Input;

using Microsoft.Practices.Composite.Presentation.Commands;

using System.ComponentModel;

namespace NewsAggregator.Search

{

    public class SearchViewModel : INotifyPropertyChanged

    {

        public ICommand SearchCommand { get; private set; }

        public SearchViewModel()

        {

            this.SearchCommand = new DelegateCommand<string>(OnSearch);

        }

        private void OnSearch(string title)

        {

        }

        private string title;

        public string Title

        {

            get

            {

                return this.title;

            }

            set

            {

                this.title = value;

                OnPropertyChanged(“Title”);

            }

        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region INotifyPropertyChanged Members

        protected void OnPropertyChanged(string propertyName)

        {

            if (this.PropertyChanged != null)

            {

                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

        }

        #endregion

    }

}

So a property has now been created that you can bind against. Add the following binding to the textbox, and ensure two way binding is used so all the changes are synchronised back into the model, and add the command parameter to the search button. the XAML in the SearchView should look like this

<UserControl x:Class=”NewsAggregator.Search.SearchView”

   xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;

   xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;

   xmlns:Commands=”clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation”>

    <UserControl.Resources>

        <Style x:Key=”TitleBorder” TargetType=”Border”>

            <Setter Property=”CornerRadius” Value=”10″/>

            <Setter Property=”Background” Value=”#FFDEDEDE”/>

            <Setter Property=”Margin” Value=”0,0,5,0″/>

            <Setter Property=”Grid.Column” Value=”0″/>

        </Style>

        <Style x:Key=”TitleText” TargetType=”TextBlock”>

            <Setter Property=”FontSize” Value=”16″/>

            <Setter Property=”Foreground” Value=”#FF14517B”/>

            <Setter Property=”Margin” Value=”10,3,0,0″/>

        </Style>

        <Style x:Key=”SearchButton” TargetType=”Button”>

            <Setter Property=”Grid.Column” Value=”2″/>

        </Style>

    </UserControl.Resources>

    <Grid>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width=”*”/>

            <ColumnDefinition Width=”200″/>

            <ColumnDefinition Width=”100″/>

        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>

            <RowDefinition/>

        </Grid.RowDefinitions>

        <Border Style=”{StaticResource TitleBorder}”>

            <TextBlock Text=”NEWS SEARCH “ Style=”{StaticResource TitleText}” />

        </Border>

        <TextBox x:Name=”txtSearchTopic” Grid.Column=”1″ Padding=”1,3,1,1″

                Text=”{Binding Path=Title, Mode=TwoWay}”/>

        <Button Content=”Search” Style=”{StaticResource SearchButton}”

               Commands:Click.Command=”{Binding SearchCommand}”

               Commands:Click.CommandParameter=”{Binding Path=Title}”/>

    </Grid>

</UserControl>

So what happens now is that when text is entered in the textbox it is synchronised back to the SearchViewModel and it is also bound to the search button.

Set the data context of the SearchView to the SearchViewModel

Visual Basic

Partial Public Class SearchView

    Inherits UserControl

    Public Sub New(ByVal viewModel As SearchViewModel)

        InitializeComponent()

        Me.DataContext = viewModel

    End Sub

End Class

C#

using System.Windows.Controls;

namespace NewsAggregator.Search

{

    public partial class SearchView : UserControl

    {

        public SearchView(SearchViewModel viewModel)

        {

            InitializeComponent();

            this.DataContext = viewModel;

        }

    }

}

Now ensure that this View shows up in the Region that has been created, by going to the SearchModule and using constructor injection again.

Visual Basic

Imports Microsoft.Practices.Composite.Modularity

Imports Microsoft.Practices.Composite.Regions

Public Class SearchModule

    Implements IModule

    Private _RegionManager As IRegionManager

    Public Sub New(ByVal regionManager As IRegionManager)

        Me._RegionManager = regionManager

    End Sub

    Public Sub Initialize() Implements Microsoft.Practices.Composite.Modularity.IModule.Initialize

        Me._RegionManager.RegisterViewWithRegion(“SearchRegion”, GetType(SearchView))

    End Sub

End Class

C#

using Microsoft.Practices.Composite.Modularity;

using Microsoft.Practices.Composite.Regions;

namespace NewsAggregator.Search

{

    public class SearchModule : IModule

    {

        private IRegionManager regionManager;

        public SearchModule(IRegionManager regionManager)

        {

            this.regionManager = regionManager;

        }

        #region IModule Members

        public void Initialize()

        {

            this.regionManager.RegisterViewWithRegion(“SearchRegion”, typeof(SearchView));

        }

        #endregion

    }

}

If you run the application you should find that the search module has been added

SearchModule

Decoupled Communication Mechanism

You will notice that the hard wired baseball query it still being used. To set up this decoupled communication two things are going to be required. In Prism there are quite a few ways that this can be achieved, but the EventAggregator will be used in this demo. This gives you access to a publish and subscribe mechanism where you can have multiple publishers for a topic and multiple subscribers as well.

Add a new Silverlight project (this is usually referred to as an infrastructure module) called NewsAggregator.Infrastructure and delete the default class1 that is added

Infrastructure

add references to the Patterns & Practices libraries

Libraries

add a class called SearchEvent that inherits from CompositePresentationEvent

Visual Basic

Imports Microsoft.Practices.Composite.Presentation.Events

Public Class SearchEvent

    Inherits CompositePresentationEvent(Of String)

End Class

C#

using Microsoft.Practices.Composite.Presentation.Events;

namespace NewsAggregator.Infrastructure

{

    public class SearchEvent : CompositePresentationEvent<string>

    {

    }

}

In the NewsAggregator.Search project, add a reference to the NewsAggregator.Infrastructure project

AddInfrastucture

in the constructor of the SearchViewModel add an event aggregator event to the constructor, and publish the “Title” in the OnSearch method

Visual Basic

Imports Microsoft.Practices.Composite.Presentation.Commands

Imports System.ComponentModel

Imports Microsoft.Practices.Composite.Events

Imports NewsAggregator.Infrastructure

Public Class SearchViewModel

    Implements INotifyPropertyChanged

    Private _SearchCommand As ICommand

    Public Property SearchCommand() As ICommand

        Get

            Return _SearchCommand

        End Get

        Private Set(ByVal value As ICommand)

            _SearchCommand = value

        End Set

    End Property

    Private _eventAggregator As IEventAggregator

    Public Sub New(ByVal eventAggregator As IEventAggregator)

        Me.SearchCommand = New DelegateCommand(Of String)(AddressOf OnSearch)

        Me._eventAggregator = eventAggregator

    End Sub

    Private Sub OnSearch(ByVal title As String)

        Me._eventAggregator.GetEvent(Of SearchEvent).Publish(title)

    End Sub

    Private _Title As String

    Public Property Title() As String

        Get

            Return Me._Title

        End Get

        Set(ByVal value As String)

            Me._Title = value

            OnPropertyChanged(“Title”)

        End Set

    End Property

#Region “INotifyPropertyChanged Members”

    Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

#End Region

#Region “INotifyPropertyChanged Members”

    Protected Sub OnPropertyChanged(ByVal propertyName As String)

        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))

    End Sub

#End Region

End Class

C#

using System.Windows.Input;

using Microsoft.Practices.Composite.Presentation.Commands;

using System.ComponentModel;

using Microsoft.Practices.Composite.Events;

using NewsAggregator.Infrastructure;

namespace NewsAggregator.Search

{

    public class SearchViewModel : INotifyPropertyChanged

    {

        public ICommand SearchCommand { get; private set; }

        private IEventAggregator eventAggregator;

        public SearchViewModel(IEventAggregator eventAggregator)

        {

            this.SearchCommand = new DelegateCommand<string>(OnSearch);

            this.eventAggregator = eventAggregator;

        }

        private void OnSearch(string title)

        {

            this.eventAggregator.GetEvent<SearchEvent>().Publish(Title);

        }

        private string title;

        public string Title

        {

            get

            {

                return this.title;

            }

            set

            {

                this.title = value;

                OnPropertyChanged(“Title”);

            }

        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region INotifyPropertyChanged Members

        protected void OnPropertyChanged(string propertyName)

        {

            if (this.PropertyChanged != null)

            {

                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

        }

        #endregion

    }

}

This is the first half of setting the event aggregation, now we need to configure the listening event.

Switch over to the NewsAggregator.Digg project and add a reference to the NewsAggregator.Infrastructure (in the same way you’ve just done this to the NewsAggregator.Search above)

In the DiggSearchResultsViewModel remove the previous dummy search data, and add an event aggregator event to the constructor, and subscribe to the the OnSearchEvent which is a local event just created to process the title.

 

Visual Basic

Imports System.Collections.ObjectModel

Imports Microsoft.Practices.Composite.Events

Imports NewsAggregator.Infrastructure

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, ByVal eventAggregator As IEventAggregator)

        Stories = New ObservableCollection(Of DiggStory)()

        Me.diggService = diggService

        eventAggregator.GetEvent(Of SearchEvent).Subscribe(AddressOf OnSearchEvent)

    End Sub

    Public Sub OnSearchEvent(ByVal title As String)

        Me.diggService.BeginSearch(title, 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;

using Microsoft.Practices.Composite.Events;

using NewsAggregator.Infrastructure;

namespace NewsAggregator.Digg

{

    public class DiggSearchResultsViewModel

    {

        private IDiggService diggService;

        public DiggSearchResultsViewModel(IDiggService diggService, IEventAggregator eventAggregator)

        {

            Stories = new ObservableCollection<DiggStory>();

            this.diggService = diggService;

            eventAggregator.GetEvent<SearchEvent>().Subscribe(OnSearchEvent);

        }

        public void OnSearchEvent(string title)

        {

            this.diggService.BeginSearch(title, 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;

        }

    }

}

Important: Notice that the OnSearchEvent is a public method. This is because Prism does not keep a strong reference to that particular event, this help prevent problems with garbage collection, so it doesn’t prevent your ViewModel being garbage collected for example if the ViewModel is no longer in use. If you do chose to make this private you will need to remember to unsubscribe whenever your ViewModel is no longer in use.

If you run the application, and enter”baseball” you should find that the application can still search base. Try entering a “football” search and you should get football results

FootBallSearch

You should now be able to develop the Twitter client in exactly the same was and add that as a module to the module catalogue in the shell and have that just work.

The complete source code for the 4 part series is available here (Download the WPF Silverlight Prism Folder)

Building a Composite WPF and Silverlight Application with Prism – Part 3


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

  1. View Models are far much more testable
  2. 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

Projects

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

<UserControl x:Class=”NewsAggregator.Digg.DiggSearchResultsView”

   xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;

   xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”&gt;

    <Grid x:Name=”LayoutRoot” Background=”White”>

        <ListBox Name=”storiesList” ItemsSource=”{Binding Stories}”></ListBox>

    </Grid>

</UserControl>

Click F5 to run the program and you should have the following

DiggStory

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&#8221;

   xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221; >

    <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

DiggClient

  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

  1. It will start the search using the query parameter
  2. 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

C#

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

ConnectedToService

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&#8221;

   xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;

      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

SearchResults

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)

Building a Composite WPF and Silverlight Application with Prism – Part 2


In Building a Composite WPF and Silverlight Application with Prism – Part 1 I laid down the foundations of the composite application including

  • Configuring and connecting to Prism
  • Creating a Shell for the Silverlight application
  • Connecting that Shell using a Bootrapper class
  • Adding a Digg module and adding it to the ModuleCatalog

In this second tutorial in the series we will further develop the DiggModule by adding a View, what should be clear is that this architecture is applicable for your own custom application development needs. Prism ostensibly demonstrates the true power of WPF and Silverlight, in that it is a framework that can target both web and smart client applications, using the same modules and code.

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

Open Shell.xaml and you should have the following standard XAML code

<UserControl x:Class=”NewsAggregator.Shell.Shell”

   xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;

   xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;

   Width=”400″ Height=”300″>

    <Grid x:Name=”LayoutRoot” Background=”White”>

    </Grid>

</UserControl>

To restyle the shell, add the following markup

<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&#8221;

   xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;

            Width=”300″ Height=”400″>

    <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>   

    </Grid>

</UserControl>

Here the background colour is set, and a couple of grid rows are added. Now add a ContentControl whose purpose is to be a container for other controls. We will be adding a twitter client as well later on, so a tab control (you can drag it from the toolbox) is needed to switch between the Digg service and the Twitter on. Both controls are assigned to grid rows. Add the following below the end Grid.RowDefinitions tag

        <ContentControl Grid.Row=”0″ Margin=”2″ />

        <basics:TabControl Grid.Row=”1″ Margin=”3″/>

The following controls will act as containers, that any module can insert a view into i.e. The DiggModule and TwitterModule need to be added so these controls can show them. One approach, is for each module to communicate with the Shell directly, and know which control it needs to push things into, but that is tightly coupling the module to the shell.

Prism has a feature called a Region (you can think of a “Region” as a named place holder for where Views are going to be placed), and Modules can find that region by name, and push things into them. to access this in your XAML, you need to declare this namespace at the top

   xmlns:Regions=”clr-namespace:Microsoft.Practices.Composite.Presentation.Regions;assembly=Microsoft.Practices.Composite.Presentation”

Which will allow you to add a Region to a control. In the following markup I have added a SearchRegion and a ResultsRegion so when the Shell is loaded Prism will find these regions and register them in a way that the modules will be able to find them. The following the the complete markup you should have in Shell.xaml

<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&#8221;

   xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;

    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″>

        </basics:TabControl>

    </Grid>

</UserControl>

Now that some regions are available, create a View in the Digg module. to do this add a new Silverlight User Control called DiggSearchResultsView.xaml

DiggSearch

Add a textblock with some text in it (this is so it can be view in the shell) and delete the Width and Height properties

<UserControl x:Class=”NewsAggregator.Digg.DiggSearchResultsView”

   xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;

   xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”&gt;

    <Grid x:Name=”LayoutRoot” Background=”White”>

        <TextBlock>“Hello, I am here, Digg it””</TextBlock>

    </Grid>

</UserControl>

You should have

Hello

Now that this basic View is completed it now needs to be added to a Region. There are several ways to do this, including View Discovery or View Injection.

View Discovery

To get a hold of regions in Prism you need access to the Region Manager Service, and the easiest was to access it, is via it being injected in the constructor. In the DiggModule, add a constructor, and then add a IRegionManager to this. In the Initialize method, you can then indicate to the Region Manager, the name and type of View you wish to pass.

Visual Basic

Imports Microsoft.Practices.Composite.Modularity

Imports Microsoft.Practices.Composite.Regions

    Public Class DiggModule

        Implements IModule

        Private regionManager As IRegionManager

        Public Sub New(ByVal regionManager As IRegionManager)

            Me.regionManager = regionManager

        End Sub

    Public Sub Initialize() Implements Microsoft.Practices.Composite.Modularity.IModule.Initialize

        Me.regionManager.RegisterViewWithRegion(“ResultsRegion”, GetType(DiggSearchResultsView))

    End Sub

    End Class

 

C#

using Microsoft.Practices.Composite.Modularity;

using Microsoft.Practices.Composite.Regions;

namespace NewsAggregator.Digg

{

    public class DiggModule : IModule

    {

        private IRegionManager regionManager;

        public DiggModule(IRegionManager regionManager)

        {

            this.regionManager = regionManager;

        }

        #region IModule Members

        public void Initialize()

        {

            this.regionManager.RegisterViewWithRegion(“ResultsRegion”, typeof(DiggSearchResultsView));

        }

        #endregion

    }

}

If you run the program you should have the following

InternetExplorer

As you can see the view has shown up in the Region.

That wraps up this second tutorial. In the third tutorial, we will start to interact with data, and demonstrate the Model View View Model (MVVM) design pattern.

The complete source code for the 4 part series is available here (Download the WPF Silverlight Prism Folder)