Background
Is WPF ready for the real world? And the answer is hell yes! So, prove it!
These next few articles are my documentation of trying to write a real world line-of-business application using WPF! What is important to notice is that I only use the goodness provided by WPF and nothing else!!! Part 1 will lay the foundation for the future articles.
Why Write This Article, Isn't It Covered Already?
Probably, but what better way to learn than to play? On a more serious note, the only real reference application out there that I have seen is the DinnerNow application. DinnerNow is a perfect example of using all that .NET 3.0 has to offer, but it is a little over complicated. The purpose of these articles is to address some of the basics that can potentially be used to create a similar application like DinnerNow!!!
Disclaimer
This is a beginner article, but it assumes some knowledge of WPF, SQL Server, MVC and LINQ.
WPF Introduction
Advanced WPF
MVC
LINQ/SQL Server
WPF Bootcamp
Most LOB applications consume and manipulate data... So, let's start there!
Before We Begin...
OK, so this is obvious but create a new WPF application project using Visual Studio 2008!
Data
It is safe to assume that Microsoft SQL Server is a popular source of data. Download the AdventureworksLT SQL database and install it.
Creating a LINQ-to-SQL Mapping
Add a new item to the project using "LINQ to SQL Classes" template.
Open server explorer and add a data connection. Type in your server name and select AdventureWorksLT
as the database.
Once added, select all the tables and drag them on to the design surface. This will create all the needed entities (our model).
Model-View-Controller?
I am loosely following Josh's implementation of the MVC pattern, mine will more likely resemble Dr. WPF's MV-poo though.
Model
The domain-specific representation of the information on which the application operates.
Disclaimer
I am not a Software Architect and this article is not a design guide on how to architect an nTier or MVC application.
Although this is my quest to create a real world LOB application... for now, I am going to cheat a little. I am going to use my data sets returned by LINQ as my Model. What is nice about this approach is that it already implements the INotifyPropertyChanged
event! Before all the REAL architects start flaming me, future articles will address this.
Not to give away too much, but the future plan is to have an SQL DB -> LINQ-to-SQL -> WCF Service -> Model.
The main purpose of the Model is to represent the data in an easy to bind format!
About the Code
If you run the sample code provided, remember to change the connection string in the settings file. It currently uses this connection string:
Data Source=
localhost\sqlexpress;Initial Catalog=AdventureWorksLT;Integrated Security=True
View
Renders the model into a form suitable for interaction. This is where WPF really shines... I am creating various User Controls and binding them to the content of my window!
Communication between the view and model is via data binding and routed commands (via the controller).
Each view will be loaded into a new Window. I will use these attached properties to keep track of my open windows.
local:WindowViewState.IsManaged="True"
Sales Orders
Ok, so I don't have a Grant Hinkson in my back pocket... It's not beautiful but it is functional!!!
The purpose of this view is to allow a user to view/edit any existing sales order. Data like order date, due date, ship date, ship method, etc. can be changed. To change the customer details, a separate view will be used. Each customer has a list of addresses associated with it, the ship to and bill to addresses can only be changed to a valid address already in the list.
The following commands will also be supported: New, Delete and Update (to be implemented in future articles).
The sales order view is data bound to the SalesOrderHeaders
collection.
Customers
This view is used to view/edit customers. Details like first name, middle name, last name, etc. can be changed.
The customers view is data bound to the Customers
collection.
Products
This view is used to view/edit products. Details like name, part number, list price, etc. can be changed.
The products view is data bound to the Products
collection.
Controller (AKA Poo)
Processes responds to events, typically user actions, and may invoke changes on the model. The controller is basically the glue to hold together the model and the view.
The controller is used to interact with the model (i.e. add new sales order, delete a sales order, update a sales order).
I have created some routed commands for each action the view can perform. For more information on routed commands, read this article published by Josh Smith.
Binding Issue 1 of n
This article mainly focuses on data binding. Before we dig in, please read this article about what I have learned about binding... It will be used extensively in these articles!!!
Binding to a Normalized Database
Let's take the AdventureWorksLT
as a example. Each customer has a list of addresses associated with it. Each sales order has a customer. If I now want to create a data entry screen for a sales order, how do I bind it correctly so that I can change the delivery/billing address but still have my options constrained to the available addresses?
<ComboBox ItemsSource="
{Binding Path=Customer.CustomerAddresses, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
SelectedValue="{Binding Path=ShipToAddressID}" SelectedValuePath="AddressID" />
The ComboBox
fitted this perfectly, I first bind the ComboBox.ItemsSource
to all the available addresses (constrained by Customer
). All that is now left to do is tell the ComboBox
which item it should currently select. The SelectedValuePath
determines which property should be used to "identify" the current item and the SelectedValue
is then bound...
Examine this carefully... the ComboBox
is bound to Customer.CustomerAddresses
but the SelectedValue
is actually using the binding source up the visual tree!
This is a very common scenario in a normalized database...
BindingMode.TwoWay
The default binding mode is not always the same for different controls. It is always a safe practice to explicitly set the binding mode. In this scenario, we are going to use BindingMode.TwoWay
most of the time. We want the binding to update our model automatically!
Mode=TwoWay
Master/Detail Binding
A common scenario is a master/detail binding... If I can take the sales order example... I bind a ComboBox
to a list of open sales orders (Master
), once a sales order is selected, its details are showed in the details pane (Detail
).
Their are two common ways to do it:
Bind the details pane DataContext
to the ComboBox.SelectedItem
:
DataContext="{Binding Path=SelectedItem, ElementName=SalesOrdersListBox}"
Or use the {Binding Path=/}
syntax.
To use this binding, first set the IsSynchronizedWithCurrentItem="True"
and then bind the DataContext
as follows:
DataContext="{Binding Path=/}"
UpdateSourceTrigger.Explicit
I want to control when my data source is updated. More about this later...
UpdateSourceTrigger=Explicit
What Does WPF Not Have (Yet)
I am sure time was the only constraint here but there are two controls that WPF needs urgently to enter the LOB market...
DatePicker
Here I used the Marlon's AvalonControlsLibrary.(It is free and the full source code is supplied.)
DataGrid
Currently I am just using a ListView
, but I will soon upgrade to an Xceed DataGrid.(It is free.)
Future Issues to be Addressed
- Controller & routed commands (Providing New, Delete and Update)
- Validation
- Moving to a data service model and crossing the machine boundaries
This is all for part 1... If you found this useful, please vote for my article... any comments (good or bad) are very welcome!
Website