Change and Deleted Item Tracking within the Infragistics DataGrid

July 2, 2012

Now that I’m back developing production Line of Business (LOB) applications again, I wanted to share some techniques and challenges that I’ve overcome in our WPF UI.

Our company uses the Infragistics NetAdvantage for WPF product for our WPF UI. We have been very happy with the product, support response and updates. Currently we use the XamRibbon, and XamOutlookBar in our Prism shell; the XamDataChart in our Dashboard; and XamDataGrid in our data entry forms. Our application is in its infancy and as new requirements, features and capabilities are added we will be using more of the controls in the NetAdvantage for WPF full featured suite.

Introduction

For my first post, I thought I would cover an aspect of using the XamDataGrid (all data grids really) that is foundational to LOB applications.

What I’m referring to is tracking the users edits (inserts, updates and deletes) while working in a data grid and then providing the capability to utilize the same database connection to perform the updates, and optionally perform the updates as an atomic transaction.

There are use cases when a user changes data in a data grid, that those changes are immediately reflected back to the database.

Our scenario is one where the design requires that the user be able to make their edits and then commit all the changes all at once. An example of this would be a master-detail use case, when child rows are associated with a master record, e.g. customers, orders, order detail.

In this example, all other development concerns such as data validation, concurrency, actually reading from or writing to a database are not part of the example code; this enables the reader to focus on one technique for tracking inserts, updates, and deletes.

XamDataGrid Requirement

The Infragistics XamDataGrid requires that the data source implement IBindingList in order for the data grid to support adding rows.

Yes, there are techniques for dynamically adding data to your data source without having to do this, but I wanted to allow the data grid to perform its work in a natural way so our data source is derives from BindingList<T>.

Tracking Inserts, Updates and Deletes

When the user deletes a row in the XamDataGrid, the row is removed from the collection and is not longer visible in the UI.

This is where the below ChangeTrackingBindingList<T> comes into play. This class keeps track of the deleted rows by adding a deleted row to an internal collection and then exposing a method (GetDeletedItems) to return those deleted rows when required. If you look below, you’ll see I’ve overridden the RemoveItem method; the implementation adds the deleted row to the internal collection of deleted rows.

It also exposes a method (GetChangedItems) to return only those non-deleted rows that have been inserted or updated.

namespace GridEditing.Infrastructure {
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;

    /// <summary>
    /// Represents the ChangeTrackingBindingList that tracks changes and deleted items.
    /// </summary>
    /// <typeparam name="T">
    /// T: The type of object to provide change tracking binding list services for.
    /// </typeparam>
    public class ChangeTrackingBindingList<T> : BindingList<T> where T : ITrackDirtyEntity {

        IList<T> _deletedItems = new List<T>();

        /// <summary>
        /// Initializes a new instance of the <see cref="ChangeTrackingBindingList{T}"/> class.
        /// </summary>
        public ChangeTrackingBindingList() {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ChangeTrackingBindingList{T}"/> class.
        /// </summary>
        /// <param name="list">The list.</param>
        public ChangeTrackingBindingList(IList<T> list)
            : base(list) {
        }

        /// <summary>
        /// Gets all items in the collection.
        /// </summary>
        /// <returns>
        ///   <see cref="IEnumerable{T}"/> that contains all items from this collection; 
        ///   includes deleted and non-deleted items.
        /// </returns>
        public IEnumerable<T> GetAllItems() {
            return this.Union(_deletedItems).ToList();
        }

        /// <summary>
        /// Gets items that have been changed in the collection.
        /// </summary>
        /// <returns>
        ///   <see cref="IEnumerable{T}"/> that contains items that have been changed; 
        ///   does not include deleted items.
        /// </returns>
        public IEnumerable<T> GetChangedItems() {
            return this.Where(i => i.IsDirty).ToList();
        }

        /// <summary>
        /// Gets the deleted items.
        /// </summary>
        /// <returns>
        ///   <see cref="IEnumerable{T}"/> that contains all items deleted from this collection.
        /// </returns>
        public IEnumerable<T> GetDeletedItems() {
            return _deletedItems;
        }

        /// <summary>
        /// Clears the items.
        /// </summary>
        protected override void ClearItems() {
            base.ClearItems();
            _deletedItems = new List<T>();
        }

        /// <summary>
        /// Sets the item's MarkedAsDeleted property to <c>true</c>. 
        /// Adds the item to the DeletedItems collection. Removes item from list.
        /// </summary>
        /// <param name="index">The index.</param>
        protected override void RemoveItem(Int32 index) {
            var item = this[index];
            _deletedItems.Add(item);
            base.RemoveItem(index);
        }
    }
}

Consuming the ChangeTrackingBindingList<T>

The below MainWindowViewModel exposes the Customers collection. The collection is initialized and customers inserted in the constructor. (please do not populate your collections in your constructors, this is demo-only code)

You’ll notice that after loading the customers, I loop through the collection and set the IsDirty property to false. Your base class for your entity objects should handle this for you, so that when an object is populated from a database, the object is returned from the service layer in a non-dirty state to provide accurate tracking in the UI layer. The below example is over simplified on purpose to show how the view model will process the data once the user saves their changes.

The most important method below is the SaveExecute method that is invoked when the user clicks the Save button. The CanSaveExecute method determines if the collection has been changed or not. Any change to the collection will cause the Save button to be enabled.

Please Note: the code in the SaveExecute method would 99.9999% of the time actually be executing in a service layer. The service layer method would receive the ChangeTrackBindingList<T> as an argument and would use a data layer to process the changes.

Within the SaveExecute method we can see the workflow:

  • Deleted items are removed from the database.
  • Then the inserted or updated items are committed to the database.
  • The view model would then reload the inserted and changed data into the data grid. This reloading refreshing the timestamps that play the role in concurrency and identity columns are populated after an item is inserted and reloaded from the database.
namespace GridEditing {
  using System;
  using System.Diagnostics;
  using System.Linq;
  using System.Windows.Input;
  using GridEditing.Infrastructure;
  using GridEditing.Model;

  public class MainWindowViewModel : ObservableObject {
    ChangeTrackingBindingList<Customer> _customers;
    ICommand _saveCommand;
    Boolean _customersDirty;

    public ChangeTrackingBindingList<Customer> Customers {
      get { return _customers; }
      set {
        _customers = value;
        RaisePropertyChanged("Customers");
      }
    }

    public ICommand SaveCommand {
      get { return _saveCommand ?? (_saveCommand = new RelayCommand(SaveExecute, CanSaveExecute)); }
    }

    public MainWindowViewModel() {
      // load from the service layer
      var list = new ChangeTrackingBindingList<Customer>();
      list.Add(new Customer { FirstName = "Josh", LastName = "Smith", Id = 1 });
      list.Add(new Customer { FirstName = "Sacha", LastName = "Barber", Id = 2 });
      list.Add(new Customer { FirstName = "Brian", LastName = "Lagunas", Id = 3 });
      list.Add(new Customer { FirstName = "Karl", LastName = "Shifflett", Id = 4 });
      foreach (var customer in list) {
        customer.IsDirty = false;
      }
      this.Customers = list;
      this.Customers.ListChanged += (s, e) => _customersDirty = true;
    }

    Boolean CanSaveExecute() {
      return _customersDirty;
    }

    void SaveExecute() {
      // call the service layer, passing the DeleteMarkingBindingList<Customer>
      // the service layer will loop through each item, and either insert it, update it, delete it, 
      // or discard.
      // this is demo code only, but demonstrates the workflow

      foreach (var customer in this.Customers.GetDeletedItems()) {
        // if the Id property is zero then the service layer does not have to do anything
        // if the Id property is greater than zero, the service layer will delete the item.
        // simulate removing from the data base
        Debug.WriteLine(
          String.Format("Customer deleted: {0} {1}", customer.FirstName, customer.LastName));
      }

      foreach (var customer in this.Customers.GetChangedItems()) {
        if (customer.Id == 0) {
          // perform insert in service layer
          customer.Id = this.Customers.Max(c => c.Id) + 1;
          // simulate inserting into the data base
          Debug.WriteLine(
            String.Format("Customer inserted: {0} {1}", customer.FirstName, customer.LastName));
        } else {
          // perform update in service layer
          // simulate updating the data base
          Debug.WriteLine(
            String.Format("Customer updated: {0} {1}", customer.FirstName, customer.LastName));
        }
      }

      // reload the updated records from the database
      // simulate getting all records from the database

      _customersDirty = false;
    }
  }
}

Running the Application

When the application is initially spun up, the Save button will be disabled since no inserts, updates or deletes have taken place.

DisabledButton

After a row has been inserted, the Save button is enabled.

EnabledButton

To the initial data, the following changes have been made.

ChangesMade

When the Save button is clicked, notice the Debug output window. Each of the requested operations are performed; these operations are easily driven by the ChangeTrackingBindingList<T>.

DebugOutputWindow

After the Save is completed, the UI will be updated as below, the Id field has been populated and the Save button is disabled again.

UpdatedRecords

UI XAML – XamDataGrid Tips

The Infragistics XamDataGrid really makes it easy for developers to deliver a full-featured data grid editing experience. This example does not even scratch the surface of the many capabilities of the data grid. In future posts I’ll demonstrate some real-world scenarios with respect to the XamDataGrid’s UI capabilities, for now, let’s stick with tracking the users edits and updating the database.

Tips

1.  By default the XamDataGrid will set string properties to null if the user enters a blank or empty string in the data grid. This is not the behavior I want, instead I need an empty string. This is accomplished using a no-op converter.

Infragistics has made applying the no-op converter a snap by setting ValueToTextConverter property on the XamTextEditor using a style as I’ve done below. In the XamDataGridNullStringPreventionConverter’s Convert and ConvertBack methods I simply return value. The causes the XamDataGrid to set string properties to an empty string instead of the null value.

2.  Take a look down at the XamDataGrid.FieldSettings, you’ll notice CellClickAction has been set to EnterEditModeIfAllowed. The user no longer has to double click a cell to get into edit mode, simply clicking the cell, will put the cell in edit mode.

3.  Making an Id field read-only is straightforward. Have a look at the Id field in the XamDataGrid’s FieldLayout. By setting Id columns FieldSettings.AllowEdit to false, the cells are read-only.

<Window 
    xmlns:local="clr-namespace:GridEditing"
    xmlns:infrastructure="clr-namespace:GridEditing.Infrastructure"
    x:Class="GridEditing.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Data Grid Editing and Deleting" 
    Height="341" 
    Width="608" 
    ResizeMode="NoResize"
    xmlns:igDP="http://infragistics.com/DataPresenter" 
    xmlns:igEditors="http://infragistics.com/Editors">
  <Window.DataContext>
    <local:MainWindowViewModel />
  </Window.DataContext>
  <Window.Resources>
    <infrastructure:XamDataGridNullStringPreventionConverter 
      x:Key="XamDataGridNullStringPreventionConverter" />
  </Window.Resources>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <TextBlock Text="Infragistics Data Grid Editing and Deleting" Margin="11" FontSize="18" />
    <igDP:XamDataGrid 
      Grid.Row="1" Margin="11" 
      DataSource="{Binding Path=Customers}" IsNestedDataDisplayEnabled="False">
      <igDP:XamDataGrid.Resources>
        <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}" >
          <Setter Property="Margin" Value="0" />
        </Style>
        <!--this style is required to prevent null strings from being sent to the model-->
        <Style TargetType="{x:Type igEditors:XamTextEditor}" >
          <Setter 
            Property="ValueToTextConverter" 
            Value="{StaticResource XamDataGridNullStringPreventionConverter}" />
        </Style>
      </igDP:XamDataGrid.Resources>
      <igDP:XamDataGrid.FieldSettings>
        <igDP:FieldSettings CellClickAction="EnterEditModeIfAllowed" />
      </igDP:XamDataGrid.FieldSettings>
      <igDP:XamDataGrid.FieldLayoutSettings>
        <igDP:FieldLayoutSettings 
                    SupportDataErrorInfo="None"
                    DataErrorDisplayMode="None" 
                    AutoGenerateFields="False"
                    AddNewRecordLocation="OnBottom" 
                    AllowAddNew="True" 
                    AllowClipboardOperations="All" 
                    AllowDelete="True" 
                    ExpansionIndicatorDisplayMode="Never" />
      </igDP:XamDataGrid.FieldLayoutSettings>
      <igDP:XamDataGrid.FieldLayouts>
        <igDP:FieldLayout>
          <igDP:FieldLayout.SortedFields>
            <igDP:FieldSortDescription FieldName="Id" Direction="Ascending" />
          </igDP:FieldLayout.SortedFields>
          <igDP:Field Name="Id">
            <igDP:Field.Settings>
              <igDP:FieldSettings AllowEdit="False" />
            </igDP:Field.Settings>
          </igDP:Field>
          <igDP:Field Name="FirstName" Width="200" />
          <igDP:Field Name="LastName" Width="200" />
        </igDP:FieldLayout>
      </igDP:XamDataGrid.FieldLayouts>
    </igDP:XamDataGrid>

    <Button 
      Width="65" Margin="7" Grid.Row="2" Content="Save" 
      Command="{Binding Path=SaveCommand}" HorizontalAlignment="Right"/>

  </Grid>
</Window>

Download

After downloading change the extension from .zip.DOC to .zip. This is a requirement of WordPress.

Download Demo Application (25K)

Requirements

To run the demo application, you’ll need to get the Infragistics NetAdvantage for WPF. A demo version is available here.

Close

There are business frameworks like CSLA that have trackable objects and handle the above scenarios I’ve shown. Currently, I prefer to the simplest solution possible and use the above for tracking user changes within the XamDataGrid.

You can see the amount of very simple code to enable this scenario is small, easy to understand and implement.

Have a great day,

Just a grain of sand on the worlds beaches.


Boise Code Camp Prism, Ocean 3 Session

March 24, 2012

The links to the code presented at the Boise Code Camp is in the below Downloads section.

This links include Ocean 3 and a modified Prism 4 library that has two new features.

The Ocean 3 solution includes a Prism Ocean Demo application that shows off Ocean working with Prism. In a later release, I’ll add database demonstration code as well that uses the new and improved Service<T> and Repository<T>.

The demo application shows off:

  • WPF Ribbon being populated by a Prism Region; when forms are navigated to, their respective ribbon tabs are automatically rendered.
  • Automatic view/view model wiring up by the Unity container. No view or view model navigation registrations required.
  • Automatic setting of the correct IRegionManager on all view models; this region adapter does not draw the IRegionManager out of the IoC container, instead it uses the one assigned to the view. This technique prevents the incorrect IRegionManager being assigned in cases of child windows or scoped regions.
  • Demonstration of a child window hosting a form; that same form can be shown within the shell or child window requiring no form or view model modifications to function correctly; i.e. no “if in popup then do this code.”
  • When views are navigated away from, the currently focused control is cached; when that same instance of that form is navigated back to, focus will automatically be set to the correct control.
  • Queued dialog service for forms.
  • The view modal dialogs are asynchronous, by not blocking the UI thread, a form that has a dialog displayed, can be navigated away from and navigated back to.
  • When a view has a view modal dialog displayed, the views ribbon tabs are automatically disabled.
  • Ocean FormControl that provides many services for LOB forms.
  • Ocean FormNotificationControl that shows the form status from a validation perspective.
  • Ocean provides a comprehensive validation stack.
  • In addition, many small features are also shown.

I’ll release a new version of BBQShack soon, PrismBBQShack. This is a complete end-to-end store application that will show off all features of Ocean 3 (like the data stack) and will have a Win8, WinRT XAML touch-enabled point of sale application.

Downloads

After downloading, you MUST read the READ ME NOW.txt file in the Acme.Example solution.

After downloading, you must rename the to downloads from .zip.doc to .zip.  This is a requirement of Word Press.

Ocean 3

Modified Prism 4

To use the modified Prism 4 download, after unzipping, go to the \Prism\Prism4\ folder and run these two batch files:

  • Run Update Prism Binaries.bat
  • RegisterPrismBinaries.bat

They will rebuild the supplied modified Prism 4 and will register this version of Prism in the registry so that you can use the Add References dialog. This modified version of Prism will work with any current Prism application as no API’s were removed or changed, only added.

Have a great day,

Just a grain of sand on the worlds beaches.


Stuff – WPF Line of Business Using MVVM Video Tutorial

May 9, 2010

Stuff is an example application I wrote for WPF Line of Business Tour at Redmond event.  During some of the event sessions I used the code in Stuff to demonstrate topics I was teaching. 

During the event, we did not do an end-to-end examination of Stuff, that is the purpose of this blog post. 

Stuff

Stuff

In its current form, Stuff is a demo application that allows you to store information about the movies you own.  It uses the Netflix OData cloud database for movie look up, which makes adding a movie to your collection very easy.

One goal for Stuff was that a developer could take the code, open it in Visual Studio 2010 and press F5 and run the application.  I didn’t want burden developers with setting up a database, creating an account to use an on-line service or have to mess with connection strings, etc.

The Netflix OData query service made my goal achievable because it has a very low entry bar.

Stuff v2.  Shortly, I’ll modify Stuff by swapping out the remote data store layers and point it at Amazon’s web service so that I can not only look up movies but books, music and games too.  The reason I didn’t do this up front was because developers would have to go and get an Amazon account just to use and learn from the application. 

Another goal I had for Stuff was to limit referencing other assemblies or frameworks including my own.  All the code for Stuff is in the solution except the Prism EventAggregator.  I hope that trimming the code down to exactly what is being used in the application helps with the learning process.

There is a complete solution in both C# and VB.NET.

Model-View-ViewModel

You won’t see an individual video on MVVM below, because its not needed.  Instead, when appropriate I make mention of MVVM in the videos.

MVVM is a super simple pattern that describes a way to think about WPF and Silverlight applications.  When thinking in MVVM, we think in terms of a Model (our data), View (our UI) and ViewModel (traffic cop, abstraction of the View and adapter for the Model all rolled up into a single class). 

The Model, View and ViewModel typically communicate using the rich data binding stacks of WPF and Silverlight.

MVVM stands by itself and does not require an additional framework or special skills to use. 

I’ve seen developers investigating MVVM get discouraged when they see MVVM applications that take advantage of advanced features like IOC containers, service locators or MEF; in-effect lumping other programming concepts or API’s into MVVM.  If this happened to you, take a step back and get to know and understand MVVM for what it is, “a way to think about WPF and Silverlight applications.”

When writing WPF or Silverlight applications you may want to take advantage of an IOC container, service locator or MEF, but these are not required to use the MVVM pattern in your applications. 

As you gain experience in how you think about WPF and Silverlight application architecture and design patterns used to create these applications, you’ll find that the advance concepts are actually not that difficult and really do add value to your projects.

If MVVM is new to you, take it slowly, the light will come on.  For example, when you think about a Button, instead of thinking in terms of the Click event handler, you’ll think about data binding the Button’s Command property to an ICommand property on the ViewModel;  instead of thinking about addressing a control in the code-behind file by name, you’ll ask yourself, “what property do I need to expose on my ViewModel” that the UI control can data binding to.

MVVM is a very natural way to think about WPF and Silverlight applications, enjoy the learning journey and have fun.

Stuff Application Video Tutorial

Instead of writing a long blog post, I decided to tell the Stuff application’s story through a series of 14 short videos on different topics. 

I’ve tried real hard to keep each of the following videos short and focused.  The videos are all 1400 x 900.  To the get most from the videos, please read this very short blog post Karl’s Vimeo videos.

Introduction to the Stuff application

Video covers using the Stuff application and where Stuff stores your data locally.

http://www.vimeo.com/11582022

Solution and project structure

Video covers the how and why the solution and projects are structured the way they are.

http://www.vimeo.com/11582296

Introduction to OData

Video gives a short introduction to OData and querying OData sources.

http://www.vimeo.com/11583198

Abstracting the remote data store

Video explains how the Stuff application abstracts the remote data store for the purpose of allowing the concrete implementation to be easily changed in the future or for test purposes. 

http://www.vimeo.com/11453412

Business Entity Objects

Video explains the entity objects used in Stuff.  A short explanation of how I implemented searching the local Stuff data store is also provided.

http://www.vimeo.com/11584004

BusinessEntityBase and Validation

Video explains the BusinessEntityBase class and entity validation in Stuff.  Stuff uses the Data Annotations attributes to decorate class properties with validation rules.  This is a proof of concept that I wanted to try to see how far Data Annotations would take me.

http://www.vimeo.com/11585675

Service Container

Video explains the ServiceContainer (ServiceLocator) used in Stuff.  ServiceContainers provide a mechanism for resolving concrete types at run-time, design-time and test-time for the various services your application exposes.

http://www.vimeo.com/11455192

IDialogService

Video explains how simple it is to implement a dialog service that your ViewModels can consume.  The swapping out of the concrete implementation is also covered.

http://www.vimeo.com/11455208

