Introduction
I have been searching for a basic WPF – MVVM sample project that uses Entity Framework (EF). I found a few projects that combine EF with WCF, and I even found one WPF – MVVM project that used EF directly, but connected to LocalDB and was not basic. When I tried modifying the connection string to a named SQL Server, the project error'd out. After not having any luck finding the kind of basic sample WPF-MVVM project using Entity Framework, I decided to create my own sample project which connects to a named server (an SQL 2008 R2 server on my local machine – will also work on SQL Server 2012 Express). The target audience for this article will ideally have a background in SQL Server programming with TSQL, C# programming, and Visual Studio 2010 (or higher), and .NET Framework 4.0 (or higher).
This sample project is a three-tier project with a Data layer, Business layer, and Presentation layer and consists of the following – two comboboxes, one textbox (in MainWindow.xaml), one ADO.NET Entity Model which contains two tables (Author and Book), and one ViewModel – MainWindowViewModel.cs. This sample was created in Visual Studio 2012 on a Windows 7 workstation, but it can also be created in VS2010.
On project startup, MainWindowViewModel
will populate the Author table entity which is the datasource for the first combobox (call it combo1 for shorthand). Upon selecting an Author item from combo1, the second combobox (call it combo2) gets populated with the related rows from the Book table entity, and on selecting a book item from combo2, the content of the Description field for that book will be displayed in the textbox.
First I create two data tables in a database (any database) on a named SQL Server, which this SQL Server happens to reside on my Win7 workstation – SQL Server 2008 R2 (this project will also work with SQL Server 2012 Express). The tables are Author (the Master table) and Book (the Detail table). These two tables are related with a PK-FK constraint.
Here is the SQL code for creating the tables – which will be the Model part of the project.
create table Author (AuthorId int primary key, AuthorName nvarchar(50))
create table Book (BookId int primary key, AuthorId int, Title nvarchar(50), Description nvarchar(200), Price money)
alter table Book add constraint FK_Book_Author foreign key (AuthorId) references Author (AuthorId)
insert into Author values (1, 'Gambardella, Matthew')
insert into Author values (2, 'Ralls, Kim')
insert into Author values (3, 'Corets, Eva')
insert into Book values (1, 1, 'XML Developers Guide', 'An in-depth look at creating applications with XML.', 4.95)
insert into Book values (2, 1, 'Foods of the world.', 'Simply a book about food.', 5.95)
insert into Book values (3, 1, 'Cars', 'A book about cars.', 8.95)
insert into Book values (4, 2, 'Scarecrows', 'This be could horror or agriculture.', 4.15)
insert into Book values (5, 3, 'Book of blue', 'First in a series of books about colors', 6.30)
insert into Book values (6, 3, 'EF', 'Some tips and trics on Entity Frameworks', 3.45)
Next, I create a new (standard) WPF project from Visual Studio. I named my project Wpf_EF_Mvvm_sample (for anyone who would like to copy the code directly – the project can be named anything – just changed the namespaces in the code). From here I first add an ADO.NET Entity Model (will work with either EF5 or EF6, I tried both of them). This entity model will serve as the Model for the Model-View-ViewModel pattern. I name the .edmx "AuthorBook". I use the wizard to "Generate from Database" and select the respective server that contains the database where I created the two tables. I name the Entity Context "AuthorBookEntities". Click next and select the two tables, Author and Book. Then I change the model name (below in the same dialog window where the tables were selected) to AuthorBookModel and click OK. The Entity Model is now generated. This is the Data layer. This entity model will use DBContext.
Next I add a MainWindowViewModel.cs
class to the project. I add this directly to the root folder of the project (same folder as MainWindow.xaml. The idea is to keep this basic.
In MainWindowViewModel
I add a using System.ComponentModel
directive for the INotifyPropertyChanged
interface that we will need to implement so we can use properties to perform the operations. I also add a using System.Runtime.Compilerservices
directive for the [CallerMemberName]
tag which is used the the NotifyPropertyChanged
method of the INotifyPropertyChanged
implementation. This is a very nice feature because it eliminates the need to include the property names on the calls to NotifyPropertyChanged()
in the MainWindowViewModel
properties (formerly NotifyPropertyChanged("somePropertyName");
).
The only source code for the project will be contained in MainWindowViewModel.cs. This is the Business tier (or Business layer). Here is the source code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Wpf_EF_Mvvm_sample
{
class MainWindowViewModel : INotifyPropertyChanged
{
AuthorBookEntities ctx = new AuthorBookEntities();
public MainWindowViewModel()
{
FillAuthors();
}
private void FillAuthors()
{
var q = (from a in ctx.Authors
select a).ToList();
this.Authors = q;
}
private List<Author> _authors;
public List<Author> Authors
{
get
{
return _authors;
}
set
{
_authors = value;
NotifyPropertyChanged();
}
}
private Author _selectedAuthor;
public Author SelectedAuthor
{
get
{
return _selectedAuthor;
}
set
{
_selectedAuthor = value;
NotifyPropertyChanged();
FillBook();
}
}
private void FillBook()
{
Author author = this.SelectedAuthor;
var q = (from book in ctx.Books
orderby book.Title
where book.AuthorId == author.AuthorId
select book).ToList();
this.Books = q;
}
private List<Book> _books;
public List<Book> Books
{
get
{
return _books;
}
set
{
_books = value;
NotifyPropertyChanged();
}
}
private Book _selectedBook;
public Book SelectedBook
{
get
{
return _selectedBook;
}
set
{
_selectedBook = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
One other nice feature of VS2012 (of the many nice features) – you can place the mouse cursor in any of the using statements at the top of the MainWindowViewModel
class file and right-click. Select "Organize Usings" 2nd option from the top of the dropdown, then select "Remove unused usings". This helps to clean up clutter.
In the MainWindowViewModel
class I first create a context object
AuthorBookEntities ctx = new AuthorBookEntities();
Then in the constructor I load up the first entity/list (Authors) with the FillAuthors()
method. Note the LINQ here – it needs to be a list because a DBSet cannot be consumed directly by a control, and this is the data source for a List property – Authors – which the ItemSource
property of combo1 will be bound to in the XAML. The SelectedItem
property of combo1 (in the XAML) will be bound to the SelectedAuthor
property in MainWindowViewModel
. When this property gets invoked, the FillBook method will be called, and the related books for the selected author will populate the Books list property which the ItemSource
property for to which combo2 will be bound.
Now combo1 and comb2 are populated. The real cool part is when an item is selected from the SelectedItem
property of comobo2 which is bound to the SelectedBook
property. The SelectedBook
property will contain only one row from the Books list. The Text
property of the textbox will be bound to the SelectedBook.Description
property, so when an item is selected from combo2 the content of the Description field from the selected Book row will automatically appear in the textbox. Note: this could introduce a slight dependency on the field names of the Book table using this technique. One workaround for this situation would be to create a method for retrieving the Description value of the selected Book object, and then create a property for the textbox to bind to that would be passed the Description value.
The MainWindowViewModel
is the Business layer.
The third tier of this project, the Presentation layer, will be MainWindow.xaml, which is the view. MainWindow.xaml will contain all the XAML that will bind to the properties in MainWindowViewModel
. But before we hook up the XAML, we need to compile what we have so far so that the project assembly is created and thus create a DataContext to be consumed by MainWindow.xaml.
Note that the project can be compiled with no presentation layer. This is an example of "separation of concerns" which is the core concept of the MVVM pattern. In WinForms you would attach a bunch of code to the events of the controls on the form. If you happen to delete/remove a control from the form – the project would not compile. This is an example of the tightly coupled paradigm of WinForms programming. The MVVM pattern resolves this issue by separating the code from the UI and using Binding and commands to interact between the UI and the code (the Business layer).
After compiling the project, we have an assembly that the MainWindow (the view) can bind to. First I add a reference to the location where MainWindowViewModel
resides (which is the root folder). We add this line to the MainWindow.xaml header section
xmlns:vm="clr-namespace:Wpf_EF_Mvvm_sample"
Then we add the data context
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
This could also be done in the code behind (MainWindow.xaml.cs). I’m just being a purist here keeping the code behind MainWindow.xaml.cs clear of any code that wasn’t there by default.
Here is the XAML for the Presentation layer which is the View (the 3rd tier of this three tier project).
<Window x:Class="Wpf_EF_Mvvm_sample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Wpf_EF_Mvvm_sample"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ComboBox DisplayMemberPath="AuthorName" ItemsSource="{Binding Authors}"
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="1" Name="combo1" SelectedItem="{Binding SelectedAuthor}" />
<ComboBox DisplayMemberPath="Title" ItemsSource="{Binding Books}"
Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="1" Name="combo2" SelectedItem="{Binding SelectedBook}" />
<TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Name="tbDesc" Text="{Binding SelectedBook.Description}"/>
</Grid>
</Window>
Conclusion
I have presented a basic three tier project that contains a Data layer, Business layer, and Presentation layer, the Model, the ViewModel, and the View. The Model is composed of an ADO.NET Entity Model, the ViewModel is composed of the MainWindowViewModel
class, and the View is composed of the MainWindow.xaml. Hopefully, this sample project will provide some enlightenment for those seeking an easy to follow/understand WPF-MVVM project using Entity Framework.