This is part 2 in a series of demos to accompany an East Bay .Net User's Group project
. While the User's Group project takes a "software-as-service" approach, our series will focus primarily on the presentation layer, applying WPF to a set of non-trivial interface requirements, while paying special attention to the need for clean, maintainable, extensible code. The project consists of an OrderEntry application, used to generate and track walk-in and phone-in customer orders, with a separate OrderMonitor application to display open pizza orders to the kitchen staff. We are NOT trying to suggest that this particular UI (or the use of WPF, for that matter), would represent an appropriate solution for a similar set of real-world business requirements
, but it enables us to discuss many real-world project concerns while exercising a broad range of WPF features.
- PizzaManiac 1 : Simplistic WPF layout to support basic application structure and behavior
- PizzaManiac 2 : Realistic feature set, real-time WCF communications, and some UI customization
- PizzaManiac 3 : Significant increase in complexity, to support multiple product types
- PizzaManiac 4 : Robust communications and data management, .Net 3.5 updates, and a more polished UI
The application now supports a much more realistic level of user interactivity, uses WCF to communicate between the OrderEntry and OrderMonitor clients in real-time, and makes some initial progress toward a more aesthetically pleasing UI.
Explicit "Add/Edit/Delete" buttons in the OrderEntry client have been eliminated, in favor of support for clicking directly on list elements, with each element able to summarize a large amount of order status information.
The OrderMonitor marks orders that have been cancelled (or edited) with a red "Cancelled" overlay, and displays them at 50% opacity. Both clients use colored borders to highlight overdue orders (yellow as a warning, and red for those which are already late, with time limits currently set to one and two minutes, for demo purposes).
Controls in the Add/Edit view present a much more customized appearance compared with the earlier version
of our project. New features include the ability to schedule an order, and to assign delivery to an authorized driver after receiving notification (from OrderMonitor) that all pizzas in the current order have been processed. Marking an order "complete" causes it to become locked, preventing any further changes.
Note that pizza options and pricing, processing time limits, and other aspects of the application can be configured through .xml files located in the output directory.
Basic User's Guide
- Launch "SimpleServiceHost.exe" in the _Output\Debug directory
- Launch OrderEntryUI.exe and OrderMonitorUI.exe
- In the OrderEntry client, click the "Add New Order" button, add at least one pizza, and click "Save Changes"
- To simulate that a Pizza is finished cooking, click on the Pizza in the OrderMonitor client, and click the round "X" button
- To edit an existing order, or to assign delivery or mark an order completed, click on the order in the OrderEntry client.
- Scheduled orders are started automatically, when the current time is within 30 minutes of the scheduled time for that order.
- Changes to orders already in progress will cause the original order to be cancelled, and a new order to be started.
UI Implementation Notes
The "Add New Order" button is an ordinary Button placed above a transparent ListBox, with list items and the button sharing the same underlying visual style, to make the button appear to be part of the list.
Order and Pizza lists have been customized simply by placing them on a styled background, while the more complex requirements of the TabControl required actually editing a local copy of the associated ControlTemplate.
The "Expected" order completion field is actually a Windows Forms "DateTimePicker" control. We must temporarily HIDE the interop host each time we display the Add/Edit view, since embedded Windows Forms controls are unable to participate in any animation performed by their WPF container.
Preferring markup to code wherever possible throughout the presentation layer greatly simplifies our implementation, but requires continuous refactoring to keep the Xaml clean and maintainable. We continue to separate color definitions from the code which references them, but it is usually easier to keep Style definitions in the same file with the code which references them, until a large volume of inter-related styles starts to interfere with our ability to read the associated layout, and makes it more convenient to move the styles into their own ResourceDictionary. Grouping files using prefixes such as "convert...", "res...", and "view..." makes it easier to navigate the source, especially after you've been away from the project for awhile.
One interesting use of ValueConverter in the current implementation is to auto-size the Item/Size/Price column widths to fill the available grid area.
Remote Communications Implementation Notes
To handle communication between the clients, we are currently relying on simple "OneWay" messaging with callbacks, over WCF named pipes, with two services hosted inside a single console app., "SimpleServiceHost.exe", which must be launched manually before starting either of the UI clients. We will probably transition to use WAS extensions to IIS, and replace LINQ queries to XML files with data pulled from an actual database in a future iteration of our project.
Although there's no benefit in a project this small, we've followed recommended WCF best practices in separating data contracts from service contracts and service implementations, and we've taken the additional step of creating a separate "ServiceAbstraction" class to isolate proxy references from WPF presentation code in the client.
One very convenient (and unexpected) technique involves EXTENDING local data references generated via the "Add Service Reference" option, enabling each WPF client to create bindings to whatever interpretation of a "Pizza" (for example) is most convenient to meet its own specific display requirements. Note that we can define constructors for our extended classes in the OrderEntry client, but must override deserialization to extend classes in OrderMonitor, because OrderMonitor just receives Pizzas which were instantiated in the OrderEntry application.Generating a local service reference involves a few extra steps in the OrderEntry client...
- Launch SimpleServiceHost.exe manually (NOT from inside Visual Studio's debug environment)
- Right-click on the client project, select "Add Service Reference", and enter information shown in the dialogs below
- Set the "Show All Files" option for the project, and change the two "Collection" references to "ObservableCollection" in the file "Service References\OrderProcessing\Reference.svmap\References.cs"
- Note that, in order to set a breakpoint inside our extended objects, we must first comment-out the attribute "System.Diagnotics.DebuggerStepThroughAttribute" for that object, in References.cs.
Even with our attention to code maintenance concerns, the volume of files in our projects is starting to interfere with our ability to navigate the source. We'll need to address this in our next iteration, especially with the increased complexity that will come when we add support for order items other than just Pizzas.
Other projects by Andy L.