IEventAggregatorService

Video introduces the Prism Event Aggregator and how Stuff implements an IEventAggregatorService.  Event Aggregator enables creating decoupled applications by disconnecting the initiating object (publisher) from the receiving object (subscriber).

http://www.vimeo.com/11585963

IMovieDataStoreService

Video covers the local data store used by Stuff to persist its data.  Additionally, it explains how local binary serialization and deserialization is used for loading and saving data and how to workaround a problem with binary deserialization in Expression Blend 3 and 4 that is caused by Blend not reloading its application domains between builds.

http://www.vimeo.com/11586513

VisualStateAssistant AttachedProperty

Video explains the VisualStateAssistant AttachedProperty that enables the changing of VisualStates in the UI from a ViewModel using data binding.

http://www.vimeo.com/11586881

Behaviors in Stuff

Video explains the Expression behaviors used in the Stuff application and how to wire them up.

http://www.vimeo.com/11587047

Design-time Visual Studio and Blend support

Video explains what I did to enabled a good design-time experience in the Stuff application in both Visual Studio and Expression Blend.  Video covers the design-time services and design-time data as well as some issues you need to deal with with each of the tools.

http://www.vimeo.com/11590320

Dispatcher, BackgroundWorker and WebClient

Video covers the use of the Dispatcher in the Stuff application and how I used posting to the Dispatcher to set focus to controls after UI transitions.  I also cover using the BackgroundWorker for performing background  search operations and using the WebClient to asynchronously download images.

http://www.vimeo.com/11590982

Downloads

If you have not installed Expression Blend 4, then your system will not have the Expression Blend SDK assemblies and you won’t be able to compile the solution. 

I have added a folder to the solution named, “Expression SDK Dlls.”  If you do not have Blend installed, you’ll need to update the references to the Blend SDK assemblies by removing the broken references and re-adding the reference to the dll in the above folder.

C# Source Code

VB.NET Source Code

Close

Hope you can learn just a little bit more about WPF Line of Business application programming from this example.

Have a great day,

Just a grain of sand on the worlds beaches.


Announcing events WPF for LOB & Windows 7 and .NET 4

March 14, 2010

Jaime Rodriguez and Karl Shifflett have created their best offering to-date; three days packed with WPF, optimized for building business applications and a tour of WPF for Windows 7 and .NET 4.

Event

This event is actually two events running back-to-back, "WPF for LOB" and "Windows 7 and .NET 4".

You can register for one or both events. If you want to attend both, you must register for both.

For this event we are adding an optional fourth day for those who want to spend a day programming, applying the information learned over the three days with your own code. We will have Microsoft Experts and MVP’s on hand to assist and pair program with you. This extra day provides you time to work at your own pace and ensure that you understand how to take advantage of the material presented over the previous three days.

Overview – WPF for LOB Event

This two day training is designed to teach developers how to create Line of Business (LOB) applications using Windows Presentation Foundation (WPF).

  • Day One Topics: WPF graphics subsystem, layout, element trees, resources, dependency properties, routed events, custom controls, templates and threading.
  • Day Two Topics: Data binding, commands, building applications using the Model-View-ViewModel pattern, creating testable applications, implementing data validation, and error handling and notification, view navigation and transitions.

After completion, attendees will have a solid understanding of WPF, its advantages and how to use the M-V-VM pattern to create great WPF LOB applications.

Overview – Windows 7 and .NET 4 Day Event

We will spend all day recapping the new WPF features in .NET4 and showing you how to take advantage of some of these new features:

  • Tooling Improvements
  • Graphics & Platform Improvements
  • Deployment Improvements
  • New features that light-up on Windows 7

Dates, Location, Registration

All events run from 8:30am – 5:30pm. Doors open at 8:00am.

Remember, to attend both events you must register for each event.

Registration Tip

If you have never filled out an MSDN event profile before, you’ll do so the first time you register.

If asked about being a partner, if you are not a partner or don’t know if you are:

  • When asked "are you registered" select No. 
  • Select "Visiting partner" under Partner Level.
  • Get creative on the Partner Type;  if in doubt, we are all "System builders"

Date

Event

Location

Registration Links

4/28–4/29

WPF for LOB

Microsoft Redmond Campus, Building 20

WPF for LOB Registration

4/30

Windows 7 and .NET 4

Microsoft Redmond Campus, Building 20

Windows 7 and .NET 4 Registration

5/1

Pair Programming

TBD – announced during training

None required

Detail Agenda

We will post the detailed agenda for the three training days around the 4/22/2010.

Logistics

Format
Instructor-led training from 8:30am – 5:30pm. 15 minute breaks every couple hours. 45 minutes lunch around mid-day.

Food
Breakfast, lunch and afternoon snacks are provided.

Cost
The training is FREE. You do need to register prior to the event, but there is no cost.

Registration is first-come-first serve, sign-up as early as possible to reserve your spot!

Cancellation
If you register and then learn you cannot attend the event, please take steps to cancel your reservations. Not cancelling could prevent another developer for attending the training. Thank you for your professionalism.

Instructors

About the instructors
Karl Shifflett is a software architect, former Microsoft MVP from Bellevue, Washington. He is currently working for Microsoft on the Cider Team as a Program Manager. He has been designing & developing business applications since 1989 and transitioned to .NET in March of 2003. In April of 2007 he joined the list of WPF and Microsoft Expression fanatics & evangelists. He is a member of Team Mole that delivered Mole Visualizer For Visual Studio to the world. He is the author to XAML Power Toys and loves WPF LOB. 

Karl’s blog is at http://karlshifflett.wordpress.com

Jaime Rodriguez is a Principal Technical Evangelist at Microsoft. He focuses on WPF and Silverlight.   For the last four years, he has helped a lot of enterprises and ISVs adopt WPF in large scale, mission critical, projects.

Jaime has been doing software development for fifteen years. Prior to Microsoft, he worked at Xerox, HP, Cerner and GeoAccess.  He joined Microsoft 9 years ago, he spent the first four years as an Enterprise Architect Consultant in Microsoft Services, and the last five he has been a client evangelist covering Windows Forms, WPF and Silverlight. 

Jaime’s blog is at http://blogs.msdn.com/jaimer

Have a great day,

Just a grain of sand on the worlds beaches.


MSDN WPF Samples Consolidated

March 2, 2010

The WPF Documentation Team has consolidated and published the MSDN WPF Samples here:

http://code.msdn.microsoft.com/wpfsamples

The page has nice preview pictures of each project along with a categorized table of contents.

If you are new to WPF or want to learn another aspect of WPF I recommend that you view the page and study the content.

Have a great day,

Just a grain of sand on the worlds beaches.


BBQ Shack – Ocean v2 for Visual Studio 2008

February 7, 2010

homepage What the Heck is BBQ Shack?

In September of 2009 I went on a cruise to Alaska with a simple goal of writing a WPF application that shared business objects and Ocean framework code with a Silverlight 3 project within the solution.  The WPF and Silverlight code sharing has since been made much easier.  Since Visual Studio 2010 Beta2, Silverlight 3 and 4 can easily share code with WPF;  I blogged how to do this here.

Before this cruise Jaime Rodriguez was encouraging me to quit writing line of business applications that looked and felt like Windows Forms applications.  He wanted me to start taking advantage of the capabilities of the WPF platform and not settle with an older UI paradigm.

Additionally, I felt challenged by the WPF work Billy Hollis did in 2008.  I strongly recommend that every WPF & Silverlight line of business application developer watch this challenging dnrTV video.

To my surprise, once I got aboard ship, I realized that two suite cases of Scope Creep got dropped off in my suite.  BBQ Shack began to grow into a medium size application requiring a lot of new Ocean framework features be coded on this cruise.

BBQ Shack was inspired by a real BBQ take-out, Outlaw BBQ Shack that is located in Matthews, NC.  If you are ever in the Charlotte, NC area you must visit the Outlaw BBQ Shack.  This small family owned business provided the BBQ for the Super Bowl XLI in 2007.

The BBQ Shack application story is; a software provider writes a WPF application that is initially used for manager functions, item maintenance and touch screen cash register software.  After a few months of use, the customer asks for a web presence.  So a Silverlight ordering application is deployed.  To save time and money, code reuse was critical.  (this is our scenario, not actual events in real life)

While the above story is just a scenario, I spent a good bit of time (and had fun) creating a data model that would allow me to have a very flexible way of maintaining inventory and understanding actual costs for products sold.

My goal for this blog post and the BBQ Shack application is to demonstrate the features in Ocean v2, while at the same time provide a real world MVVM line of business example.  Trying to reign in the scope, I did not write the Sales or Order Packing menu options nor the actual checkout screens.

I wrote 90% of BBQ Shack on the week long cruise, including a simple code generator for the business objects, business and data layer code.  After getting back from the cruise, I spent time with superstars Glenn Block and Jeff Handley.  They encouraged me to go back and make BBQ Shack IOC and test friendly.  Glenn and I also knocked around on a Friday evening for a few hours and wrote BLL(Of T) and DAL(Of T).  I refined that work and integrated it into BBQ Shack and Ocean v2.

In case you’re wondering, I used Expression Blend to create the above BBQ grill in XAML.

Table of Contents

Videos

The below links are to my Vimeo videos.  I have a short blog post here, that explains Vimeo and the features it provides you as a viewer.

I recommend that you view at least the first video before reading the blog post.  This will give you context and help your understanding of the application and Ocean API features introduced here.

BBQ Shack WPF version

BBQ Shack Silverlight version

Non-linear navigation deep dive

Ocean v2

I have created a home page for Ocean here.  Ocean v1, born on an Alaskan cruise in September of 2008 is the name for the framework I use for my application development.

Ocean v2 was created on my September 2009 Alaskan cruise along with the BBQ Shack.

See Ocean’s home page for more on Ocean v3.

BBQ Shack Features

I’m going to limit this blog post to the following feature list, otherwise I would never publish this work.  The videos at the bottom of the post will talk about every feature in BBQ Shack.

  • Non-linear application navigation with transitions
  • Task switcher
  • MVVM Modal & logging services
  • Master page concept forms
  • Background loading of all data
  • Business entity validation
  • BLL(Of T)
  • DAL(Of T)
  • Silverlight Code Sharing

While not a feature, Dan Dole a co-worker at Microsoft told me about a color pallet site called kuler.  I’ve long been told my UI’s don’t look so great.  I was trying to turn out a decent UI for BBQ Shack so I searched through the many color pallets at kuler.  BBQ Shack only uses a single pallet with 5 colors, along with black, white and a light gray.  Simple turned out to be best.

