EF Code First and WPF with the Chinook database. Part 2 – The client

Now we have the data fetching work sorted out we can turn our attentions to the front-end.

I am going to make a very simple view in this post listing the artists and associated albums with their tracks.  It is going to look like this:

image

Now, this is very basic but it serves its purpose to show the tools we are going to be using – a later post will use the power of WPF to style and transform the screen into something more palatable.

The first thing to do is add a WPF project(I have named mine MusicApp.WPF.Client).  If you rename the the MainWindow.xaml file then don’t forget to change the startup uri in app.xaml

<Application x:Class="MusicApp.WPF.Client.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="Main.xaml">

In terms of the structure for the project I have separate folders for views, view models and common files.

The View

Once the basic structure is in place we can add our view to the Views folder.  The view in this case is a user control that we will add to the main window.

The view itself is very simplistic – 1 grid containing 3 ListBoxes and is created as follows

<UserControl x:Class="MusicApp.WPF.Client.Views.ArtistList"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              Height="Auto" Width="Auto">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.5*" />
            <ColumnDefinition Width="0.5*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="0.7*"/>
            <RowDefinition Height="0.3*"/>
        </Grid.RowDefinitions>
        <ListBox ItemsSource="{Binding Path=Artists}" Margin="5" 
                 DisplayMemberPath="Name" Grid.Column="0" 
                 Grid.ColumnSpan="2" Grid.Row="0" 
                 SelectedItem="{Binding SelectedArtist}" />
        
        <ListBox ItemsSource="{Binding Path=SelectedArtist.Albums}" Margin="5" 
                 DisplayMemberPath="Title" Grid.Column="0" Grid.Row="1" 
                 SelectedItem="{Binding SelectedAlbum}" />
        
        <ListBox ItemsSource="{Binding Path=SelectedAlbum.Tracks}" Margin="5" 
                 DisplayMemberPath="Name" Grid.Column="1" Grid.Row="1" />
    </Grid>
</UserControl>

This is a view with a 2 x 2 grid with columns of equal width (1/2 of the grid each) and one row at 70% width and one at 30%.  Inside the grid there is a ListBox that takes up the whole top row (ColumnSpan=2) and one ListBox in each of the columns in the lower row.

I have included all of the bindings so that I know what is required on the view model when I write it.  I will need a collection property called ‘Artists’ and 2 properties for the selected artist and selected album.

The DisplayMemberPath details which property of the object will be displayed as text in the ListBoxItem.

Once the control has been written then we can add it to the main window xaml.  There are 2 things to do to add a usercontrol to another xaml file

  1. add the correct namespace.
  2. add the control

Adding the namespace requires that we add an xmlns to the top of the main window file

xmlns:views="clr-namespace:MusicApp.WPF.Client.Views"

Once we have this then we can simply add the control as a new object on the window

<views:ArtistList/>

which gives us a Main window with the following xaml

<Window x:Class="MusicApp.WPF.Client.Main"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:MusicApp.WPF.Client.Views"
        Title="Main" Height="475" Width="425">
    <Grid>
        <views:ArtistList/>
    </Grid>
</Window>

The ViewModel

Now the view is done I can create a ViewModel to support it.  The ViewModel will need to implement INotifyPropertyChanged and I have the following snippet method that will fire the event implemented by it

private void Notify(string propertyName)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Unity

I want to be able to use a different data provider here if I want to so I am going to introduce the Unity IoC container to the application at this point.  Unity can be installed into the current project directly from NuGet (install-package unity) or you can download directly. It needs a little configuration before we can use it.

I am going to use it to resolve a reference to the IDataProvider interface we created previously so I add a new app.config file to the client project and add the following configuration

<configuration>
    <configSections>
        <section name="unity"
                             type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
    </configSections>
    <unity>
        <containers>
            <container>
                <type type="MusicApp.Model.IDataProvider, MusicApp.Model" mapTo="MusicApp.Model.EFCodeFirstDataProvider, MusicApp.Model" />
            </container>
        </containers>
    </unity>
</configuration>

This is simply adding a mapping between IDataProvider and EFCodeFirstDataProvider which allows me to resolve the former to the latter.

We need a way of referencing this container in our application so I use a factory class with a static member to hold the actual UnityContainer

class ContainerFactory
{
    private static UnityContainer container;

    public ContainerFactory()
    {
        if (container == null)
        {
            container = new UnityContainer();
            UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            section.Containers.Default.Configure(container);
        }
    }

    public UnityContainer Container
    {
        get
        {
            return container;
        }
    }
}

I can then have a reference to an IDataProvider in the ViewModel and resolve it from the constructor as follows

ContainerFactory factory = new ContainerFactory();
provider = factory.Container.Resolve<IDataProvider>();

The whole ViewModel including properties now looks like this.

public class ArtistListViewModel : INotifyPropertyChanged
{
    private IDataProvider provider;
    private ObservableCollection<Artist> artists;
    private Artist selectedArtist;
    private Album selectedAlbum;

    public ArtistListViewModel()
    {
        ContainerFactory factory = new ContainerFactory();
        provider = factory.Container.Resolve<IDataProvider>();
        GetArtists();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public ObservableCollection<Artist> Artists
    {
        get
        {
            return artists;
        }
        private set
        {
            artists = value;
            Notify("Artists");
        }
    }

    public Artist SelectedArtist
    {
        get
        {
            return selectedArtist;
        }
        set
        {
            selectedArtist = value;
            Notify("SelectedArtist");
        }
    }

    public Album SelectedAlbum
    {
        get
        {
            return selectedAlbum;
        }
        set
        {
            selectedAlbum = value;
            Notify("SelectedAlbum");
        }
    }

    private void GetArtists()
    {
        Artists = new ObservableCollection<Artist>(provider.GetArtists());
    }

    private void Notify(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Wire it up

Now we have the ViewModel and view in place we need to wire them together this is just a case of setting the views DataContext from its constructor

public ArtistList()
{
    InitializeComponent();
    this.DataContext = new ArtistListViewModel();
}

Wrapping up

Now we have these wired we simply need to add the ConnectionStrings to the config file (that we added the unity config to earlier), change the startup project to the new WPF app and run it.  This will now display the window shown at the start and I can change the artist to update the albums/tracks at the bottom.

Next time – making the view look good.

About these ads

About Leom Burke
Working for a healthcare company in the UK with a focus on C#/WPF/WCF/EF. Personally I also enjoy working with ASP.NET (MVC), ruby and investigating best practice using methods like TDD and bettering the quality of code. This is personal blog and as such has no relationship to the thoughts, views and feelings of the company I happen to be working for.

3 Responses to EF Code First and WPF with the Chinook database. Part 2 – The client

  1. Pingback: EF Code First and WPF with the Chinook database. Part 3 – Styles and DataTemplates 101 « Random Ramblings

  2. Namgeun Jeong says:

    I kept having an error after GetSection(“unity’) statement.
    I make it work by replacing that statement and the next one with

    container.RegisterType()
    .RegisterType();

    I don’t know why. I will let you know once I find that out.

    • Leom Burke says:

      This is odd – ConfigurationManager.GetSection should work fine as long as you have unity section in your config file as shown.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: