Free!  FoxPro to WPF screen converter
   Almost free!  One day of FREE VFP to WPF evaluation of your FoxPro app ONSITE
   Free!  Read the foreword to Les' new book (available here)...
Skip Navigation Links

The simplest possible WPF/MVVM Add/Edit/Delete form

For many of your FoxPro conversion candidates, this model may meet your needs


Les Pinter

Login or register to download source code

Introduction

If you're contemplating converting a FoxPro application to a modern, supported technological base, Windows Presentation Foundation (WPF) is likely your choice. Model-View-ViewModel (MVVM) is how such applications are written, to take advantage of the striking interfaces that Extended Application Markup Language (XAML) makes possible, and to write code that permits unit testing of your code (using nUnit or other testbeds) without launching your executable. But simple, straightforward examples of real-world patterns are hard to come by. I hope to remedy that deficiency by means of a series of how-to articles, of which this is the first.

The test case

Most FoxPro forms follow one of a rather small number of canonical patterns. One of them features a datagrid displaying records from a single table. Beneath it, textboxes representing the columns from the selected ("current") row of the grid are exposed for editing the selected record, or for adding a new record. That pattern is the subject of this article,

Here's an example of that pattern:

Fig. 1 - a typical simple form

In FoxPro, this kind of form requires about fifty lines of code. Let's see what it takes in C#, with the added complication of using Model/View/ViewModel (MVVM),

MVVM is the paradigm used to take advantage of data binding, one of the nicest features of Windows Presentation Foundation (WPF).

Building the DataModel

In FoxPro, data access is effortless. APPEND BLANK AND DELETE are how you add or delete a record. If you've used Buffering mode 3, TableUpdate(.T.) saves changes, and TableRevert(.T.) cancels them. Couldn't be simpler.

In C#, the first task is to write a program to manage all dealings with your data. The good news is, the Integrated Development Environment (IDE) does it for you. But it's not your father's FoxPro.

I created a database called WPFTest containing a table called People in SQL Server. SQL is the easiest way to store data for an application. It's expensive, but I have never had to rebuild an index, repair a trashed DBF header, or build workarounds for glacially slow index traffic on a local area network. If you customer balks at a two thousand dollar database, they probably won't like paying you five or six thousand a week to write the program, so consider it a way to qualify prospects.

Here's the People table:

 ID Guid   uniqueidentifier)
 FirstName VarChar(20)
 LastName  VarChar(20)
 Address   VarChar(50)
 City      VarChar(20)
 State     Char(2)
 ZIP       VarChar(10)
 Phone     VarChar(12)

To create your datamodel, add a separate class library project to your solution, erase the "Class1.cs" file that it automatically adds, add a New Item (use ADO as the filter to add an ADO Entity Data Model named "DataModel"), and walk through the wizard to add a connection to your database and select your People table. Once you've added your WPF Application project to the solution, drag and drop your App.Config file to the WPF project. Add a reference from the DataModel project to your WPF project, and use Tools, NuGet Package Manager to "install-package EntityFramework". You'll add a using DataModel statement at the top of the ViewModel that we'll write shortly.

The WPF Form using Extended Application Markup Language (XAML)

Now let's look at the XAML needed to display this form. Click the button below to show/hide the code.

The Window declaration

The Window declaration centers the form and provides a title, width and height; the namespace (xmlns) declarations provide internal names for resources used by the screen renderer.

The Window Resources section

The Window.Resources section provides some visual styles for buttons and textboxes; ordinarily, you would have a single resource file or an App.XAML file to style all controls. These can be quite robust; some are 5,000 lines or more long.

The Grid

The preferred layout mechanism for a WPF form is a Grid. The StackPanel tries to render its contents every time a row is added, and it can cause a 10-second data loading process to take five minutes. So don't EVER enclose a DataGrid or RadGridView inside a StackPanel. Rows 0, 2 and 3 are 30 pixels high; row 1 holds the grid, and occupies the remaining space in the Grid.