Non-Linear Application Navigation – View Manager Services

Billy Hollis used the phrase, “non-linear navigation” in the PDC presentation he did that was similar to the above dnrTV video.  The application he demonstrated was a product he wrote for his customer.  He spoke about the ability to hyperlink to any form or data record from anywhere in the application. 

Visualize Dashboard that presents a list of items that are low on inventory.  The ability to navigate directly to the item or another pertinent form is invaluable.  (yes sounds simple, keep reading)

Visualize some UI that presents the user a list of all active forms with the ability to navigate back to it from anywhere in the application.

WPF and Silverlight do not ship with baked in MDI support.  In my opinion this is good because it forces developers to design much better UI’s for their customers that don’t include countless little windows.

I also see non-linear navigation as a solution for end users that are required to multi-task to perform their job function.

BBQ Shack demonstrates non-linear navigation that allows a form to have a modal dialog displayed, slide that form out of the way to allow the user to perform another tasks and then easily navigate back to that task, just like users need to do in the real world.

BBQ Shack also provides 4 different animated form transitions that can be user selectable at run-time as part of a users profile if desired.

The below image pictures the list that is displayed when the user right-clicks on the circled “4” at the top of the Inventory button.  Clicking on one of the records will navigate the user directly to the form with that record.

listnavigation

When form is slide out of the way, it is not destroyed.  In fact it is running and can continue to run processes when its out of view.  This is one reason I listed background loading of all data in the features list.  If you process data access on the UI thread, you will severely limit your applications ability to provide multi-tasking features to your users and will also have issues with UI animation if the UI thread is also performing data access functions.  Background data access and processing solves this problem.

This non-linear, non-destructive feature really opens the possibilities for Silverlight 4 line of business applications.  Not forcing user to complete a form before being able to work on another form is a great UI feature.  This Silverlight feature will I ship with Ocean v3.1 for Visual Studio 2010 with Silverlight 4 support.  I need to wait until Silverlight 4 RTM’s before shipping any Silverlight 4 code.

The below image pictures the Inventory module with 4 active screens (4 includes the one pictured).  The user can easily see which forms are active with several options for getting to each one.  Notice in both the above and below images that the form state is displayed along with the record key and record title.

acitverecords

Consumers of business application software are experts in their vertical markets and are also very busy people.  Software that makes their job easier and transforms the computer from some sort of black box with a screen to a user friendly tool should be one of the goals of modern software developers.

The above search results have a unique feature.  The text of the result is a single click hyperlink to the record.  If the user clicks to the right of the text, that row will be selected and the Edit and Delete buttons will be enabled.

The below image shows a maintenance form with a modal dialog displayed.  The parent form is slightly grayed out while the application buttons at the bottom are not grayed out.  This allows for another application function to be carried out and then return to this form at a later time.   

modalform

Notice in the modal form the Item Base label is underlined.  This indicates that this is a hyperlink that when clicked will bring the Item Base Maintenance form into view, allowing that table to be modified.  When that Item Base form is closed, the application will come back to the above form in the same state it left it and will refresh the ComboBox data.

Notice the bread crumb trail under the form title of the above grayed out form.  This indicates to the user exactly where they are in the stack of forms.  If they were to click the Item Base hyperlink in the model window, the application would be navigated to the below form.  Notice the new breadcrumb and now the active forms goes from 4 to 5 in the Inventory button.

breadcrumb

If you look the above images, you’ll see several common UI patterns. 

Save and Close buttons and the Add, Edit and Delete buttons.  In the video Billy Hollis spoke to simplifying our UI’s.  I’ve always been a Toolbar developer until now.  I like these buttons and believe end users will too.

Also notice the top of every form is the same.  This is just one of the free features available by using the master page concept form custom control.  See the below section for coverage of this feature.

How Does Non-Linear Navigation Work?

A single ViewManagerService exists for each application and is accessed through the IViewMangagerService Interface.  The ViewManagerService creates and controls one or more ViewManagers.  ViewManagers can only be accessed through the ViewManagerService.  The BBQ Shack has two ViewManagers, one for each Window in the application, ApplicationMainWindow and CashierView.

Accessing the ViewManagerService through the Interface enables IOC scenarios for object construction and makes unit/integration testing of the ViewModels very easy since mock objects can be injected.

Each ViewManager is responsible for keeping track of each form transitioned into and out of view and it also provides the hook for animating forms in and out of view.

The below ApplicaitonMainWindow.xaml has been stripped down to the bare essential to illustrate navigation concepts.

The Grid named applicationLayoutRoot is the Panel where all non-linear navigation will take place.

<Grid>

    <Grid.RowDefinitions...>

    <!-- ViewServiceManger loads/transistions all views here-->
    <Grid Grid.Row="1" x:Name="applicationLayoutRoot" />

</Grid>

The below code fragments are from the above ApplicationMainWindow.xaml code behind file and show how the BBQ Shack navigation is setup and how the application starts up.

The constructor is where the Grid applicationLayoutRoot is registered. The Add method creates a new ViewManager, assigns the Panel to it and provides the ability to hook into the form transitioning process.  This hook also enables platform specific transitions and for developers to create their own.  As you can see below I have provided four transitions for WPF applications with this version of Ocean.

Since the BBQ Shack is a navigation driven application, I have elected to have the Views create most of my ViewModels in the application as you can see in the last line of the constructor.  This is not a rule, it just made sense to me and simplified my application.

Private _objIViewManagerService As IViewManagerService = ViewManagerService.CreateInstance

Public Sub New()
    
    InitializeComponent()    
    
    _objIViewManagerService.Add(Me.applicationLayoutRoot, AddressOf AnimateTransition)
    Me.DataContext = New ApplicationMainViewModel
End Sub

Private Sub AnimateTransition( _
    ByVal objCurrentView As FrameworkElement, ByVal objNewView As FrameworkElement)
    
    'TODO - developers - you can add a user preferences check here to select a different animation 
    '       or no animation
    '
    'Call one of the below built in transitions or write your own.
    '
    'Transistions.CreateInstance.NoTransition(Me, objCurrentView, objNewView)
    'Transistions.CreateInstance.DoublePaneRightToLeftTransition(Me, objCurrentView, objNewView)
    Transistions.CreateInstance.SinglePaneRightToLeftTransition(Me, objCurrentView, objNewView)
    'Transistions.CreateInstance.FadeTransition(Me, objCurrentView, objNewView)
End Sub

Private Sub ApplicationMainWindow_Loaded( _
    ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded    
    
    _objIViewManagerService.Select.Transition(Of HomeView)(My.Settings.HomeView, Nothing, _
                                                            strApplicationSuite:="BBQ Shack")
End Sub

All navigation functions are accessed through the IViewManagerService; this service manages the individual ViewManagers.  The Add method creates new ViewManagers.  The Select method returns a ViewManager.  The ShowWindow method provides a way for an application to launch a new window.  This allows your ViewModel’s to display a new window, either modal or non-modal.  Since the ViewManagerService will be mocked during unit/integration testing, the window will not actually open a new UI.

 iviewmanagerservice

The ViewManager Transition method has the following capabilities:

  • Creates a new instance of a View if required
  • Can pass a primary key to the View; that View’s ViewModel will then look that record up for display
  • Can bring a previously open View into view
  • Can pass a DataContext to a View
  • Automatically handles multiple instances of the same form.  For example, the user could have two separate Person Maintenance forms adding new records and a third form editing a Person record
  • Meets one of my requirements which was to have the navigation work in a single line of code

Transition is the most used method of the ViewManager; it has five possible method signatures that are listed below.

Public Sub Transition(ByVal objNavigateKey As NavigateKey)

Public Sub Transition(Of T As {FrameworkElement, New})(ByVal strViewKey As String)

Public Sub Transition(Of T As {FrameworkElement, New})(ByVal objNavigateKey As NavigateKey, _
                                                       ByVal objDataContext As Object)

Public Sub Transition(Of T As {FrameworkElement, New})( _
            ByVal strViewKey As String, ByVal objDataContext As Object, _
            Optional ByVal objRecordPrimaryKey As Object = Nothing, _
            Optional ByVal strApplicationSuite As String = "")

Public Sub Transition(Of T As {FrameworkElement, New})(ByVal objNavigateKey As NavigateKey)

Before looking at each of the above overloads we need to understand the NavigateKey and its roll.

navigatekey

Each View managed by the ViewManager has a unique NavigateKey associated with it. When a new View is transitioned to a NavigateKey is created if a new one is not passed. 

The ParentNavigateKey is used by the ViewManager to navigate back to the View that opened a new View when the new View is closed.

If the RecordPrimaryKey is assigned, this can be used by the View/ViewModel to retrieve data and display it.

The Version property enables having multiple instance of the same View in the same state.  For example, the user opened the Person Maintenance View and is adding a record.  They then navigate to another form that allows them to open the Person Maintenance View again and add another record.  A customer service person could easily find themselves needing to do this.

When adding a new record the View’s RecordPrimaryKey will be null.  The Version is a simple Integer, that is supplied by the IViewManagerService NextVersion property.  Look back at the above IViewManagerService class diagram to see the NextVersion property.

The ViewKey is an application assigned string value.  I have put all my ViewKeys in the application settings.  You could also put them in a constants file.  Having constants ensures that your application won’t be a victim of a spelling mistake.

Transition Timing Cycle

When the Transition method is called, the following actions occur in the order specified:

  • Check if the requested View has been created, if not it will be created
  • Check if the requested View is the current view, if so stop further process
  • If the requested View is not contained in the registered Panel, add it to the Panel
  • Iterate all Views in the registered Panel and if not collapsed, set Visibility to Collapsed
  • Set the requested View’s Visibility to Visible
  • If the View implements INotifyOnBringIntoView, then call the OnBringIntoView method
  • If the View’s DataContext implements INotifyOnBringIntoView, then call the OnBringIntoView method
  • If the current View implements INotifyOnHidingView, then call the OnHidingView method
  • If the current View’s DataContext implements INotifyOnHidingView, then call the OnHidingView method
  • Set the current View’s ZIndexProperty to 0
  • Set the requested View’s ZIndexProperty to 99
  • Set the requested View’s Opacity to 1
  • Take a snapshot image of the requested View (used by the Task Switcher)
  • Invoke the animation callback that was registered, if no callback was registered then collapse the current View
  • Set the requested View as the current View
  • If the NavigateKey has a primary key value and the DataContext passed in the Transition method implements ILoadable then call the LoadRecord method
  • If the requested View was created and the requested View’s DataContext is implements ILoadable then call the LoadRecord method
  • If the NavigateKey has a primary key value and the View implements ILoadable then call the LoadRecord method
  • If a DataContext was passed in the Transition method, the assign that DataContext to the requested View.

The above is why I like the non-linear navigation API.  It handles so many actions that I would otherwise have to program and simplifies bringing a form into view down to a single line of code.

I also like the ability for Views and ViewModels to sign up for Load, UnLoad and LoadRecord notifications by implementing an Interface and overriding a method instead of responding to events and writing event handlers.

I’m hoping others will be inspired to create even better API’s and black box form management for WPF and Silverlight.  I’ve only got about 4 hours invested in design and coding this API.  It does meet my needs, but I’m sure there are other scenarios I have not taken into consideration.  I welcome all feedback and discussion on this topic.

Let’s have a look at each overload of the Transition method and see how it works and is used in BBQ Shack.

Transition Signature One

Public Sub Transition(ByVal objNavigateKey As NavigateKey)

The above method signature is use to navigate to an existing View, typically from a list of active views.  If the NavigateKey is null or not found an exception is thrown.

_objIViewManagerService.Select.Transition(CType(param, ActiveView).NavigateKey)

The above line of code can be found in 8 locations in the BBQ Shack code where active Views are listed and the user can navigate to them by clicking on their hyperlink.  If you look above to the second image in this post, you’ll see a list of Views, when one is clicked the above code is executed.  The third image shows a list of active item records.  When the Lays chips item is clicked, the above line of code will run.

The ActiveView type is used to encapsulate an active view in lists.  The ActiveView type also provides a DataContext property that UI DataTemplates can consume to display data when the list is rendered.

Transition Signature Two

Public Sub Transition(Of T As {FrameworkElement, New})(ByVal strViewKey As String)

The above method signature is use to navigate to a View that does not perform record maintenance.  Examples of these types of forms are the the HomeView and Cashier MealSelectorView.  There will never be more than one instance of Views loaded using this method signature since there is no Version number or RecordPrimaryKey to differentiate the Views.

_objIViewManagerService.Select.Transition(Of HomeView)(Settings.HomeView)

_objIViewManagerService.Select(STR_CASHIER).Transition(Of MealSelectorView) _
                                (My.Settings.MealSelectorView)

The first line of the above code should now be familiar.

The second line is the same with a twist.  Notice Select(STR_CASHIER).  STR_CASHIER is a constant that is used to tell the ViewManagerService to use the ViewManager identified by the key that has a value returned by the constant STR_CASHIER.

Transition Signature Three

Public Sub Transition(Of T As {FrameworkElement, New})(ByVal objNavigateKey As NavigateKey, _
                                                       ByVal objDataContext As Object)

The above method signature is not used by the BBQ Shack.  However, the other Transition methods call this method internally in the ViewManager, except the first signature that does not need the services provided by this method.  If you study the code in this method signature and the PerformTransition method, you’ll have a full understanding of how the ViewManager provides its services.

Transition Signature Four

Public Sub Transition(Of T As {FrameworkElement, New})( _
            ByVal strViewKey As String, _
            ByVal objDataContext As Object, _
            Optional ByVal objRecordPrimaryKey As Object = Nothing, _
            Optional ByVal strApplicationSuite As String = "")

The above method signature is used twice in the BBQ Shack, both are listed below. 

_objIViewManagerService.Select.Transition(Of HomeView)( _
        Settings.HomeView, Nothing, strApplicationSuite:="BBQ Shack")

_objIViewManagerService.Select.Transition(Of InventoryView)( _
    Settings.InventoryMenuView, Nothing, strApplicationSuite:="Inventory")

It’s primary purpose is to load up a View and assign that View to an ApplicationSuite.

Transition Signature Five

Public Sub Transition(Of T As {FrameworkElement, New})(ByVal objNavigateKey As NavigateKey)

The above method signature is the most commonly found in the BBQ Shack.  This is used when you need open a View and create a new record, or navigate to a View and edit an existing record.

_objIViewManagerService.Select.Transition(Of ItemCategoryView)(_ 
    New NavigateKey(_objIViewManagerService.NextVersion, Settings.ItemCategoryView, _
                    Me.NavigateKey, Settings.Inventory))

The above code demonstrates navigating to a new instance of the ItemCategoryView and setting the Version property to the value returned by the NextVersion property.  This code is run when the Add button is clicked.

Notice the ParentNavigateKey is set to the NavigateKey property of the current ViewModel.

Settings.Inventory sets the application suite this View belongs to.

_objIViewManagerService.Select.Transition(Of UnitOfMeasureView)( _
    New NavigateKey(Settings.UnitOfMeasureView, _
                    CType(param, uom_UnitOfMeasure_FillSelector).uom_UnitOfMeasureIdent, _
                    New NavigateKey(Settings.InventoryMenuView), Settings.Inventory))

The above code demonstrates navigating to the UnitOfMeasureView, instructing the View/ViewModel to load the record with a primary key value equal to value of uom_UnitOfMeasureIdent.  This code is run when a record is selected from the search results listing (see third image above.)

The ViewModel that is calling this code does not have direct access to its Parent’s Parent NavigateKey, so I simply pass a new NavigateKey that will point to the Inventory main View (see third image above).

Settings.Inventory sets the application suite this View belongs to.

Injecting View Manager Services

The below two constructors from the UnitOfMeasureViewModel demonstrate how the BBQ Shack utilizes a default object construction used by the application at run-time, while at the same time exposing the ability to use IOC for object construction or inject mock objects for unit/integration testing.

This ability exists because only Interface types are used.  The first constructor does create concrete objects but those are immediately passed to the second constructor and only the Interface type is used.

Even if you are not using IOC, I would still recommend using this pattern in your MVVM applications to easily enable unit/integration testing.  Additionally, programming to an interface gives you long term flexibility and decoupling that later on you may wish you had.  Cost to do this up front is super cheap as opposed to changing a lot of code later on. 

''' <summary>
''' Default object construction, creates a default BLL and DAL
''' </summary>
Public Sub New()
    Me.New(ViewManagerService.CreateInstance, _
           ViewModelUIService.CreateInstance, _
           BLL(Of uom_UnitOfMeasure).CreateInstance)
End Sub

''' <summary>
''' Used for unit testing or if you want to swap out the default concrete BLL or DAL
''' The DAL is swapped out by passing in a BLL that has a swapped out DAL.
''' </summary>
''' <param name="objIBLL">IBLL(Of uom_UnitOfMeasure)</param>
Public Sub New(ByVal objIViewManagerService As IViewManagerService, _
               ByVal objIViewModelUIService As IViewModelUIService, _
               ByVal objIBLL As IBLL(Of uom_UnitOfMeasure))

    MyBase.New(objIViewModelUIService)
    _objIViewManagerService = objIViewManagerService
    _objIBLLUnitOfMeasure = objIBLL

End Sub

Task Switcher

taskswitcher

I originally released the Task Switch in this blog post and demonstrated it in this application blog post.  One of the readers of my blog asked me to port this code to work with MVVM applications.  When I added the View Manager Services to Ocean this was real easy to accomplish.

The BBQ Shack ApplicationMainWindow listens for the CTRL+TAB key combination and will display the above Task Switcher.  Users can click on the desired task to switch to or they can also keep pressing CTRL+TAB to cycle through active tasks and release the CTRL key to navigate to the selected form.

Try to make application navigation as easy as possible for your users.  They will appreciate your innovation and extra efforts in this space.

The XAML for the Task Switcher is pictured below.  As you can see its stacked on top of the applicationLayoutRoot, centered within Grid.Row 1.

<!-- ViewServiceManger loads/transitions all views here-->
<Grid Grid.Row="1" x:Name="applicationLayoutRoot" />

<local:TaskSwitcherControl 
    Grid.Row="1" x:Name="ucTaskSwitcherControl" HorizontalAlignment="Center" 
    VerticalAlignment="Center" Visibility="Collapsed">
    
    <local:TaskSwitcherControl.BitmapEffect>
        <OuterGlowBitmapEffect GlowSize="8" GlowColor="#FF5A5A5A" />
    </local:TaskSwitcherControl.BitmapEffect>
    
</local:TaskSwitcherControl> 

MVVM Modal and Logging Services – View Model UI Services

Opening a modal dialog window using Window.ShowModal() in a ViewModel will send most seasoned MVVM developers into a rage.  (OK, I might have overstate their reaction, but not by much).

So what’s the MVVM developer to do?  Logically they know they have to put up error dialogs, file open, file save dialogs and folder selection dialogs.  But they also understand that using these dialogs will foul up unit/integration tests or make that task much more complex than it needs to be.

I’ve read several good solutions by fellow developers.  The one I present here works for me, is fully testable and as an added benefit I’ve added logging capabilities to the every dialog.

This service is managed just like the View Manager Services.  The service is passed as an Interface into the constructor of types that need its services.  See the code in the above section, Injecting View Manager Services for an example.

The ViewModelUIService provides a rich set of methods to display message dialogs, file and folder dialogs and to log messages.

The message dialogs are all wrappers around the Task Dialog custom control I wrote back in 2007.  The latest version is in Ocean v2.

 viewmodeluiservice

The Task Dialog custom control mimics the Windows Vista Task Dialog appearance even on Windows XP.  It has the same capabilities and can show the same icons and test regions at the Task Dialog.  Until the .NET Framework provides a wrapper for the Task Dialog, this is a very good option.

 deletedialogbox

Did you notice the Log event in the above class diagram?

If you want to enable the logging of dialogs, in application startup, add a handler for the Log event.  Logging user responses to dialogs in applications is invaluable.  The LogMessage method allows adding a log message without a dialog.

Private Sub Application_Startup(ByVal sender As Object, _
                                ByVal e As System.Windows.StartupEventArgs) Handles Me.Startup
    '
    'TODO - developers - set up logging event handler
    'AddHandler _objIViewModelUIService.Log, AddressOf WriteLogToFile-Database-EventLog-Email-etc
    '
End Sub

For unit/integration testing application startup would not normally run.  Instead, you can add this line of code to your test code to test logging.

The below line of code displays the delete confirmation dialog pictured above.  The (3) is the number of seconds the user will have to wait until they can click the Yes or No buttons.  While waiting, a ProgressBar shows the remaining time.

If _objIViewModelUIService.YesNoConfirmDelete(3) = CustomDialogResult.Yes Then

The below line of code demonstrates showing the end user an exception message.  The idea was to make common programming tasks very easy.  The below ExceptionDialog method creates a dialog with the correct dialog caption, buttons and message.  As a bonus, if the application is a debug build, the Stack Trace is placed in the Additional Text collapsible region of the dialog.

_objIViewModelUIService.ExceptionDialog(e.Error)

Master Page Concept Control

ASP.NET has had master pages since .NET 1.1 (Paul Wilson’s custom control for .NET 1.1. .NET 2.0 added them in the box).

Developers are always looking for ways to simplify the coding of their applications and reduce the need to repeat boiler maker code to a minimum.  Using a master page paradigm also makes it much easier to refactor and change the general appearance of your application without having to actually touch the individual forms.

The below XAML fragment is from BBQShackFormResourceDictionary.xaml and is the BBQShackForm custom control that acts as a master page control.

I wrote about the below AdornerDecorator that wraps the form in this blog post.  Basically, this is required when a form puts up UI Elements in the WPF Adorner layer and that UI is then taken out of view and then brought back into view.  Without this you’ll end up with turds in your adorner layer.

This custom control provides the nice inner and outer borders, form titles, breadcrumb trail and form background. 

<AdornerDecorator>
    <Border x:Name="bdr" Style="{StaticResource formOuterBorderStyle}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" MinHeight="400" />
            </Grid.RowDefinitions>
            <Border Style="{StaticResource formTitleBorderStyle}">
                <StackPanel>
                    <TextBlock Text="{Binding Path=ViewModelFriendlyName}" 
                               Style="{StaticResource formTitleStyle}" />
                    
                    <TextBlock Margin="11,0,0,0" Text="{Binding Path=BreadcrumbTrail}" 
                               Style="{StaticResource formBreadcrumbStyle}" />
                </StackPanel>
            </Border>
            <Rectangle Grid.Row="1" Style="{StaticResource formBackgroundRectangleStyle}" />

            <!--Put the form below here-->
            <ContentPresenter Grid.Row="1" />    
            <!--Put the form above here-->

        </Grid>
    </Border>
</AdornerDecorator>

The below XAML fragment is from UnitOfMeasureView.xaml and is the Unit of Measure maintenance form.

UnitOfMeasureView is a UserControl that derives from EditFormViewBase.  This UserControl’s content is the above BBQShackForm custom control.  The custom control’s content is contained in the below Grid.  See the below image that boxes out the regions on a form.

<Ocean_MVVM:EditFormViewBase ...>
    <local:BBQShackForm>
        <Grid Margin="11">
            <Grid KeyboardNavigation.TabNavigation="Cycle">
                <Grid.RowDefinitions ...>
                <Grid.ColumnDefinitions ...>

                <local:FormStatusUserControl 
                    Grid.ColumnSpan="2" 
                    DateCreated="{Binding Path=uom_UnitOfMeasure.uom_DateCreated}" 
                    DateModified="{Binding Path=uom_UnitOfMeasure.uom_DateModified}" 
                    CreatedBy="{Binding Path=uom_UnitOfMeasure.uom_CreatedBy}" 
                    ModifiedBy="{Binding Path=uom_UnitOfMeasure.uom_ModifiedBy}" 
                    ErrorHeaderText="{Binding Path=ErrorHeaderText}" 
                    WatermarkMessage="{Binding Path=WatermarkMessage}" 
                    IDataErrorInfoError="{Binding Path=uom_UnitOfMeasure.Error}" 
                    ErrorMessage="{Binding Path=ErrorMessage}" 
                    UIValidationErrorMessages="{Binding Path=UIValidationErrorMessages}" />

                <Label Grid.Column="0" Grid.Row="1" Content="Id" />
                <Label Grid.Column="0" Grid.Row="2" Content="Title" />

                <TextBlock Grid.Column="1" Grid.Row="1" 
                           Text="{Binding Path=uom_UnitOfMeasure.uom_UnitOfMeasureIdent}" ... />
                <TextBox x:Name="txtuom_Title" Grid.Column="1" Grid.Row="2" Width="75" 
                         Text="{Binding Path=uom_UnitOfMeasure.uom_Title}” MaxLength="15"/>

                <Grid Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2" 
                      Grid.IsSharedSizeScope="True" HorizontalAlignment="Right">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition SharedSizeGroup="Buttons" />
                        <ColumnDefinition SharedSizeGroup="Buttons" />
                    </Grid.ColumnDefinitions>
                    <Button Command="{Binding Path=SaveCommand}" Content="Save" ... />
                    <Button Command="{Binding Path=CloseCommand}" Content="Close" ... />
                </Grid>
            </Grid>
        </Grid>
    </local:BBQShackForm>
</Ocean_MVVM:EditFormViewBase>

The FormStatusUserControl provides the FormValidationControl and the two icons on the right side of each form.

I could have added the FormStatusUserControl to the BBQShackForm master page control but chose not to.  At the time I wrote it, I wasn’t sure if I would have boxed myself in, so I kept them separated.  In my next application I may move this to the master page control.

The below image shows a box diagram of the form.  The white box shows the BBQShackForm’s area and how the Grid contents as nested within the BBQShackForm.  Notice the lack of form coloring in the above code.  The BBQShackForm control takes care of all form colors.  This is why I use the master page concept, so that most of my application layout structure and coloring is in one location.  It would be super easy for a developer to change the colors or application layout structure of an application that uses this pattern.

customcontrol

The FormStatusUserControl contains a FormNotification control and two image buttons.

The FormNotification control is similar to the ASP.NET ValidationSummary control.  If the form is valid, the WatermarkMessage is displayed as in the above image, Valid record.  Any errors reported by the Model or ViewModel are listed in the expanded view as pictured below.  The FormNotification can also display other messages like, Record Saved after a successful write to the data store.

This form validation and error reporting is free, and is part of the Ocean framework.  Go back and look at the above XAML.  You’ll notice the FormStatusUserControl has a number of properties that are data bound to the Model and ViewModel.  It is that easy.

 requireditems

The below blue “A” image button opens a form that allows a case correction rule to be added.  The end user scenario is, the user is editing a form and the current case correction rules have made an unwanted correction.  Without interrupting the users workflow or train of thought, they can click the “A”, enter the new rule and go right back to their task.  Try it, you’ll like it.

The below properties icon provides a Tooltip that displays the current record metadata.  I’ve always added the below four fields to all of my database records.  But most users don’t need to see this information on every form, so an inhanced Tooltip made the most sense.

 recordmetadata

Background Loading and Processing of All Data

Windows Forms, WPF and Silverlight UI’s are all single threaded apartments (STA).  The bottom line for you is that you can only access the UI on the same thread that created it.  This means that you can’t spawn an additional thread and have that thread interact directly with the UI.

Side note:  I’ve been thinking about a black box that would make UI programming easier and that would take full advantage of the multiple cores of the host machine without the programmer having to write additional code, manage threads or background workers. 

There has to be a way to get rid of the UI bottleneck so my 8 cores can all spring into action.  I need to go on another cruise!

Microsoft added the Systetem.ComponentModel.BackgroundWorker to the .NET 2.0 framework.  This class encapsulates spawning new threads, optionally getting progress reports and the final results of the work.

BBQ Shack uses the BackgroundWorker for all data access.  By accessing all data off the UI thread, the UI remains response and all animations can run smoothly.  Let me give you an example; in BBQ Shack when you click on a record in the search results, the current form is animated out of view, the new form is animated into view and at the same time the new form is making a call to the database to retrieve the required data.  If this was all accomplished on one thread, BBQ Shack’s performance and usability would be severely degraded.

If you have not programmed with Silverlight, you may not know that all data access must be on a background thread.  If you are planning to write “cloud hosted” applications, you’ll want all your cloud access  performed on a background thread.

If you have not checked out .NET 4.0 Parallel Programming do this today.  This fun and ground breaking computing.

The below code snippet is from the UnitOfMeasureViewModel.vb file.  It shows the minimum code required to use the BackgroundWorker.

LoadRecord is a method on my ViewModel base class.  When this method is called and a primary key of the correct type is passed in, the BackgroundWorker.RunWorkerAsync method is called.  This method will then raise the BackgroundWorker.DoWork event on another thread.  When DoWork has completed its task or an unhandled exception occurs, the BackgroundWorker.RunWorkCompleted event is raised back on the UI thread.  It is that simple, just more code needs to be authored and maintained.  (now you know why I want to create a black box to do this for me)

 Private WithEvents _objBackgroundWorker As New BackgroundWorker

 Public Overrides Sub LoadRecord(ByVal objRecordKey As Object)

     If objRecordKey Is Nothing Then
         Me.uom_UnitOfMeasure = _objIBLLUnitOfMeasure.CreateEntity

     ElseIf Not TypeOf objRecordKey Is Integer Then
         Throw New ArgumentNullException("objRecordKey", AppResources.RecordKeyWrongType)

     Else
         _objLoadKey = objRecordKey
         _objBackgroundWorker.RunWorkerAsync(objRecordKey)
     End If

 End Sub 

Private Sub _objBackgroundWorker_DoWork( _
         ByVal sender As Object, _
         ByVal e As System.ComponentModel.DoWorkEventArgs) _
             Handles _objBackgroundWorker.DoWork

     e.Result = _objIBLLUnitOfMeasure.Select(CType(e.Argument, Integer))
 End Sub

 Private Sub _objBackgroundWorker_RunWorkerCompleted( _
         ByVal sender As Object, _
         ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
             Handles _objBackgroundWorker.RunWorkerCompleted

     If e.Error Is Nothing Then

         If e.Result IsNot Nothing Then
             Me.uom_UnitOfMeasure = CType(e.Result, uom_UnitOfMeasure)

         Else
             _objIViewModelUIService.MessageDialog("Data Error", "Record Not Found", ...) 
         End If

     Else
         _objIViewModelUIService.ExceptionDialog(e.Error)
     End If

 End Sub

Notice the very first line of code in the above RunWorkerCompleted method.  You must check the value of e.Error before doing anything else.  Any exceptions from the other thread will be in the Error property.

If no exceptions were thrown on the background worker thread, you can then check the value of e.Result and consume the result.  Remember RunWorkerCompleted is executed back on the UI thread.

Business Entity Validation

I’ve written about business entity validation here and here.  Most of the content in this Code Project article are applicable to Ocean as they have the same API’s.

All of the business entity objects in BBQ Shack are shared by the WPF & Silverlight projects.  All of the validation code is platform agnostic.

One of Ocean’s goals was to enable cross platform business entity objects.  That goal has been achieved.

This works in Silverlight because when I create the Service References, I choose to reused types instead of having the proxy generated code re-implement my types.  I strongly recommend that you never allow the proxy generated code re-implement your types, always reuses your types.

I have trimmed the below code snippet for purposes of illustration.

This c_Customer class is from the c_Customer.gen.vb file.  The .gen. is my naming convention for files that are code generated.  The c_Cusomter type is complete with the c_Customer.vb partial class.  I love that partial classes allow me to add code to types that are code generated.

I’ve used this naming convention in all business applications since my days with SQL Server 7.0.  Essentially it provides a unique name for each object in the database, including columns.  That uniqueness is inherited in my code. 

While it looks strange at first, it makes it SUPER easy to search for all instance of the Customer Email field in my database, including stored procedures, all .NET code, XAML or HTML files.

Behind delivering customer features, code maintenance is my #1 priority.  This naming convention has worked for me accomplish that goal.  You should do that works for you and your team.

<DataContract(Namespace:="Ocean")> _
Partial Public Class c_Customer
    Inherits BusinessEntityBase

    <DataMember(IsRequired:=True)> _
    <CompareValueValidator(ComparisonType.Equal, 0, RequiredEntry.Yes, RuleSet:=STR_INSERT)> _
    <CompareValueValidator(ComparisonType.GreaterThan, 0, _
                           RequiredEntry.Yes, RuleSet:=STR_UPDATE_DELETE)> _
    Public Property c_CustomerIdent() As Integer
        Get
            Return _intc_CustomerIdent
        End Get
        Set(ByVal Value As Integer)
            MyBase.SetPropertyValue("c_CustomerIdent", _intc_CustomerIdent, Value)
        End Set
    End Property

    <DataMember(IsRequired:=True)> _
    <CharacterCasingFormatting(CharacterCasing.ProperName)> _
    <StringLengthValidator(1, 20)> _
    Public Property c_FirstName() As String
        Get
            Return _strc_FirstName
        End Get
        Set(ByVal Value As String)
            MyBase.SetPropertyValue("c_FirstName", _strc_FirstName, Value)
        End Set
    End Property

    <DataMember(IsRequired:=True)> _
    <CharacterCasingFormatting(CharacterCasing.LowerCase)> _
    <RegularExpressionValidator(RegularExpressionPatternType.Email, RequiredEntry.Yes)> _
    Public Property c_Email() As String
        Get
            Return _strc_Email
        End Get
        Set(ByVal Value As String)
            MyBase.SetPropertyValue("c_Email", _strc_Email, Value)
        End Set
    End Property

In BBQ Shack I have chosen to use opt-in data serialization as evidenced by the DataContract and DataMember attributes.

It took me awhile to warm up to the DataContractSerializer as opposed to the default opt-out serialization added to .NET 3.5.  I’m now using the opt-in DataContract serialization for all my WCF applications.

BusinessEntityBase

All my business entity objects derive from BusinessEntityBase.  This class provides the plumbing necessary for deriving objects to have  property and object validation,  case correction of entity values, change notification, validation interfaces implementation and other features.

The MyBase.SetPropertyValue method provides the entry point to have data validated, case correction applied and change notification.

Applying Validation Rules

Validation rules can be applied as attributes to properties, in code as shared rules or in code as an instance rule.  I like using the attributes because they are super easy to code generate, the rule is physically near the property so that when I read a business entity’s code I can “see” everything related to this property.

There are .NET restrictions as to data an attribute can contain.  For example you can put a function in an attribute so creating a rule that states that the date entered must be before Date.Now() is not permitted.  This is where the ability to add a rule in code comes into play.

For a great example of this open the BBQ Shack file, ccc_CharacterCasingCheck.vb located in the BBQShack.BusinessEntityObject project.  Now expand the Methods region.  In the AddSharedValidationRules method, not only did I add a business rule, but pointed that rule to some code I wrote that is specific to this class and will never be used again.

Ocean provides a rich set of lines of business validation rules you can use.  However, if you need your own, writing them is very easy.

  • BankRoutingNumberRule
  • ComparePropertyRule
  • CompareValueRule
  • CreditCardNumberRule
  • DomainRule
  • InRangeRule
  • NotNullRule
  • RegularExpressionRule
  • StateAbbreviationRule
  • StringLengthRule

I have also designed the validation attributes to accept both Decimal and Date values in the attribute.  Normally you can’t do this because values of these types can’t be used in an attribute.  For one example of this look at the Sale_Item.vb file in the BBQShack.BusinessEntityObjects project.

Shared validation rules are rules that all instances of a type will use.  The first time a type is created, all shared rules are added to a dictionary for the type so you only pay the cost to parse the attributes once.

Instance validation rules are added each time an instance of an object is created.  There is an AddInstanceValidationRules method in BusinessEntityBase that deriving types can override and provide required implementation.

I strongly recommend that you look at every business entity class.  You’ll find many real world examples of business rules being applied.

Rule Set

In the above code, the first validation attribute has the RuleSet property set to STR_INSERT.  This rule will only be applied when the business object is in insert mode; the mode of the object is set by assigning the BusinessEntityBase.RuleSet property.

Apply Case Correction Rules

In the above code you’ll also notice the CharacterCasingFormatting attribute in use.  This attribute adds a shared rule that gets applied by the BusinessEntityBase when a property value is set.  The following rules are provided by Ocean.

  • None
  • LowerCase
  • OutlookPhoneNoProperName
  • OutlookPhoneProperName
  • OutlookPhoneUpper
  • ProperName
  • UpperCase

The case correction API also provides hooks for developers to inject their own application specific case correction rules.  BBQ Shack provides a UI for end users to do just that.

IBLL(Of T)  c# – IBLL<T>

Superstar Glenn Block and I were in one of our code jam sessions and the idea to get rid of boiler maker business logic layer and data layer code came up.  I had originally written BBQ Shack with concrete BLL and DAL types for each business object.  That is one standard way to code these layers that many developers use.  I like it because its very easy to code generate and extend later as required.

Glenn and I cranked up a new Unit Test project, using TDD wrote IBLL(Of T) and IDAL(Of T).  I spend a few evenings of refactoring, creating concrete types and changed BBQ Shack over to use these.  Bottom line was I deleted to directories of files.  Part of the refactoring was injecting the concrete implementation of IBLL(Of T) into my ViewModels.  The testing of my ViewModels can be against a database or with mock objects.  Like the View Manager Services the ViewModels can be created by IOC too.

The main purpose of IBLL(Of T) was to relieve the developer from coding the most common business and data layer functions that deal with data store operations (CRUD).  These would be consumed by other developer written business or data layer code.

Real World Example:  When a new Customer is added, the business layer must initiate adding 3 other records and updating 2 more.  The ViewModel would call a single method in a developer written business layer method.  That method would utilize the IBLL(Of T) methods when reading and writing to the data store.  In this example, the developer only had to code the “real” business logic and not boiler maker code.

Just a quick glance at the below Interface will show the basic methods most BLL’s have today.  This is one of the four Interfaces that make up the generic BLL API.

Public Interface IBLL(Of T As {IBusinessEntity, Class, New})

  Function CreateEntity() As T

  Function Delete(ByVal intKey As Integer, ByVal objTimeStamp As Byte(), _
                    Optional ByVal strProcedureName As String = "") As Integer

  Function Delete(ByVal strKey As String, ByVal objTimeStamp As Byte(), _
                    Optional ByVal strProcedureName As String = "") As Integer

  Function Delete(Of K)(ByVal Key As K, ByVal objTimeStamp As Byte(), _
                          Optional ByVal strParameterName As String = "", _
                          Optional ByVal strProcedureName As String = "") As Integer

  Function Insert(ByVal Entity As T, ByRef intNewIdent As Integer, _
                    Optional ByVal strProcedureName As String = "") As Integer

  Function Insert(ByVal Entity As T, Optional ByVal strProcedureName As String = "") As Integer

  Function [Select](ByVal intKey As Integer, Optional ByVal strProcedureName As String = "") As T

  Function [Select](ByVal strKey As String, Optional ByVal strProcedureName As String = "") As T

  Function [Select](Of K)(ByVal Key As K, Optional ByVal strParameterName As String = "", _
                            Optional ByVal strProcedureName As String = "") As T

  Function Update(ByVal Entity As T, Optional ByVal strProcedureName As String = "") As Integer

End Interface

The BLL(Of T) and DAL(Of T) by default use a naming convention to call the correct stored procedure for database operations and to property name the parameters in the stored procedure calls.  In the case where a specific or unconventional name is required, the Optional ProcedureName parameter can be supplied.

Let’s take a look at the IBLL(Of T).Update method being called in a ViewModel.

Notice that the business object IsValid method is being called before Update is called.  If the IsValid method returns false, the ErrorHeaderText property is set to Save Errors. 

I always call the IsValid method in every layer of my code.  Doing this in the UI, ViewModel, BLL and DAL ensures that an invalid object never gets consumed.  Individual layers should be programmed defensively and never trust other layers (developers; LOL). 

Private Function Update() As Integer

    Try
        Me.uom_UnitOfMeasure.uom_DateModified = Now
        Me.uom_UnitOfMeasure.uom_ModifiedBy = Data.UserName

        If Me.uom_UnitOfMeasure.IsValid Then

            If _objIBLLUnitOfMeasure.Update(Me.uom_UnitOfMeasure) = 1 Then
                Return 1
            End If

        End If

        Me.ErrorHeaderText = "Save Errors"

    Catch ex As Exception
        _objIViewModelUIService.ExceptionDialog(ex)
    End Try

    Return 0
End Function

If you are new to WPF the purpose of setting the above ErrorHeaderText property or how it works may not be obvious. 

If you look back to the above XAML for the UnitOfMeasureView UserControl, you’ll notice that the ErrorHeaderText property on the FormNotification control is data bound to the DataContext’s ErrorHeaderText property.  This property has built-in change notification, so that when the value is assigned in a ViewModel, that value is automatically consumed by the UI.

This is one of programming paradigm differences between WPF and other UI technologies.  In WPF or Silverlight we set properties in data that is bound to the UI.  In other UI technologies, we would normally set a property on a control in the UI.  The WPF pattern is much easier to program and when you adopt MVVM is much easier to unit/integration test.

The actual call to the business layer is done through the Interface and is a single line of code, wrapped in a Try Catch block.  Any errors are reported to the end user and are logged by the method call to the View Model UI Service in the Catch block.

Ocean provides concrete implementations for each of the business and data layer Interfaces that make up the IBLL(Of T) and IDAL(Of T) story.  These concrete implementations are coded the way I would program my layers.  You can easily rewrite them to comply with how you do things, or you can just inject your own concrete implementations of the Interfaces.  You are not locked into doing things the way I do, not at all.

Below is the default concrete implementation of the BLL(Of T) provided by Ocean.  DAL is a private property of type IDAL(Of T).

Public Function Update( _
    ByVal Entity As T, _
    Optional ByVal strProcedureName As String = "") As Integer Implements IBLL(Of T).Update

    Return DAL.Update(Entity, strProcedureName)
End Function

There are many very solid and wildly accepted reasons for layering your applications. One is that it enables the swapping out of data layers without having to change your application Views or ViewModels.

If I wanted to move my data store from a local SQL Server database to Azure or to SQL Server Compact or to a WCF Service, I would only have to provide a different concrete implementation of IDAL(Of T) and recompile. 

Heck, you could even just make that a config setting, have your IOC pick it up and with the flick of a user setting, change the data store.  Now you really understand why programming asynchronous data access up front is a must; it provides your applications the flexibility they need to meet the ever changing technologies we work with.

IDAL(Of T)   c#- IDAL<T>

IDAL(Of T) is the lower application layer that IBLL(Of T) consumes to perform data store operations.  The UI or ViewModels would never call methods on IDAL(Of T) directly.

IDAL(Of T) has the same Interface methods as IBLL(Of T) so I won’t repeat that code.  There are a total of four matching Interfaces exposed by IDAL that are consumed by IBLL.

The concrete implementation of IDAL(Of T) provided by Ocean is programmed against SQL Server 2008.  To target other data stores all that is required is a new concrete implementation of IDAL(Of T).

The hardest piece of the puzzle to solve was the creation of the stored procedure calls.  Proper ADO.NET parameters must be created, in the case of BBQ Shack and the provided concrete DAL implementation I had to create SQLParameters.  This requires knowledge of the business objects data store characteristics.

In BBQ Shack, I used an attribute on the business object properties to provide the required metadata.  These were easy to code generate and the action of parsing the attribute only has to be done once.  There you go, that is the trade off.  Add a code generated attribute and I get to delete two directories of files that I’ll never have to maintain going forward. 

The below code snippet is from the uom_UnitOfMeasure.vb file in the BBQShack.BusinessEntityObjects project.

The DataColumn attribute provides all the required metadata for the concrete implementation of DAL(Of T) to create SQLParameters or other types of parameters.  DataColumn has other properties and overloads that are not used below.

With a quick glace at the below code snippet and you can easily determine the data store characteristics of each property. 

Did you notice the lack of Booleans being used in any of my attributes.  Spending a few extra minutes to create an Enum pays off big time with respect to code readability.

<DataColumn(ColumnType.Timestamp)> _
<NotNullValidator(RuleSet:=STR_UPDATE_DELETE)> _
Public Property uom_Timestamp() As Byte()
    Get
        Return _objuom_Timestamp
    End Get
    Set(ByVal Value As Byte())
        MyBase.SetPropertyValue("uom_Timestamp", _objuom_Timestamp, Value)
    End Set
End Property

<DataColumn(ColumnType.VarChar, 15)> _
<CharacterCasingFormatting(CharacterCasing.LowerCase)> _
<StringLengthValidator(1, 15)> _
Public Property uom_Title() As String
    Get
        Return _struom_Title
    End Get
    Set(ByVal Value As String)
        MyBase.SetPropertyValue("uom_Title", _struom_Title, Value)
    End Set
End Property

<DataColumn(ColumnType.Int, PrimaryKey.Yes, Identity.Yes)> _
<CompareValueValidator(ComparisonType.Equal, 0, _
                       RequiredEntry.Yes, RuleSet:=STR_INSERT)> _
<CompareValueValidator(ComparisonType.GreaterThan, 0, _
                       RequiredEntry.Yes, RuleSet:=STR_UPDATE_DELETE)> _
Public Property uom_UnitOfMeasureIdent() As Integer
    Get
        Return _intuom_UnitOfMeasureIdent
    End Get
    Set(ByVal Value As Integer)
        MyBase.SetPropertyValue("uom_UnitOfMeasureIdent", _
                                _intuom_UnitOfMeasureIdent, Value)
    End Set
End Property

Ocean provides its own data stack contained in the DataAccess class.  DataAccess provides many services like connection string management, connection management, functions with overloads for accessing ADO.NET data methods, concurrency violation notification and index violation notification.

The below concrete implementation of the Update method is from the DAL.vb file in the OceanFrameworkGenericLayers project.

It first performs standard sanity checks, then builds the parameters and finally calls the ExecuteNonQuery method in Ocean’s DataAccess class.

I had thought about caching the created parameters and then just update them, but in the end, this was easier and performance was not affected.

Public Function Update( _
    ByVal obj As T, Optional ByVal strProcedureName As String = "") _
        As Integer Implements IDAL(Of T).Update

    If obj Is Nothing Then
        Throw New ArgumentNullException("obj", "Business object was null")
    End If

    If obj.ActiveRuleSet <> STR_UPDATE Then
        Throw New InvalidOperationException( _
            String.Format("{0} using wrong ActiveRuleSet for update.  Current setting: {1}.", _
                      Me.EntityName, obj.ActiveRuleSet))
    End If

    If Not obj.HasBeenValidated Then
        Throw New InvalidOperationException( _
            String.Format("{0} has not been validated for update.", _
                    Me, EntityName))
    End If

    Dim da As DataAccess = GetDataAccess()
    Dim params As New List(Of SqlParameter)

    For Each kvp As KeyValuePair(Of String, DataColumn) In _
            SharedDataColumnsManager.GetManager(Me.EntityType).DataColumnDictionary.ToList

        If kvp.Value.IsDateCreated OrElse kvp.Value.IsCreatedBy Then
            Continue For
        End If

        If kvp.Value.Nullable Then

            Dim objValue As Object = obj.GetType.GetProperty(kvp.Key).GetValue(obj, Nothing)

            If objValue Is Nothing Then
                params.Add(BuildSQLParameter( _
                           kvp.Value.ParameterName, _
                           GetSqlDataType(kvp.Value.ColumnType), _
                           ParameterDirection.Input, DBNull.Value))

            Else
                params.Add(BuildSQLParameter( _
                           kvp.Value.ParameterName, _
                           GetSqlDataType(kvp.Value.ColumnType), _
                           ParameterDirection.Input, objValue))
            End If

        ElseIf kvp.Value.MaximumLength > 0 Then
            params.Add(BuildSQLParameter( _
                       kvp.Value.ParameterName, _
                       GetSqlDataType(kvp.Value.ColumnType), _
                       kvp.Value.MaximumLength, ParameterDirection.Input, _
                       obj.GetType.GetProperty(kvp.Key).GetValue(obj, Nothing)))

        Else
            params.Add(BuildSQLParameter( _
                       kvp.Value.ParameterName, _
                       GetSqlDataType(kvp.Value.ColumnType), _
                       ParameterDirection.Input, _
                       obj.GetType.GetProperty(kvp.Key).GetValue(obj, Nothing)))
        End If

    Next

    Return da.ExecuteNonQuery( _
            CommandType.StoredProcedure, _
            GetStoredProcedureName(Me.UpdatestrProcedureName, strProcedureName), _
            params.ToArray)

End Function

I  have not used IBLL(Of T) or IDAL(Of T) in a production application.  BBQ Shack is the first application. 

What I like about it is that all my CRUD business and data layer implementation is its basically in two files instead of boiler maker code repeated in many files.

I know, jury is still out, but I wanted to share the concept with you.

Silverlight Code Sharing

If you look at the BBQShack.BusinessEntityObjects.Silverlight project you’ll see that all the files are linked to files in the BBQShack.BusinessEntityObjects project.

Now for the real benefit of code sharing with Silverlight.

Look at the Service Reference in the BBQShack.Silverlight project pictured below.  Notice that I provided specific hints for type reuse. 

By doing this, the proxy wizard did not re-implement my business objects and I can now take full advantage of all the code in my business objects.

servicereference

I have written about Silverlight code sharing in this blog post.

System Requirements

  • Visual Studio 2008 with SP1  (I have not tried BBQ Shack on Visual Studio 2010)
  • Silverlight 3 Developer Version (not Silverlight 3 Beta, not Silverlight 4 Beta)
  • Note:  If you cannot create a Silverlight application in Visual Studio, you do not have the Silverlight Developer version installed.
  • SQL Server 2008 or SQL Server 2008 Express (select install from the 3rd or 4th column)

Downloads

Sky Drive BBQ Shack Source Code, Database and Ocean v2  (opens page where three zip files are located.)

You must download all three of the zip files.  The BBQShack and Ocean zip files should be unziped in the same folder, like your \Projects folder.  After unzipping the folders will look like this:

…\Projects\BBQShack

…\Projects\Ocean

Doing this will ensure that the Ocean projects can be found by the BBQ Shack solution.

After unzipping the above download zip files, you will have these folders in your projects folder.

folders

Database Installation

The above BBQ Shack download includes the SQL Server database .mdf and .ldf files.

If you do not have SQL Server Express 2008 download it from this page.  Select the install from the 3rd or 4th column of the download page.

If you have a SQL Server 2008 you must “attach” the database to an instance of SQL Server 2008.

If you have SQL Server Express you either can either “attach” the database to an instance of SQL Server Express or you can use the file attachment method in your connection string.

Next you must change the connection string in the BBQShack project and the BBQShack.Silverlight.Web project. 

To do this double click on the “My Properties” icon below the project name in the Solution Explorer.  Select the Settings tab and edit the connection string “Data Source” property of the connection string.  You MUST do do this for BOTH projects.

Close

BBQ Shack was a fun adventure that took me places (including Alaska) I never thought I would go.  I hope that you can learn from the code and that some of the innovation in this project provides inspiration and possibly adoption.

Have a great day,

Just a grain of sand on the worlds beaches.


WPF & Silverlight Designer Team – New Blog

January 18, 2010

BlueCiderLogo

The WPF & Silverlight Designer Team (Cider Team) has just launched a new team blog at: http://blogs.msdn.com/wpfsldesigner/default.aspx

During the months of January and February 2010 we will be adding a good bit of WPF & Silverlight content. 

This initial content will focus on the Designer toolset and walkthroughs using the Designer as well as some posts for control authors.

You can leave feedback for the Cider Team on the blog or you can send comments and questions using Twitter and include #wpfdesigner or #sldesigner in your tweets.

Have a great day,

Just a grain of sand on the worlds beaches.


Follow

Get every new post delivered to your Inbox.

Join 248 other followers