Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

BBQ Shack – Ocean v2 for Visual Studio 2008

, 7 Feb 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
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 ea

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.

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.

<span style="color:blue;"><</span><span style="color:#a31515;">Grid</span><span style="color:blue;">>

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.

<span style="color:blue;">Private </span>_objIViewManagerService <span style="color:blue;">As </span>IViewManagerService = ViewManagerService.CreateInstance

<span style="color:blue;">Public Sub New</span>()
    <span style="color:green;">
    </span>InitializeComponent()    <span style="color:green;">

    </span>_objIViewManagerService.Add(<span style="color:blue;">Me</span>.applicationLayoutRoot, <span style="color:blue;">AddressOf </span>AnimateTransition)
    <span style="color:blue;">Me</span>.DataContext = <span style="color:blue;">New </span>ApplicationMainViewModel
<span style="color:blue;">End Sub

Private Sub </span>AnimateTransition( _
    <span style="color:blue;">ByVal </span>objCurrentView <span style="color:blue;">As </span>FrameworkElement, <span style="color:blue;">ByVal </span>objNewView <span style="color:blue;">As </span>FrameworkElement)

    <span style="color:green;">'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)
    </span>Transistions.CreateInstance.SinglePaneRightToLeftTransition(<span style="color:blue;">Me</span>, objCurrentView, objNewView)
    <span style="color:green;">'Transistions.CreateInstance.FadeTransition(Me, objCurrentView, objNewView)
</span><span style="color:blue;">End Sub

Private Sub </span>ApplicationMainWindow_Loaded( _
    <span style="color:blue;">ByVal </span>sender <span style="color:blue;">As Object</span>, <span style="color:blue;">ByVal </span>e <span style="color:blue;">As </span>System.Windows.RoutedEventArgs) <span style="color:blue;">Handles Me</span>.Loaded    

    _objIViewManagerService.Select.Transition(<span style="color:blue;">Of </span>HomeView)(<span style="color:blue;">My</span>.Settings.HomeView, <span style="color:blue;">Nothing</span>, _
                                                            strApplicationSuite:=<span style="color:#a31515;">"BBQ Shack"</span>)
<span style="color:blue;">End Sub</span>

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.

<span style="color:blue;">Public Sub </span>Transition(<span style="color:blue;">ByVal </span>objNavigateKey <span style="color:blue;">As </span>NavigateKey)
<span style="color:blue;">
Public Sub </span>Transition(<span style="color:blue;">Of </span>T <span style="color:blue;">As </span>{FrameworkElement, <span style="color:blue;">New</span>})(<span style="color:blue;">ByVal </span>strViewKey <span style="color:blue;">As String</span>)
<span style="color:blue;">
Public Sub </span>Transition(<span style="color:blue;">Of </span>T <span style="color:blue;">As </span>{FrameworkElement, <span style="color:blue;">New</span>})(<span style="color:blue;">ByVal </span>objNavigateKey <span style="color:blue;">As </span>NavigateKey, _
                                                       <span style="color:blue;">ByVal </span>objDataContext <span style="color:blue;">As Object</span>)
<span style="color:blue;">
Public Sub </span>Transition(<span style="color:blue;">Of </span>T <span style="color:blue;">As </span>{FrameworkElement, <span style="color:blue;">New</span>})( _
            <span style="color:blue;">ByVal </span>strViewKey <span style="color:blue;">As String</span>, <span style="color:blue;">ByVal </span>objDataContext <span style="color:blue;">As Object</span>, _
            <span style="color:blue;">Optional ByVal </span>objRecordPrimaryKey <span style="color:blue;">As Object </span>= <span style="color:blue;">Nothing</span>, _
            <span style="color:blue;">Optional ByVal </span>strApplicationSuite <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>)
<span style="color:blue;">
Public Sub </span>Transition(<span style="color:blue;">Of </span>T <span style="color:blue;">As </span>{FrameworkElement, <span style="color:blue;">New</span>})(<span style="color:blue;">ByVal </span>objNavigateKey <span style="color:blue;">As </span>NavigateKey)<span style="color:blue;">
</span>

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