Row 1 (zero, actually)

The first grid row displays the number of records in the grid. It's not essential, but it provides an opportunity to demonstrate some features of data binding. In particular, that "{Binding RecordCount} refers to a public int RecordCount in the ViewModel with a getter and a setter, which is what makes it a property (otherwise, it's just a field. Fields don't work with databinding; you have to use get and set, and they have to be public. End of story.

Almost; You also have to "RaisePropertyChanged" (see ViewModelBase.cs, below) after you set the value of a property; otherwise, WPF can't see that it changed. Unless it's an ObservableCollection, which automatically does Property Change Notification. More on that later.

Row 2 (okay, 1) holds the DataGrid

I used a WPF datagrid, although in my practice I use Telerik's RadGridView, which does my filtering and searching. The ItemsSource is the name of the public ObservableCollection that I load in the ViewModel. The SelectedItem binding causes the currently selected row to be sent to a ViewModel property SelectedPerson, consisting of one record. As the user moves the row selector up and down, the "current record" is stuffed into a structure based on one record in the People table.

Row 3 (2): The Add/Edit textboxes for the current row

As the user scrolls from row to row, the selected record is copied to a collection called SelectedPerson. The name is completely arbitrary, but since the point is that the current grid row is sent there when the row is selected, it seemed like a good choice. The DataContext = {Binding SelectedPerson} attribute in the Grid tag tells WPF to bind all of the contained textboxes to that record. It couldn't be simpler.

Row 4 (3): The buttons

The buttons in the StackPanel in GridRow 3 have Commands. This is probably the most complicated aspect of this app, and it relies on a ViewModelBaseClass that I pirated from somewhere or other. The Commands collection in the ViewModel...well, we'll get there momentarily.

The Code-behind

The code-behind for the form is tiny, and almost always the same:

Listing 2 - the code-behind file

MainWindow.xaml.cs

using System.Windows;

namespace WpfApplication1
{
  public partial class MainWindow : Window
  {
    public PeopleViewModel vm;

    public MainWindow()
    {
      InitializeComponent();
      vm = new PeopleViewModel();
      vm.CallingForm = this; //so that the ViewModel can close the form
      DataContext = vm;
    }

    private void DataGrid_AutoGeneratingColumn(object sender,
    System.Windows.Controls.DataGridAutoGeneratingColumnEventArgs e)
    {
      if (e.Column.Header.ToString() == "ID" )
      { e.Column.Visibility = Visibility.Collapsed; }
    }
  }
}

The main business of this little program is, of course, to create an instance of the ViewModel for this form, and to assign it to the form's DataContext. But this is also an excellent place to do something for the UI - hide the ID column, which has a big, ugly, 36-character GUID value. Our data model needs them, but we don't need to see them. That's what the AutoGeneratingColumn event handler does.

Note that other UI-related code can also go here without doing any harm to the refined sensibilities of MVVM fundamentalists. If the only problem you have with autogenerating columns is that your column names are awful, you can use this in the form's Load()> event (provided your grid is named "grid"):

 private void Window_Loaded(object sender, RoutedEventArgs e)
 {string[] headings =
  { "Hidden", "First", "Last", "Street Address", "City", "ST", "ZIP code", "Tel" };
  for (int I = 0; I < headings.Length; I++) { grid.Columns[I].Header = headings[I]; }
 }

The ViewModel

The ViewModel is where all of the code for the form is located. It's activated by means of binding, in several ways:

  • The setter of public properties, like TextBoxes, ComboBoxes and others. The set code always starts by storing the value sent to it, but any code can follow. I've seen setters with dozens of lines of code;
  • Commands, which are attached to Buttons via binding;
  • SelectedItem bindings for lists, like ListBoxes, DropDownCombos and GridViews. As the user moves up and down the list, the setter fires; the value is passed to the public string property bound to SelectedItem, and again, can be used for any purpose.

Take a look at the ViewModel below; it has examples of all three.

The ViewModel appears in Listing 2, below.

Properties in the ViewModel class

RaisePropertyChanged("PropName") is implemented in the ViewModelBase class, It sends "property change notification" back to the form. Without it, the form won't know that the value was changed in your code. If other public properties are changed, you'll also have to RaisePropertyChanged for those properties. I often forget to include it, and then spend hours wondering why my lovely code doesn't work. So you've been forewarned.

The first entry in the ViewModel class is actually a public field; as we'll see later, the calling form is assigned to this field so that the calling form that uses this viewmodeln can be closed using CallingForm.Close(). Not pure MVVM, but what the hell.

There are 5 public properties that are used to communicate with the form.

  • The RecordCount property is set to the number of rows in the people collection, explained below; the RaisePropertyChanged("RecordCount") is needed to tell the form that the value changed.
  • The CommandMap, consisting of two properties: One to hold the collection of commands, and another to make them easy to reference from WPF;
  • The dbContext class (WPFTestEntities) generated by ADO, and the mgr class created from it;
  • An ObservableCollection of Person objects, used to populate the grid; and a
  • Person object called SelectedPerson, which is what the textboxes below the grid are bound to.

The Constructor (which always has the same name as the containing class in C#) creates the three objects used in the code, adds the commands that are bound to our buttons, and loads the data - just a few records, in this case.

LoadData() clears the people collection, and then defines and executes a query and loads the records (one at a time) into people. It also stores the number of records loaded into RecordCount (which contains its own RaisePropertyChanged() call), and loads SelectedPerson, just as scrolling the grid to the first row would do.

Add() creates a new Person record, assigns its key a new Guid value, adds it to both the dbContext (the mgr object) and to the people collection, and stores the new record (which is still blank, except for the ID) into the SelectedPerson object. That object is bound to the textboxes below the grid. The change in the collection triggers the mgr.HasChanges() binding, which turns Save and Cancel on and everything else off (see the AddCommand() calls, above.)

Delete() uses the fact that SelectedPerson is the row to be deleted. The code removes it from both the mgr.People collection and the people collection, saves the changes, and refreshes the RecordCount property.

Save() is the easiest thing you can do in Entity-Framework. Refreshing the RecordCount may not be necessary.

CanSave() is a delegate that determines whether buttons are enabled or disabled.

Cancel() is relatively complicated, and something of a surprise. One might expect a "RejectChanges()" method on the mgr object, but no. And calling LoadData() after canceling seems a bit extreme, especially if your data file has hundreds of thousands of records. We'll look for a better approach.

Close() violates the spirit of MVVM, but I don't give a damn. Ideological purity kind of creeps me out.

The ViewModel Base Class

The ViewModel Base Class is something I use in all of my apps. It provides the RaisePropertyChanged event, which must be called to tell your form that you changed a property's value in your ViewModel; and it provides the mechanism for "routed commands", which can be bound to buttons in your forms. Note that routed commands have two delegates: The first is what to execute when the button is clicked; the second enables/disables the button based on whatever criteria are appropriate. In the case of this application, if data has been changed, only Save and Cancel are enabled. Fortunately, there is a HasChanges() method on the dbContext (mine is called >mgr).

ViewModelBaseClass.cs appears in Listing 3, below. There is absolutely no need to read this code, unless you're curious. I don't know that reading it will answer any questions, but it's here for completeness.

Final remarks

The completed project looks like this:

Fig. 2 - The completed project

You can build little forms like this in an hour. If there are too many columns to fit below the grid, you can use a tabPage and put the controls for the details on tabPage 2. This model can be extended to support many common variations, like comboboxes populated from other tables, as well as checkboxes and other properties used to filter the main table, Try it with one of your own tables. Let me know what challenges come up. I'm always looking for new article ideas.

See 'ya.

Les

qqq


Copyright(C) Pinter Consulting, 2012Tel: +1 (650) 464-6924
Automated conversion between C# and VB by Visible C#/Visible VB from Tangible Software Solutions