Introduction

There are a number of walk - throughs for Entity Framework on the EF pages and from the usual web resources.  None of these are for WPF using MVVM.  This article and sample are intended to cover that area.   

This Walk Through - but particularly the sample - is intended to demonstrate how to architect a data orientated WPF  business application.  It is as simple as practical whilst using real world techniques.   

Enterprise level applications necessitate some complexity, so the subject is broken down into several articles.

The level of complication to aim for is quite a difficult balance to strike - as  this is the first in a series it will lean towards simplicity.

Requirements

Visual Studio 2013 and .Net4.51 - VS Windows Express will do.
SQL Server - the script is generated from SQL server 2012 but the database is pretty simple and will probably work with earlier or later versions.
MVVM Light ( available free via NuGet). You can probably get away without installing this if you just want to f5 the solution since the dll are already in the bin folder.

It is assumed the reader will download and browse the sample application. Just reading this article without reference to the code will probably be rather confusing.

The reader should be at least familiar with the concepts of MVVM and Entity Framework.

Database first will be used for creation of the EF model. The author prefers this because it focuses the developer attention on building an optimal database rather than making it an after thought.  It is also quicker to build the database first, so long as you know what you need in it. 
Code first can produce some quite strange database designs if you're not careful. 

This is something of a digression though and if you prefer, use code first.

Set Up

The first thing you need to do is set up the SalesSystem database.

You will need SQL server - SQL server express will do. If you don't have it installed yet then preferably choose one of the options which includes the Management studio. LocalDB would also work if you prefer to avoid running sql server services

For working with SQL Server databases the author recommends SQL Server Management Studio. 
This tends to encourage the developer to focus on and learn more about working with databases. 
Databases are where your data goes.  They're important and their design matters.

In the zip file you will find SalesSystemWithData.sql.  Your first step is to edit this in notepad and change the file locations to an appropriate folder on your system. These are near the top of the file.

Change

D:\Biz\sqlServerData\MSSQL11.MSSQLSERVER\MSSQL\DATA\SalesSystem.mdf

and

D:\Biz\sqlServerData\MSSQL11.MSSQLSERVER\MSSQL\DATA\SalesSystem_log.ldf

To wherever your instance of sql server has it's database files.

In order to create the database, open SQL Server Management Studio, connect to the local server or another instance. Click New Query. Drag SalesSystemWithData.sql onto the new query window and click the ! Execute button.
You can alternatively run the script from Visual studio Pro + by dragging the .sql script over on to the area you would usually see code in. A new tab will open with a green run triangle in it's toolbar.
Click that and it will prompt for a connection to a server.

If you installed locally and your local instance is not named and uses mixed or Windows authentication then you should be good to go.
If you installed on a different server then you will need to change the ConnectionString in App.Config. Where it says "data source=(local)",  substitute your server for "(local)".

Tables

There are 4 tables in the database:

  • Customers
  • Orders
  • Order Lines
  • Products

An order has Customer.CustomerId as a foreign Key.
An Order Line has Order.OrderId and Product.ProductId as foreign Keys.
This first sample will cover Create, Read, Update and Delete (CRUD) on Customers and Products ( only ).

Creating an Entity Data Model

Now you have a database you can start using Entity Framework.
The first thing you're going to need is an Entity Data Model.
If this is a new subject to you then you are probably best taking a look at this page
That video is pretty good but somewhat outdated and aimed at a console application, the approach for EF6 is still very similar though.

Model - Step by step

In Solution Explorer, right click your project, Add new Item, choose Data and ADO.Net Entity Data Model.
The name you give this is not awfully important but should still be reasonably meaningful. Where in doubt suffix "Model". Over type with SalesModel in the Name box and click Add.
EF Designer from database will be highlighted and it's what you want, so click Next.

As the SalesSystem database is new you will need to choose New Connection and connect to wherever you set the database up.
What you type in the box at the bottom is pretty important as it'll be the name of the class you use for database context so bear that in mind. Overtype the box at the bottom with SalesContext.
Entity Framework 6 should have it's radio button spot next to it selected, if it's not there then you need to install it via nuget.

Check that's correct and Click Next. 

Select Tables and change the Model Namespace to SalesModel
Click Finish, after a bit of thinking it should present you with a database diagram.

Take a look in the app.config and find your connection string.  Double check this is called SalesContext.  Depending on how you set this up, it may contain your machine name.
In the sample the machine name is changed to (Local) - which means whatever machine the code runs on. 

The connection name must match up with what's in quotes in the DBContext Constructor.  There is a gotcha here to watch out for.  Using the version of EF6 at the time of writing generated code will give an error.  This may well be fixed at some future date.  

Expand SalesModel.edmx and SalesModel.Context.tt.
Open SalesModel.Context.cs
Where it says

public partial class SalesContext : DbContext
{
    public SalesContext()
        : base("name=SalesContext")
    {
    }


Make sure that the string in  : base ("SalesContext") matches the name in app.config.
If it says "name=" then remove the "name=".  It should then look like:

public partial class SalesContext : DbContext
{
    public SalesContext()
        : base("SalesContext")
    {
    }

If you don't you will get a rather mysterious run time error when you try and use the context.

All done.

Entity Framework is now ready to be used via that SalesContext.

Those unfamiliar with EF might want to hit f6 to compile and expand SalesModel.tt to take a look at the partial classes within it. There's one per table.   These are generated by that .tt file ( a T4 template ).
Opening Customer you will notice a public property per field ( scalar properties ) and a collection of the related Orders (Navigation property).

If you prefer your connection string to have a different name you could change that in app.config and match the new name with that inside those quotes above in the line :base("..."). 

Should you later change a database you open the model diagram, right click and choose "Update from Database" and choose the relevant options in the wizard. Although this updates the model and you will see new columns in the diagram the generated classes will not change until you compile - so press f6 and then check your changes are matched by the generated classes.

Outline UI Requirement

The sample follows a deliberately simple UI design.   Whilst it's useful to see a working example your requirements are likely to diverge so a simple UI avoids adding any "extra" complication which could confuse the reader.

If you're thinking "What sort of more complicated requirement?".  Let's just take a quick detour to imagine a common scenario - briefly.

Business systems often have many records.  Just throwing 200,000 records into a DataGrid is going to be very slow and the user would take a long time to find any specific record.

A common requirement is to enable the user to avoid 3 hours of scrolling.  One approach is to have a mechanism to filter the data presented in a DataGrid.     A common style of View involves a MastHead and three columns.   A Treeview or set of combo boxes in the left column allowing the user to refine the subset he wants to look at, the Datagrid central.  The right column for any extra information such as associated data for a selected record. 

As you will see, there's quite enough ground to cover in this article "just" to explain how to show and edit data in a Datagrid.   Let's not get too carried away with the design criteria for step 1.

The user needs to be able to issue commands to navigate and edit.
A parent Window should contain a ToolBar of some sort for buttons and an area to view/edit each Table/Entity.

The parent window traslates into MainWindow.  Since this is MVVM the DataContext is set to an instance of MainWindowViewModel which will contain most of the code which does stuff.

Why not all the code?
Completely avoiding code behind can over complicate some aspects of MVVM.  So long as code behind is only concerned with View processing then that still conforms to MVVM.  There is therefore some code behind in this project.

Navigation and Command Buttons are in two horizontal ListViews. These go in a TabControl which fills row 0 of a Grid.
A Frame fills the rest of the window - in Row 1 of the grid. The content of this frame is swopped out in code behind by the navigation process.  This is where the data will be shown and editing will take place.

Each Table has a corresponding UserControl containing a DataGrid.  A Usercontrol is used since we need some sort of thing to hold everything for each Entity and a UserControl means it's completely discrete and encapsulated.  
Later in the series these UserControls will be somewhat more sophisticated. 

Explanation of Implementation

An explanation of each piece of code in the sample would quickly become a huge ungainly article ( I tried ).
Instead, this article explains the reasoning behind major aspects of the approach which will hopefully be of more use to someone designing their own application.
Certain aspects and in particular details of the UI are rather glossed over to keep the size of the article down.
Browse the sample application and examine the code.

What gets a ViewModel?

As a rule of thumb, each disparate or repeated complex piece of View gets a ViewModel.
In practice this means each entity shown as well as each Window and each UserControl.
An ItemsControl will probably have a  viewmodel to encapsulate the functionality for each item.
Much like defining how to break an application up into classes, what becomes a ViewModel is quite difficult to give hard and fast rules for.  You quickly get a "feel" for it though so don't over think definitions.

An instance of an entity is exposed from a row viewmodel.  
You might think - "Hang on a minute. there's an object there which I could use, why bother wrapping it with a viewmodel? "
Wrapping an entity gives another layer in which you can place functionality such as conversion and change tracking.  You will need this sort of thing and you would otherwise have to pollute each entity class.  Wrapping an entity in a ViewModel keeps such code separate and allows you to inherit common functionality such as INotifyPropertyChanged from a base viewmodel.  

Why not copy every property?

Copying all the fields from an entity to a completely discrete Viewmodel would be possible but that adds a lot of processing.

Some would say that exposing an entity directly is not pure MVVM since the entity is the model and exposing the model to the View is a bit naughty.
Others say that getting the data into that entity is the model layer and the entity object is just a class., so it's no problem.
This is one of those areas in MVVM which is perhaps somewhat grey.

Another approach which is fairly popular is to make the entity a member of a Viewmodel and "wrap" each property of the entity with a public property of the Viewmodel whose "Getter" returns an entity property and setter sets one.  This allows you to add another layer of control over what processing you apply to each property as it is set but at the price of a fair bit of code.
Although it uses a plain class rather than an entity you can see this in the sample here, along with a code snippet to speed up writing those particular property wrappers.

The author's opinion is that if you need extra functionality then introduce the complexity of a wrapper.  If you don't then it's added complexity for no gain.
Such wrapping adds a bit more complexity and this article is intended to be as simple as practical so the approach is good, but not used here.

Overall Code Structure

The DataContext of MainWindow is set to an instance of MainWindowViewModel.
MainWindowViewModel exposes a collection of CommandVM for commands and ViewVM for navigation.

The tables to be maintained are presented in two Usercontrols:
ProductsView datacontext is set to an instance of ProductsViewModel which exposes a collection of ProductVM which in turn exposes an instance of the Product Entity
CustomersView datacontext is set to an instance of CustomersViewModel which exposes a collection of CustomerVM which in turn exposes an instance of the Customer Entity

Usercontrol Viewmodels inherit from CrudVMBase and "row" Viewmodels holding an instance of an entity (corresponding to a row in a Datagrid) inherit from VMBase.

All Viewmodels ultimately inherit from NotifyUIBase which implements INotifyProperyChanged.  ViewModels are instantiated in the constructor code behind of views.  In a real world application you would probably want to use MVVM Light ViewModel Locator to resolve them and dependency injection to supply mock data for design and testing purposes. 

Ribbon/Toolbar/What?

The design calls for some sort of Ribbon or Toolbar in the "Masthead" area of the window.
A Toolbar with two options, holding a drop down for each would be simple but boring and would drop down over the datagrid.

A Ribbon would offer a huge amount of functionality.  Great for Word or a hugely complicated enterprise application. This requirement is relatively simple for a full Ribbon.  Implementing one would have also taken a disproportionate amount of development time. 

A TabControl was chosen since it is simple to implement and the controls are obvious to the user. The approach has an added benefit in that it demonstrates how easy it is to build UI in WPF to a specific requirement.

There are two groups of commands we need - navigation and editing.
The TabControl contain a TabItem for each, holding a horizontally arranged ListView.
The Navigate TabItem has buttons bound to a collection of ViewVM.
The Edit TabItem has buttons bound to a collection of CommandVM.

<TabControl HorizontalAlignment="Left">
    <TabItem Header="Navigate" FontSize="10">
        <ListView ItemsSource="{Binding Views}" BorderBrush="Transparent" FontSize="12" FontWeight="Bold"
                    ScrollViewer.CanContentScroll="False"
                    >
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"></StackPanel>
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
            <ListView.ItemTemplate>
                <DataTemplate>
 
                        <Button Command="{Binding Navigate}"
                                BorderThickness="0" Height="32" Width="100"
                                Background="{StaticResource LightBrightGradientBrush}"
                                Foreground="{StaticResource DarkDullBrush}"
                                >
                            <TextBlock Text="{Binding ViewDisplay}"
                                    TextAlignment="Center"
                                             
                                    />
                        </Button>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </TabItem>
    <TabItem Header="Edit"  FontSize="10">
        <ListView ItemsSource="{Binding Commands}" BorderBrush="Transparent" FontSize="12" FontWeight="Bold"
                    ScrollViewer.CanContentScroll="False"
                    >
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
            <ListView.ItemTemplate>
                <DataTemplate>
 
                        <Button Command="{Binding Send}"
                                BorderThickness="0"
                                Margin="0" Padding="0"
                                >
                            <Path Data="{Binding IconGeometry}" Stretch="Uniform" Fill="{StaticResource MidDullBrush}"                                                          Width="32" Height="32"/>
                            <Button.ToolTip>
                                <TextBlock Text="{Binding CommandDisplay}"/>
                            </Button.ToolTip>
                        </Button>
 
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </TabItem>
</TabControl>

 Note how the Icons are actually Paths bound to Geometries which are basically strings in a Resource Dictionary rather than jpg or ico or png. Paths scale with no problem, they're very light weight and they leave the background visible so you still see the mouseover effect an Image would cover up.

Navigation

The presentation of the Navigate buttons is covered above. Each Button is bound to an instance of ViewVM with it's command bound to a Navigate  RelayCommand,  invoked as the user clicks.
Each ViewVM creates and holds an instance of a View which is created based on the ViewType property set by MainWindowViewModel as it initialises the list of ViewVM.
The approach instantiates a view the first time the user chooses it and therefore spreads out the demand to read data and instantiate controls. All these UserControls will contain UI elements which have UI thread affinity, they must be instantiated on the UI thread. 
Options are therefore somewhat limited and this approach has the benefit of simplicity.
Another alternative would be to use delayed binding and get the data in viewmodel constructors - only really reliable for small amounts of data though.

NavigateVM:

public RelayCommand Navigate { get; set; }
public ViewVM()
{
    Navigate = new RelayCommand(NavigateExecute);
}
public void NavigateExecute()
{
    if(View == null && ViewType != null)
    {
        View = (UserControl)Activator.CreateInstance(ViewType);
    }
    var msg = new NavigateMessage { View = View, ViewModelType = ViewModelType, ViewType = ViewType };
    Messenger.Default.Send<NavigateMessage>(msg);
}


As you can see from the snippet, the Navigate command holds an instance of a View which will be used again on navigation after the first call.  State is therefore maintained.  If, for whatever reason, you did not wish to retain state then you could instead instantiate a new UserControl every time. 

NavigateMessage is subscribed to by the MainWindow code behind.  The method called make the UserControl passed in the message the contents of the MainWindow Frame. This uses MVVM Light messaging and the technique is described in greater detail here

MainWindow code behind.

private void ShowUserControl(NavigateMessage nm)
{
    EditFrame.Content = nm.View;
}


The ProductsView and CustomersView UserControls both have ViewModels which inherit from CrudVMBase. 

This base viewmodel handles the standard edit commands.  Which Viewmodel is appropriate at a given time is controlled by IsCurrentView - set by NavigateMessage.

CrudVMBase:

private void CurrentUserControl(NavigateMessage nm)
{
    if (this.GetType() == nm.ViewModelType)
    {
        isCurrentView = true;
    }
    else
    {
        isCurrentView = false;
    }
}


All CRUD ViewModels which are instantiated and in memory will get any CommandMessage.  IsCurrentView allow them to work out whether they're in the MainWindow or just in memory. 

 

Edit Commands

A couple of people have asked questions about this area of the sample.  For further explanation please see the article: Practical Poly.
The Edit commands are bound from a collection of CommandVM these send a CommandMesage.

public CommandVM()
    {
        Send = new RelayCommand(SendExecute);
    }
    privatevoid SendExecute()
    {
        Messenger.Default.Send<CommandMessage>(Message);
    }

If you take a look at CommandMessage you can see there's a nested enum which is used to define which command a message represents.

public class CommandMessage
    {
    public CommandType Command { get; set; }
    }
    public enum CommandType
    {
        Insert,
        Edit,
        Delete,
        Commit,
        Refresh
    }

CrudVMBase handles the command messages and calls method which are over-ridden in the inheriting viewmodels.

protected void HandleCommand(CommandMessage action)
{
    if (isCurrentView)
    {
        switch (action.Command)
        {
            case CommandType.Insert:
                break;
            case CommandType.Edit:
                break;
            case CommandType.Delete:
                DeleteCurrent();
                break;
            case CommandType.Commit:
                CommitUpdates();
                break;
            case CommandType.Refresh:
                RefreshData();
                break;
            default:
                break;
        }
    }
}


Those methods are virtual in CrudVMBase.  Iimplementation is provided in their inheriting viewmodels which override the method called in their base class.  The base class therefore calls the method in the inheriting class.

 

GetData - Intial read and refresh command


protected async override void GetData()
{
    ThrobberVisible = Visibility.Visible;
    ObservableCollection<CustomerVM> _customers = new ObservableCollection<CustomerVM>();
    var customers = await (from c in db.Customers
                            orderby c.CustomerName
                            select c).ToListAsync();
    //  await Task.Delay(9000);
    foreach (Customer cust in customers)
    {
        _customers.Add(new CustomerVM { IsNew = false, TheCustomer = cust });
    }
    Customers = _customers;
    RaisePropertyChanged("Customers");
    ThrobberVisible = Visibility.Collapsed;
}
The combination of marking that method async and using await... ToListAsync means the actual call to get the data is asynchronous.

Why is this important?
In a real world application the database will be on a server and hence there will be a delay introduced by the network.  
A client may well be one of many and the quantity of data to read is probably going to be much larger than the 6 records in the sample table.  

All in all, such a call will often take significant time - making it asynchronous means the call runs on a separate thread to the UI and the UI therefore remains responsive for the user.
   When the asynchronous call returns data from SQL server this comes back to the UI thread and the method continues straight after that await.  An ObservableCollection is filled and substituted for the ObservableCollection the ItemsSource of the Datagrid is bound to. Since the entire collection is substituted we need to raise Notify Propertychanged in order to tell the view to read the data again.  

You could instead delete any data already in the observable collection and add each record in a loop.  Each added record would trigger a collection changed notification and the view would respond.  That would be a bit more code and a lot less efficient.
  Notice that IsNew is set to false for these entities which have just been read out the database.  By defaulting to true this property allows us to identity any entity created by the Datagrid automatically as the user edits a new row - that will be IsNew true via the default constructor.

CustomerVM wraps an instance of the Customer entity and edited entries will be detected using the dbcontext ChangeTracker.  Note that the dbcontext db is a private member instance of the entity framework context per CRUD viewmodel.   This needs to retain scope for change tracking purposes. This technique is only reasonable for small to medium sized sets of data and later articles will cover alternative approaches.

In this particular GetData you will notice the line await Task.Delay(9000); is commented out.
Under ordinary circumstances data will probably be read off your development machine pretty quickly and you won't get a chance to see the throbber do it's thing.
As an experiment, temporarily remove the comment to include that line, and spin it up to see the throbber working for 9 seconds.  
Since this is asynchronous, like the database access, the UI will remain responsive and the applictaion will remain responsive as you click on other buttons. 

Deletion

Which entry to delete is specified by the user first selecting a row. To work out which item that is from the ViewModel, the SelectedItem is bound to a Selectedxxxxx property where xxxxx is the name of the entity.

There are several ways of dealing with related records dependent on something you are about to delete.
One is to make deletes cascade - you can do this by changing the properties in the model.
If you wanted to do that with Customer, right click the relationship line between Customer and Order. In the properties window change End2 OnDelelete from None to Cascade.
That would, however, lose you all the orders you had for a Customer in a database which is unlikely to be a good idea on a proper system.  The accounts people might not be terribly happy with that design when they find they have no orders left and no way to work out how much a customer owes.

We don't want users to be able to delete orders but we still want them to be able to delete some unused customer record they added by mistake.
The logic followed in the sample is to avoid deleting any record which has a related record.
This is done by querying the count of the related records.  
There's a trick here for performance.
The slightly odd syntax of the LINQ below counts the related entries without pulling the entire records across off the database. Be careful when writing such code and note the syntax used. 

protected override void DeleteCurrent()
{
    UserMessage msg = new UserMessage();
    if (SelectedCustomer !=null)
    {
        int NumOrders = NumberOfOrders();
        if (NumOrders > 0)
        {
            msg.Message = string.Format("Cannot delete - there are {0} Orders for this Customer", NumOrders);
        }
        else
        {
            db.Customers.Remove(SelectedCustomer.TheCustomer);
            Customers.Remove(SelectedCustomer);
            RaisePropertyChanged("Customers");
            msg.Message = "Deleted";
        }
    }
    else
    {
        msg.Message = "No Customer selected to delete";
    }
    Messenger.Default.Send<UserMessage>(msg);
}

Commiting Updates

Editing in the Datagrid, inserting a new row and deleting an entity only take place in the memory of the user's machine. 
In order to persist changes to the database these still need to be committed via Entity Framework and you do that by calling the SaveChanges method on your dbcontext.

CrudVMBase handles a command message which is a Commit CommandType by calling CommitUpdates.  This is over-ridden in the two Usercontrol Viewmodels rather than complicate things by trying to make a generic method. 
IsNew is used to see which entries have been appended, these are added to the relevent entity collection of the context.
If all is well the SaveChanges is called on the context and it is this which actually ends up generating SQL.

SaveChanges is synchronous - you don't actually want the user to be able to go do something else whilst the database is being updated.  Raising orders, creating customers and the like are not trivial tasks.  This isn't just an excel spreadsheet.  It's reasonable to assume there will be relatively few changes to write. 

protected override void CommitUpdates()
{
    UserMessage msg = new UserMessage();
    var inserted = (from c in Customers
                     where c.IsNew
                     select c).ToList();
    if(db.ChangeTracker.HasChanges() || inserted.Count > 0)
    {
        foreach (CustomerVM c in inserted)
        {
            db.Customers.Add(c.TheCustomer);
        }
        try
        {
            db.SaveChanges();
            msg.Message = "Database Updated";
        }
        catch (Exception e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                ErrorMessage = e.InnerException.GetBaseException().ToString();
            }
            msg.Message = "There was a problem updating the database";
        }
    }
    else
    {
        msg.Message = "No changes to save";
    }
    Messenger.Default.Send<UserMessage>(msg);
}


User Messages

Through the above command snippets you can see Messenger is used to send a UserMessage.
This is subscribed to and handled in code behind for the MainWindow.  

The string could instead be bound via a ViewModel property.  A trigger could be used to run the storyboard which sets the opacity to 1 then fades it as the bound value is changed but that would be a bit more complicated.  

All we need to do is show a message.  Displaying things is a View responsibilty. Rather than overcomplicating things, this is handled in code behind.

MainWIndow:

private void ReceiveUserMessage(UserMessage msg)
{
    UIMessage.Opacity = 1;
    UIMessage.Text = msg.Message;
    Storyboard sb = (Storyboard)this.FindResource("FadeUIMessage");
    sb.Begin();
}


The code sets the opacity to 1 which is opaque and hence visible It then starts FadeUIMessage which sets the opacity to 0 again after 6 seconds.

This way the message appears and tends to catch the users attention and then disappears after a while so they aren't confused by stale messages.

Error Messages

In it's current incarnation, an error message should only really appear in some exceptional circumstance.
The most common reason for this will be as you are developing and introduce a bug.
In CommitUpdates you can see if the code is running in the debugger, the ErrorMessage property is set to the innermost exception.

if (System.Diagnostics.Debugger.IsAttached)
{
    ErrorMessage = e.InnerException.GetBaseException().ToString();
}

This is then bound from a TextBlock in the Usercontrol which will appear on the right of the datagrid header.
<TextBlock Text="{Binding ErrorMessage}"  HorizontalAlignment="Right" VerticalAlignment="Top"/>

This approach is often quite handy whilst developing and occasionally whilst investigating some issue in a live system. The check on whether the debugger is attached means the end users will never see a message of this sort.

Omissions

There are several particular points to notice here which are glossed over.

  • Validation is missed out completely.  You'll see why in step 2.  
  • Editing is directly in the Datagrid - this is a bad idea in a production application.  
  • The two sets of data are just simple datagrids on a screen.   

The reason for this is simple. You can only put so much in a sample.  The more that goes into each step the harder it is for anyone to understand how it works and what is going on.  There's already quite a lot to get to grips with in step 1 for the newbie.  Arguably too much.


Summary

As the first step in a series, the sample illustrates a simple approach to:

  • Using an Entity Data Model
  • Basic UI
  • Read records
  • Create record
  • Edit record
  • Delete record

Later steps will explain more sophisticated aspects of editing and validating data.

 

Further Reading

Entity Framework documentation
Josh Smith Article on MVVM
Channel 9 Video on MVVM


Omissions

There are several particular points to notice here which are glossed over.
  1. Validation is missed out completely.  You'll see why in step 2.
  2. Editing is directly in the Datagrid - this is a bad idea in a production application.
  3. The two sets of data are just simple datagrids on a screen.

The reason for this is simple.

You can only put so much in a sample.  The more that goes into each step the harder it is for anyone to understand how it works and what is going on. 

There's already quite a lot to get to grips with in step 1 for the newbie. Arguably too much.