<span style="color:blue;">Public Sub </span>Transition(<span style="color:blue;">ByVal </span>objNavigateKey <span style="color:blue;">As </span>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(<span style="color:blue;">CType</span>(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

<span style="color:blue;">Public Sub </span>Transition(<span style="color:blue;">Of </span>T <span style="color:blue;">As </span>{FrameworkElement, <span style="color:blue;">New</span>})(<span style="color:blue;">ByVal </span>strViewKey <span style="color:blue;">As String</span>)

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(<span style="color:blue;">Of </span>HomeView)(Settings.HomeView)

_objIViewManagerService.Select(STR_CASHIER).Transition(<span style="color:blue;">Of </span>MealSelectorView) _
                                (<span style="color:blue;">My</span>.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

<span style="color:blue;">Public Sub </span>Transition(<span style="color:blue;">Of </span>T <span style="color:blue;">As </span>{FrameworkElement, <span style="color:blue;">New</span>})(<span style="color:blue;">ByVal </span>objNavigateKey <span style="color:blue;">As </span>NavigateKey, _
                                                       <span style="color:blue;">ByVal </span>objDataContext <span style="color:blue;">As Object</span>)

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

<span style="color:blue;">Public Sub </span>Transition(<span style="color:blue;">Of </span>T <span style="color:blue;">As </span>{FrameworkElement, <span style="color:blue;">New</span>})( _
            <span style="color:blue;">ByVal </span>strViewKey <span style="color:blue;">As String</span>, _
            <span style="color:blue;">ByVal </span>objDataContext <span style="color:blue;">As Object</span>, _
            <span style="color:blue;">Optional ByVal </span>objRecordPrimaryKey <span style="color:blue;">As Object </span>= <span style="color:blue;">Nothing</span>, _
            <span style="color:blue;">Optional ByVal </span>strApplicationSuite <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>)

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

_objIViewManagerService.Select.Transition(<span style="color:blue;">Of </span>HomeView)( _
        Settings.HomeView, <span style="color:blue;">Nothing</span>, strApplicationSuite:=<span style="color:#a31515;">"BBQ Shack"</span>)

_objIViewManagerService.Select.Transition(<span style="color:blue;">Of </span>InventoryView)( _
    Settings.InventoryMenuView, <span style="color:blue;">Nothing</span>, strApplicationSuite:=<span style="color:#a31515;">"Inventory"</span>)

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

Transition Signature Five

<span style="color:blue;">Public Sub </span>Transition(<span style="color:blue;">Of </span>T <span style="color:blue;">As </span>{FrameworkElement, <span style="color:blue;">New</span>})(<span style="color:blue;">ByVal </span>objNavigateKey <span style="color:blue;">As </span>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(<span style="color:blue;">Of </span>ItemCategoryView)(_
    <span style="color:blue;">New </span>NavigateKey(_objIViewManagerService.NextVersion, Settings.ItemCategoryView, _
                    <span style="color:blue;">Me</span>.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(<span style="color:blue;">Of </span>UnitOfMeasureView)( _
    <span style="color:blue;">New </span>NavigateKey(Settings.UnitOfMeasureView, _
                    <span style="color:blue;">CType</span>(param, uom_UnitOfMeasure_FillSelector).uom_UnitOfMeasureIdent, _
                    <span style="color:blue;">New </span>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. 

<span style="color:green;">''' </span><span style="color:gray;"><summary>
</span><span style="color:green;">''' Default object construction, creates a default BLL and DAL
''' </span><span style="color:gray;"></summary>
</span><span style="color:blue;">Public Sub New</span>()
    <span style="color:blue;">Me</span>.New(ViewManagerService.CreateInstance, _
           ViewModelUIService.CreateInstance, _
           BLL(<span style="color:blue;">Of </span>uom_UnitOfMeasure).CreateInstance)
<span style="color:blue;">End Sub

</span><span style="color:green;">''' </span><span style="color:gray;"><summary>
</span><span style="color:green;">''' 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.
''' </span><span style="color:gray;"></summary>
</span><span style="color:green;">''' </span><span style="color:gray;"><param name="objIBLL"></span><span style="color:green;">IBLL(Of uom_UnitOfMeasure)</span><span style="color:gray;"></param>
</span><span style="color:blue;">Public Sub New</span>(<span style="color:blue;">ByVal </span>objIViewManagerService <span style="color:blue;">As </span>IViewManagerService, _
               <span style="color:blue;">ByVal </span>objIViewModelUIService <span style="color:blue;">As </span>IViewModelUIService, _
               <span style="color:blue;">ByVal </span>objIBLL <span style="color:blue;">As </span>IBLL(<span style="color:blue;">Of </span>uom_UnitOfMeasure))

    <span style="color:blue;">MyBase</span>.New(objIViewModelUIService)
    _objIViewManagerService = objIViewManagerService
    _objIBLLUnitOfMeasure = objIBLL

<span style="color:blue;">End Sub
</span>

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.

<span style="color:green;"><!-- ViewServiceManger loads/transitions all views here-->
</span><span style="color:blue;"><</span><span style="color:#a31515;">Grid </span><span style="color:red;">Grid.Row</span><span style="color:blue;">="1" </span><span style="color:red;">x</span><span style="color:blue;">:</span><span style="color:red;">Name</span><span style="color:blue;">="applicationLayoutRoot" />

<</span><span style="color:#a31515;">local</span><span style="color:blue;">:</span><span style="color:#a31515;">TaskSwitcherControl
    </span><span style="color:red;">Grid.Row</span><span style="color:blue;">="1" </span><span style="color:red;">x</span><span style="color:blue;">:</span><span style="color:red;">Name</span><span style="color:blue;">="ucTaskSwitcherControl" </span><span style="color:red;">HorizontalAlignment</span><span style="color:blue;">="Center"
    </span><span style="color:red;">VerticalAlignment</span><span style="color:blue;">="Center" </span><span style="color:red;">Visibility</span><span style="color:blue;">="Collapsed">

    <</span><span style="color:#a31515;">local</span><span style="color:blue;">:</span><span style="color:#a31515;">TaskSwitcherControl.BitmapEffect</span><span style="color:blue;">>
        <</span><span style="color:#a31515;">OuterGlowBitmapEffect </span><span style="color:red;">GlowSize</span><span style="color:blue;">="8" </span><span style="color:red;">GlowColor</span><span style="color:blue;">="#FF5A5A5A" />
    </</span><span style="color:#a31515;">local</span><span style="color:blue;">:</span><span style="color:#a31515;">TaskSwitcherControl.BitmapEffect</span><span style="color:blue;">>

</</span><span style="color:#a31515;">local</span><span style="color:blue;">:</span><span style="color:#a31515;">TaskSwitcherControl</span><span style="color:blue;">></span> 

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.

<span style="color:blue;">Private Sub </span>Application_Startup(<span style="color:blue;">ByVal </span>sender <span style="color:blue;">As Object</span>, _
                                <span style="color:blue;">ByVal </span>e <span style="color:blue;">As </span>System.Windows.StartupEventArgs) <span style="color:blue;">Handles Me</span>.Startup
    <span style="color:green;">'
    'TODO - developers - set up logging event handler
    'AddHandler _objIViewModelUIService.Log, AddressOf WriteLogToFile-Database-EventLog-Email-etc
    '
</span><span style="color:blue;">End Sub
</span>

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.

<span style="color:blue;">If </span>_objIViewModelUIService.YesNoConfirmDelete(3) = CustomDialogResult.Yes <span style="color:blue;">Then</span>

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. 

<span style="color:blue;"><</span><span style="color:#a31515;">AdornerDecorator</span><span style="color:blue;">>

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.

<span style="color:blue;"><</span><span style="color:#a31515;">Ocean_MVVM</span><span style="color:blue;">:</span><span style="color:#a31515;">EditFormViewBase </span><span style="color:blue;">...>

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)

 <span style="color:blue;">Private WithEvents </span>_objBackgroundWorker <span style="color:blue;">As New </span>BackgroundWorker

 <span style="color:blue;">Public Overrides Sub </span>LoadRecord(<span style="color:blue;">ByVal </span>objRecordKey <span style="color:blue;">As Object</span>)

     <span style="color:blue;">If </span>objRecordKey <span style="color:blue;">Is Nothing Then
         Me</span>.uom_UnitOfMeasure = _objIBLLUnitOfMeasure.CreateEntity

     <span style="color:blue;">ElseIf Not TypeOf </span>objRecordKey <span style="color:blue;">Is Integer Then
         Throw New </span>ArgumentNullException(<span style="color:#a31515;">"objRecordKey"</span>, AppResources.RecordKeyWrongType)

     <span style="color:blue;">Else
         </span>_objLoadKey = objRecordKey
         _objBackgroundWorker.RunWorkerAsync(objRecordKey)
     <span style="color:blue;">End If

 End Sub 

Private Sub </span>_objBackgroundWorker_DoWork( _
         <span style="color:blue;">ByVal </span>sender <span style="color:blue;">As Object</span>, _
         <span style="color:blue;">ByVal </span>e <span style="color:blue;">As </span>System.ComponentModel.DoWorkEventArgs) _
             <span style="color:blue;">Handles </span>_objBackgroundWorker.DoWork

     e.Result = _objIBLLUnitOfMeasure.Select(<span style="color:blue;">CType</span>(e.Argument, <span style="color:blue;">Integer</span>))
 <span style="color:blue;">End Sub

 Private Sub </span>_objBackgroundWorker_RunWorkerCompleted( _
         <span style="color:blue;">ByVal </span>sender <span style="color:blue;">As Object</span>, _
         <span style="color:blue;">ByVal </span>e <span style="color:blue;">As </span>System.ComponentModel.RunWorkerCompletedEventArgs) _
             <span style="color:blue;">Handles </span>_objBackgroundWorker.RunWorkerCompleted

     <span style="color:blue;">If </span>e.Error <span style="color:blue;">Is Nothing Then

         If </span>e.Result <span style="color:blue;">IsNot Nothing Then
             Me</span>.uom_UnitOfMeasure = <span style="color:blue;">CType</span>(e.Result, uom_UnitOfMeasure)

         <span style="color:blue;">Else
             </span>_objIViewModelUIService.MessageDialog(<span style="color:#a31515;">"Data Error"</span>, <span style="color:#a31515;">"Record Not Found"</span>, ...)
         <span style="color:blue;">End If

     Else
         </span>_objIViewModelUIService.ExceptionDialog(e.Error)
     <span style="color:blue;">End If

 End Sub</span>

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:=<span style="color:#a31515;">"Ocean"</span>)> _
<span style="color:blue;">Partial Public Class </span>c_Customer
    <span style="color:blue;">Inherits </span>BusinessEntityBase

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

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

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

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.

<span style="color:blue;">Public Interface </span>IBLL(<span style="color:blue;">Of </span>T <span style="color:blue;">As </span>{IBusinessEntity, <span style="color:blue;">Class</span>, <span style="color:blue;">New</span>})

  <span style="color:blue;">Function </span>CreateEntity() <span style="color:blue;">As </span>T

  <span style="color:blue;">Function </span>Delete(<span style="color:blue;">ByVal </span>intKey <span style="color:blue;">As Integer</span>, <span style="color:blue;">ByVal </span>objTimeStamp <span style="color:blue;">As Byte</span>(), _
                    <span style="color:blue;">Optional ByVal </span>strProcedureName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>) <span style="color:blue;">As Integer

  Function </span>Delete(<span style="color:blue;">ByVal </span>strKey <span style="color:blue;">As String</span>, <span style="color:blue;">ByVal </span>objTimeStamp <span style="color:blue;">As Byte</span>(), _
                    <span style="color:blue;">Optional ByVal </span>strProcedureName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>) <span style="color:blue;">As Integer

  Function </span>Delete(<span style="color:blue;">Of </span>K)(<span style="color:blue;">ByVal </span>Key <span style="color:blue;">As </span>K, <span style="color:blue;">ByVal </span>objTimeStamp <span style="color:blue;">As Byte</span>(), _
                          <span style="color:blue;">Optional ByVal </span>strParameterName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>, _
                          <span style="color:blue;">Optional ByVal </span>strProcedureName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>) <span style="color:blue;">As Integer

  Function </span>Insert(<span style="color:blue;">ByVal </span>Entity <span style="color:blue;">As </span>T, <span style="color:blue;">ByRef </span>intNewIdent <span style="color:blue;">As Integer</span>, _
                    <span style="color:blue;">Optional ByVal </span>strProcedureName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>) <span style="color:blue;">As Integer

  Function </span>Insert(<span style="color:blue;">ByVal </span>Entity <span style="color:blue;">As </span>T, <span style="color:blue;">Optional ByVal </span>strProcedureName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>) <span style="color:blue;">As Integer

  Function </span>[Select](<span style="color:blue;">ByVal </span>intKey <span style="color:blue;">As Integer</span>, <span style="color:blue;">Optional ByVal </span>strProcedureName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>) <span style="color:blue;">As </span>T

  <span style="color:blue;">Function </span>[Select](<span style="color:blue;">ByVal </span>strKey <span style="color:blue;">As String</span>, <span style="color:blue;">Optional ByVal </span>strProcedureName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>) <span style="color:blue;">As </span>T

  <span style="color:blue;">Function </span>[Select](<span style="color:blue;">Of </span>K)(<span style="color:blue;">ByVal </span>Key <span style="color:blue;">As </span>K, <span style="color:blue;">Optional ByVal </span>strParameterName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>, _
                            <span style="color:blue;">Optional ByVal </span>strProcedureName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>) <span style="color:blue;">As </span>T

  <span style="color:blue;">Function </span>Update(<span style="color:blue;">ByVal </span>Entity <span style="color:blue;">As </span>T, <span style="color:blue;">Optional ByVal </span>strProcedureName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>) <span style="color:blue;">As Integer

End Interface
</span>

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). 

<span style="color:blue;">Private Function </span>Update() <span style="color:blue;">As Integer

    Try
        Me</span>.uom_UnitOfMeasure.uom_DateModified = Now
        <span style="color:blue;">Me</span>.uom_UnitOfMeasure.uom_ModifiedBy = Data.UserName

        <span style="color:blue;">If Me</span>.uom_UnitOfMeasure.IsValid <span style="color:blue;">Then

            If </span>_objIBLLUnitOfMeasure.Update(<span style="color:blue;">Me</span>.uom_UnitOfMeasure) = 1 <span style="color:blue;">Then
                Return </span>1
            <span style="color:blue;">End If

        End If

        Me</span>.ErrorHeaderText = <span style="color:#a31515;">"Save Errors"

    </span><span style="color:blue;">Catch </span>ex <span style="color:blue;">As </span>Exception
        _objIViewModelUIService.ExceptionDialog(ex)
    <span style="color:blue;">End Try

    Return </span>0
<span style="color:blue;">End Function</span>

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).

<span style="color:blue;">Public Function </span>Update( _
    <span style="color:blue;">ByVal </span>Entity <span style="color:blue;">As </span>T, _
    <span style="color:blue;">Optional ByVal </span>strProcedureName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>) <span style="color:blue;">As Integer Implements </span>IBLL(<span style="color:blue;">Of </span>T).Update

    <span style="color:blue;">Return </span>DAL.Update(Entity, strProcedureName)
<span style="color:blue;">End Function
</span>

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)> _
<span style="color:blue;">Public Property </span>uom_Timestamp() <span style="color:blue;">As Byte</span>()
    <span style="color:blue;">Get
        Return </span>_objuom_Timestamp
    <span style="color:blue;">End Get
    Set</span>(<span style="color:blue;">ByVal </span>Value <span style="color:blue;">As Byte</span>())
        <span style="color:blue;">MyBase</span>.SetPropertyValue(<span style="color:#a31515;">"uom_Timestamp"</span>, _objuom_Timestamp, Value)
    <span style="color:blue;">End Set
End Property

</span><DataColumn(ColumnType.VarChar, 15)> _
<CharacterCasingFormatting(CharacterCasing.LowerCase)> _
<StringLengthValidator(1, 15)> _
<span style="color:blue;">Public Property </span>uom_Title() <span style="color:blue;">As String
    Get
        Return </span>_struom_Title
    <span style="color:blue;">End Get
    Set</span>(<span style="color:blue;">ByVal </span>Value <span style="color:blue;">As String</span>)
        <span style="color:blue;">MyBase</span>.SetPropertyValue(<span style="color:#a31515;">"uom_Title"</span>, _struom_Title, Value)
    <span style="color:blue;">End Set
End Property

</span><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)> _
<span style="color:blue;">Public Property </span>uom_UnitOfMeasureIdent() <span style="color:blue;">As Integer
    Get
        Return </span>_intuom_UnitOfMeasureIdent
    <span style="color:blue;">End Get
    Set</span>(<span style="color:blue;">ByVal </span>Value <span style="color:blue;">As Integer</span>)
        <span style="color:blue;">MyBase</span>.SetPropertyValue(<span style="color:#a31515;">"uom_UnitOfMeasureIdent"</span>, _
                                _intuom_UnitOfMeasureIdent, Value)
    <span style="color:blue;">End Set
End Property
</span>

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.

<span style="color:blue;">Public Function </span>Update( _
    <span style="color:blue;">ByVal </span>obj <span style="color:blue;">As </span>T, <span style="color:blue;">Optional ByVal </span>strProcedureName <span style="color:blue;">As String </span>= <span style="color:#a31515;">""</span>) _
        <span style="color:blue;">As Integer Implements </span>IDAL(<span style="color:blue;">Of </span>T).Update

    <span style="color:blue;">If </span>obj <span style="color:blue;">Is Nothing Then
        Throw New </span>ArgumentNullException(<span style="color:#a31515;">"obj"</span>, <span style="color:#a31515;">"Business object was null"</span>)
    <span style="color:blue;">End If

    If </span>obj.ActiveRuleSet <> STR_UPDATE <span style="color:blue;">Then
        Throw New </span>InvalidOperationException( _
            <span style="color:blue;">String</span>.Format(<span style="color:#a31515;">"{0} using wrong ActiveRuleSet for update.  Current setting: {1}."</span>, _
                      <span style="color:blue;">Me</span>.EntityName, obj.ActiveRuleSet))
    <span style="color:blue;">End If

    If Not </span>obj.HasBeenValidated <span style="color:blue;">Then
        Throw New </span>InvalidOperationException( _
            <span style="color:blue;">String</span>.Format(<span style="color:#a31515;">"{0} has not been validated for update."</span>, _
                    <span style="color:blue;">Me</span>, EntityName))
    <span style="color:blue;">End If

    Dim </span>da <span style="color:blue;">As </span>DataAccess = GetDataAccess()
    <span style="color:blue;">Dim </span>params <span style="color:blue;">As New </span>List(<span style="color:blue;">Of </span>SqlParameter)

    <span style="color:blue;">For Each </span>kvp <span style="color:blue;">As </span>KeyValuePair(<span style="color:blue;">Of String</span>, DataColumn) <span style="color:blue;">In </span>_
            SharedDataColumnsManager.GetManager(<span style="color:blue;">Me</span>.EntityType).DataColumnDictionary.ToList

        <span style="color:blue;">If </span>kvp.Value.IsDateCreated <span style="color:blue;">OrElse </span>kvp.Value.IsCreatedBy <span style="color:blue;">Then
            Continue For
        End If

        If </span>kvp.Value.Nullable <span style="color:blue;">Then

            Dim </span>objValue <span style="color:blue;">As Object </span>= obj.GetType.GetProperty(kvp.Key).GetValue(obj, <span style="color:blue;">Nothing</span>)

            <span style="color:blue;">If </span>objValue <span style="color:blue;">Is Nothing Then
                </span>params.Add(BuildSQLParameter( _
                           kvp.Value.ParameterName, _
                           GetSqlDataType(kvp.Value.ColumnType), _
                           ParameterDirection.Input, DBNull.Value))

            <span style="color:blue;">Else
                </span>params.Add(BuildSQLParameter( _
                           kvp.Value.ParameterName, _
                           GetSqlDataType(kvp.Value.ColumnType), _
                           ParameterDirection.Input, objValue))
            <span style="color:blue;">End If

        ElseIf </span>kvp.Value.MaximumLength >

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 (not Silverlight 3 Beta, not Silverlight 4 Beta)
  • SQL Server 2008 or SQL Server 2008 Express

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.

Database Installation

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

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.

Filed under: CodeProject, M-V-VM, MVVM, Ocean, Silverlight, Silverlight Controls, VB.NET, WPF Controls, WPF General

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Karl Shifflett
Architect Gayle Manufacturing Company
United States United States
Karl loves .NET, WPF, WCF, ASP.NET, VB.NET and C#.
 
Awards:
 
  • December 2008 VB.NET Code Project Article Award
  • 2009 Code Project MVP
  • 2008 Code Project MVP
  • 2008 Microsoft MVP - Client App Dev
  • December 2007 VB.NET Code Project Article Award
  • Gold Medal Winner at IBM's 1998 PROIV Programming Contest in Las Vegas
Click here to check out my Blog
 
Click here to learn about Mole 2010 debugging tool for Visual Studio 2010
 
Click here to read about XAML Power Toys
 

Just a grain of sand on the worlds beaches.

Follow on   Twitter

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web03 | 2.8.141015.1 | Last Updated 8 Feb 2010
Article Copyright 2010 by Karl Shifflett
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid