Tucking ViewModels Beneath Views in Solution Explorer in Visual Studio 2013

October 30, 2013

VMTuckedAway

Back when I was using Visual Studio 2010 a few weeks ago I had a cool macro that would allow me to select a view and viewmodel file in the Solution Explorer, run the macro and then the viewmodel would be nested nicely under the view as pictured above.

Well… Beginning with Visual Studio 2012 (which I skipped over), Visual Studio no longer support macros.  Kind of a bummer since many developers wrote macros and depended on them for automating tasks, etc.

The good news is, writing a Visual Studio 2013 VSPackage is not really that difficult and the DTE macro code can pretty much be copied and pasted and with a little editing you’re up and running again. With the VSPackage you also get the benefit of not watching that spinning and dancing macro icon that would occasionally appear in the Windows Taskbar which caused your macro to freeze for awhile.

Background

I like to tuck my viewmodels beneath the view.  To me, it makes for nice organization and condenses the number of top level files I have to view in the Solution Explorer.

Most of my viewmodels have a one-to-one relationship with its view.  For those that don’t, I don’t tuck them beneath.

I also like to name my views and viewmodels according to the above pattern. For me, it keeps things clean and consistent.

Introducing Visual Studio 2013 Extension View Model Tuck Away

The below image was captured from the “Extensions and Updates…” dialog after I installed the VSPackage.

ExtensionManager

Operation

The digitally signed package available on the Visual Studio Gallery operates like this:

  • Developer selects a view and viewmodel file in the solution explorer
  • Developer runs the ViewModel Tuck Away command

The VSPackage will verify that:

  • Two files have been selected in the Solution Explorer
  • That one file has a .xaml file extension and contains the word “view”
  • That one file has a .cs file extension and contains the word “viewmodel”

The VSPackage will then tuck the selected viewmodel beneath the selected view.

During the execution of the VSPackage command, the Visual Studio Status Bar text will be updated to reflect the success or failure of the command.

I wrote the VSPackage this way to enforce naming standards at my work and home projects.

I know that some (many) may not like this.  No problem, the source is on GitHub (see Source below) and you can very easily modify it, rebuild the solution and double click on your new VSIX file.

Surfacing the VSPackage Command in Visual Studio 2013

You can surface the VSPackage Command in three ways:

  • Tools Menu, ViewModel Tuck Away
  • Add command to a Toolbar
  • Provide shortcut key for command (not really practical since you used the mouse to select the two files, may as well click a Toolbar icon or text.)

By default the command is added to the Tools Menu as seen below:

ToolsMenu

To add the command to a Toolbar follow the numbers below. #2 below allows you to select the Toolbar you want the command to appear on.  #7 allows you to position the command on the selected Toolbar.

AddCommandToToolBar

I added my command to the Standard Toolbar and moved it to the right side as pictured below.  For me, that little icon will do.

ToolBarSetup

However you may want to see the commands text as pictured below. To do this, use the above “Modify Selection” ComboBox just below #7 above and select “Image and Text”.

ToolBarSetupWithText

What is a VSPackage and How Did You Do This?

You can read all about Visual Studio 2013 VSPackages here. According to this MSDN article:

“VSPackages are software modules that extend the Visual Studio integrated development environment (IDE) by providing UI elements, services, projects, editors, and designers.”

To author your own VSPackage you’ll need to download and install the VS 2013 SDK from here, look towards the bottom of the page in the “Additional Software” section for Visual Studio SDK. The documentation for the SDK is here.

After installing the SDK you’ll get some new templates (very good I might add). To fire up a new VSPackage follow the numbers.

NewVSPackage

I strongly suggest studying the default VSPackage that the template creates.  I adds a Tools menu command that displays a message box.  Simple, but there is a lot of plumbing code it creates for you that you pretty much don’t have to fool with. The VSPackage only has a few files of “plumbing” making it easy to understand how it all fit together.

The below code is the core functionality required to identity two selected items, figure out which is the view and viewmodel and then set the dependency.

Of interest is how much power VSPackages have access to. For example the line of code: var dte = (DTE)GetService(typeof(DTE)); provides access to the DTE.  Most macro programmers are already familiar with the power and features exposed by the DTE. Once your command is invoked, you fire up the DTE and code away.  Not a bad upgrade path from macros or Visual Studio Add-Ins which are depreciated in VS 2013.

In a similar fashion this line of code: var uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); provides the developer access to the IVsUIShell capabilities.

As I mentioned above, you can use your own custom logic for determining which selected file is the parent and which will be the new child of the parent.  There are many techniques for determining this, like I said I chose to enforce naming standards for my views and viewmodels.

Boolean IsView(String targetFileName) {
    var extension = Path.GetExtension(targetFileName);
    return !String.IsNullOrWhiteSpace(extension) && extension == ".xaml" && 
            targetFileName.IndexOf("view") > -1;
}

Boolean IsViewModel(String targetFileName) {
    var extension = Path.GetExtension(targetFileName);
    return !String.IsNullOrWhiteSpace(extension) && extension == ".cs" && 
            targetFileName.IndexOf("viewmodel") > -1;
}

/// <summary>
/// This function is the callback used to execute a command when the a menu item is clicked.
/// See the Initialize method to see how the menu item is associated to this function using
/// the OleMenuCommandService service and the MenuCommand class.
/// </summary>
void MenuItemCallback(Object sender, EventArgs e) {
    try {
        //get access to the familar DTE object.
        var dte = (DTE)GetService(typeof(DTE));

        if (dte.SelectedItems.Count == 2) {
            String fileNameOne = dte.SelectedItems.Item(1).ProjectItem.FileNames[1].ToLower();
            String fileNameTwo = dte.SelectedItems.Item(2).ProjectItem.FileNames[1].ToLower();
            ProjectItem viewProjectItem;
            ProjectItem viewModelProjectItem;

            //need to figure out which select items are the view and viewmodel
            if (IsView(fileNameOne) && IsViewModel(fileNameTwo)) {
                viewProjectItem = dte.SelectedItems.Item(1).ProjectItem;
                viewModelProjectItem = dte.SelectedItems.Item(2).ProjectItem;
            } else if (IsView(fileNameTwo) && IsViewModel(fileNameOne)) {
                viewProjectItem = dte.SelectedItems.Item(2).ProjectItem;
                viewModelProjectItem = dte.SelectedItems.Item(1).ProjectItem;
            } else {
                dte.StatusBar.Text = "Must select a view.xaml file and a viewmodel.cs " +
                                     "file in the solution explorer.";
                return;
            }

            //line of code that creates the dependency between files.
            viewProjectItem.ProjectItems.AddFromFile(viewModelProjectItem.FileNames[1]);

            //show the tucked into viewmodel
            viewProjectItem.ExpandView();
            dte.StatusBar.Text = "Your viewmodel is now dependent on its view.";
        } else {
            dte.StatusBar.Text = "Must select a view.xaml file and a viewmodel.cs file in " +
                                 "the solution explorer.";
        }
    } catch (Exception ex) {
        var uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
        var clsId = Guid.Empty;
        Int32 result;
        var message = String.Format("Exception occurred in VmTuckAway: {0}", ex);
        ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(
            0,
            ref clsId,
            "VmTuckAway Exception",
            message,
            String.Empty,
            0,
            OLEMSGBUTTON.OLEMSGBUTTON_OK,
            OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
            OLEMSGICON.OLEMSGICON_INFO,
            0, // false
            out result));
    }
}

VSIX Manifest Files and VSPackage Assets

If you want to make your own VSPackages you’ll also want to understand the, “source.extension.vsixmanifest” file and how to properly edit it.

You need to read the VSIX Extension Schema 2.0 Reference found here. Very easy read with good examples.

One tip I remembered the hard way was the file property “Include in VSIX” which must be True for VSPackage assets you include in the “source.extension.vsixmanifest.”

These Large and Small icons provide a nice UI for your VSPackage in the “Extensions and updates…” dialog.

IncludeInVSIX

GitHub Source

The source code can be downloaded from GitHub here:

https://github.com/Oceanware/VmTuckAway

Download Signed VSPackage Extension on Visual Studio Gallery

http://visualstudiogallery.msdn.microsoft.com/cbe8ae32-555e-4e10-91b9-ee0d57bab037

Close

Hopes this helps someone and have a great day,

Just a grain of sand on the worlds beaches.


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.


Simplifying Prism WPF Navigation – Synchronous Navigation Confirmation

September 5, 2011

Microsoft patterns & practices Prism 4 library and guidance gives WPF and Silverlight developers a very solid foundation for creating business applications. Adopting Prism patterns and library features guides the developer towards creating applications that: can evolve over time, are not tightly coupled, can be independently developed across a large team, are Blendable, and testable.

New to Prism 4 was the Navigation API’s. The use of the Navigation API’s greatly simplifies application development because the Navigation API takes over the responsibility of object creation and siting objects in the target region.

The Prism library is designed to support applications that can target WPF and Silverlight with good code reuse. While not all developers need this capability, a lot of thought and design decisions were made to fully support this scenario.

One API that took this capability into account while being designed is the Navigation API; specifically the confirmation of a navigation request. Objects that implement IConfirmNavigationRequest have the option to veto a navigation request. In order to support the limitation that Silverlight does not allow blocking dialog boxes, the IConfirmNavigationRequest.ConfirmNavigationRequest method had to be written so that Silverlight objects could participate in vetoing a navigation request without a blocking dialog.

Without going into all the details (you can read them here), objects that implement IConfirmNavigationRequest are required to invoke the callback in the ConfirmNavigationRequest method arguments. Objects are free to implement any vetoing code they want as long as the callback is invoked.  In practice the navigation request is halted until the callback is invoked. This design enables Silverlight developers to implement a variety UI solutions for prompting the user, for example the Prism Interaction Request.

I’ve found that I’m my WPF development that I had to jump through the above hoops just to have navigation confirmation, when in fact WPF has out of the box support for modal, UI blocking dialogs. In an effort to simplify my WPF applications I’ve created a replacement for the Prism RegionNavigationService that allows for synchronous navigation confirmation.

The implementation is very straightforward and only requires that you add one class and one interface to the Prism library and recompile it.

The RegionNavigationSynchronousService is the replacement for the stock RegionNavigationService. This class uses the new synchronous (blocking) navigation confirmation interface IConfirmNavigationRequestSynchronous, listed below.

public interface IConfirmNavigationRequestSynchronous : INavigationAware
{
    /// <summary>
    /// Determines whether this instance approves being navigated away from.
    /// </summary>
    /// <param name="navigationContext">The navigation context.</param>
    Boolean ConfirmNavigationRequestSynchronous(NavigationContext navigationContext);
}

By default, Prism automatically registers the RegionNavigationService in the container as part of the bootstrapping pipeline. However, we want to use the WPF friendly synchronous confirmation service, RegionNavigationSynchronousService.

All that is required is to override the ConfigureContainer method in your bootstrapper and register RegionNavigationSynchronousService as I’ve done below.

namespace TestBench {
    using System.Windows;
    using Microsoft.Practices.Prism.Modularity;
    using Microsoft.Practices.Prism.Regions;
    using Microsoft.Practices.Prism.UnityExtensions;
    using Microsoft.Practices.Unity;
    using TestBench.Customers;

    class Bootstrapper : UnityBootstrapper {
        protected override IModuleCatalog CreateModuleCatalog() {
            var catalog = new ModuleCatalog();
            catalog.AddModule(typeof (CustomersModule));
            return catalog;
        }

        protected override DependencyObject CreateShell() {
            var shell = this.Container.Resolve<ShellView>();
            Application.Current.MainWindow = shell;
            Application.Current.MainWindow.Show();
            return shell;
        }

        protected override void ConfigureContainer() {
            base.ConfigureContainer();

            // register the new navigation service that uses synchronous navigation 
            // confirmation instead of the async confirmation.
            this.Container.RegisterType(typeof(IRegionNavigationService), 
                typeof(RegionNavigationSynchronousService));
        }
    }
}

Below is a very simple implementation.

PLEASE do not put MessageBox code in your view models!  This is for demo purposes only and to keep the code simple.  Please use a dialog service that abstracts the UI dialog away from the view model.

The below code is from the included download and is in the CustomerMaintenanceViewModel. The below ConfirmNavigation property allows the demo to confirm or not but is not part of the Navigation API. This method returns true or false to continue the navigation request or not.

public Boolean ConfirmNavigationRequestSynchronous(NavigationContext navigationContext) {
    if (this.ConfirmNavigation) {
        if (MessageBox.Show("Close form and navigate?", "Confirm Navigation", 
            MessageBoxButton.OKCancel, MessageBoxImage.Question) == MessageBoxResult.OK) {
            return true;
        }
        return false;
    }
    return true;
}

Demo Application

SyncDemo

Very simple Prism application that demonstrates confirming navigation requests and region lifetime. The status bar indicates the views in the ContentRegion on the right.  As you open or close views their name will be displayed; for the Customers, the customer number will be displayed.

Crack the code open, you’ll have this down in a few minutes.

Download

As always, don’t forget to “Unblock” the zip file after downloading from the Internet before you unzip it.

Synchronous Prism Navigation (177KB)

The download includes a sample Prism solution that your can run without modifying your Prism library code.  I’ve included a \Lib folder in the solution with pre-built, modified Prism library code.

I’ve also included two files in the \PrismSource folder that you can add to your Prism library. Simply copy these two files into the below folder and recompile Prism. You now have the option to use a simpler navigation confirmation API in your WPF projects. 

This only works in WPF and NOT Silverlight.  If you have the requirement to share code between WPF and Silverlight this will not work because Silverlight requires the navigation confirmation to be async.

SyncNav

Have a great day,

Just a grain of sand on the worlds beaches.


Various Clients and Forms Authentication

April 25, 2011

Conceptual

Scenarios

In each of the below scenarios, forms authentication is used to secure access to the ASP.NET MVC endpoints and WCF services.

  • WPF accessing WCF services
  • WPF accessing MVC endpoints
  • Windows Phone 7 accessing WCF services
  • Desktop and mobile browsers accessing ASP.NET MVC website pages
  • jQuery accessing MVC endpoints

While I have not included the code for a Windows Phone 7 to access MVC endpoints that is a supported scenario as well.

Requirements

  • Visual Studio 2010
  • SQL Express 2008
  • Windows Phone 7 tools
    • If you don’t have these and don’t want to see this project, you can just remove the Windows Phone 7 project from the solution.

Background

I’ve been working on Stuff v2; a movie, game, and books application. Its primary use case is, "I’m at the store and don’t remember if I have a particular movie, game, or book. I need to determine if I have it; if not, then check the online price and ratings before making the purchase."

Given the varied application clients and devices, ASP.NET forms authentication seemed like the natural choice for authentication for the website, MVC3 JSON endpoints, and WCF services.

The reason I have varied client software and devices is more of a learning experience than an application requirement. I have other applications I want to write that will need to access the application from all my devices.

When I started programming the WPF client, I ran into a stone wall with respect to WPF accessing WCF services that are secured by forms authentication. This blog post is about getting over that stone wall.

Identifying the Problem Space

At the end of the day, the problem that needs solving is managing the forms authentication cookie or ticket.

Managing means, that after authenticating, the client must be able to retrieve the ticket returned in the response and include it in future requests to secured resources.

As you will see, client API’s vary across scenarios not only in coding patterns but in complexity as well.

Regardless of which client is accessing resources that require forms authentication, the following steps outline the required workflow:

  • Log in
  • Cache the ticket returned in the response
  • Include the ticket in subsequent requests

Setting up Forms Authentication

When I created the ASP.NET MVC3 application, VariousClients.Web, I used the MVC3 Internet template with the Razor view engine. This template sets up forms authentication and provides a pretty good out-of-box SQL Express membership system for you.

The below snippet from the web.config shows a few required changes:

<authentication mode="Forms">
    <!-- cookieless="UseCookies" is required by non-browser clients
                to authenticate using forms authentication-->
    
    <!-- production applications, change to requiresSSL="true"-->
    <forms timeout="2880" cookieless="UseCookies" loginUrl="~/Account/LogOn"
            requireSSL="false" />
</authentication>

Setting up the AuthenticationService

The System.Web.ApplicationServices.AuthenticationService is a built-in service that you can expose as a service endpoint on your website. This service exposes log in, log out methods for clients that access WCF endpoints requiring forms authentication. This service uses the membership provider defined in the web.config. After logging in, the service returns a ticket in the response, similar to forms authentication log in.

Adding the service is easy. First add a folder to the root of the website named, "Services". Into that folder, add a WCF service named Authentication.svc. Delete the generated service contract and code-behind files. Next replace the contents of the Authentication.scv file with the below code snippet.

<%@ ServiceHost Language="C#" Service="System.Web.ApplicationServices.AuthenticationService" %>

Now add the following to your web.config:

<system.web.extensions>
    <scripting>
        <webServices>
            <!-- for production applications, change to requiresSSL="true"-->
            <authenticationService enabled="true" requireSSL="false"/>
        </webServices>
    </scripting>
</system.web.extensions>

Rebuild your web application.

The Authentication.svc will now appear in the Add Service Reference dialog when adding service references in your client applications.

AddServiceReference

Browsers, Web pages, and MCV3 Controller Methods

Controller classes or controller action methods can be decorated with the Authorize attribute to require that client browsers or JavaScript accessing them be authenticated.

After logging in, the browser automatically manages the authentication ticket and includes it with all future requests to the website.

The below GetTime method requires authentication:

using System;
using System.Web.Mvc;

namespace VariousClients.Web.Controllers {

    public class CloudDataController : Controller {

        [Authorize]
        public JsonResult GetTime() {
            return Json(DateTime.Now.ToLongDateString(), JsonRequestBehavior.AllowGet);
        }
    }
}

WPF and MVC3 Controller Methods

The following section applies equally to WPF, Windows Forms, console, and test projects.

Server-Side

I’ve added a RemoteLogOn method to the AccountController that is used by non-browser clients when logging in. The method signature and implementation is slightly different from the LogOn method.

        [HttpPost]
#if (!DEBUG)
        [RequireHttps]
#endif
        public Boolean RemoteLogOn(LogOnModel model) {
            if(ModelState.IsValid) {
                if(Membership.ValidateUser(model.UserName, model.Password)) {
                    FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                    return true;
                }
            }
            return false;
        }

CookieAwareWebClient

Remember, the purpose of the above RemoteLogOn is to get the ticket returned in the response. To simplify client programming, a CookieAwareWebClient wrapper can be used to execute WebClient requests while at the same time handling including the ticket in requests.

using System;
using System.Net;

namespace VariousClients.Common.Net {

    public class CookieAwareWebClient : WebClient {

        public CookieContainer CookieContainer { get; private set; }

        public CookieAwareWebClient()
            : this(new CookieContainer()) {
        }

        public CookieAwareWebClient(CookieContainer cookieContainer) {
            if (cookieContainer == null) throw new ArgumentNullException("cookieContainer");
            this.CookieContainer = cookieContainer;
        }

        protected override WebRequest GetWebRequest(Uri address) {
            if(this.CookieContainer == null) {
                throw new InvalidOperationException("CookieContainer is null");
            }
            var request = base.GetWebRequest(address);
            if (request is HttpWebRequest) {
                (request as HttpWebRequest).CookieContainer = this.CookieContainer;
            } return request;
        }
    }
}

Client-Side

void btnMvcLogIn_Click(Object sender, RoutedEventArgs e) {

    _cookieJar = new CookieContainer();
    var client = new CookieAwareWebClient(_cookieJar) { Encoding = Encoding.UTF8 };

    client.UploadValuesCompleted += (s, args) => {
        this.lblMvcResult.Content =
            args.Error == null ? Encoding.UTF8.GetString(args.Result) : args.Error.Message;
    };

    var nvc = new NameValueCollection { { "UserName", Credentials.UserName }, 
                                        { "Password", Credentials.Password }, 
                                        { "RememberMe", "true" } };
    client.UploadValuesAsync(
        new Uri("http://localhost:1668/Account/RemoteLogOn"), "POST", nvc);
}

void btnMvcGetData_Click(Object sender, RoutedEventArgs e) {

    this.lblMvcResult.Content = "calling cloud service...";
    var client = new CookieAwareWebClient(_cookieJar);

    client.DownloadStringCompleted += (s, args) => {
        this.lblMvcResult.Content = args.Error == null ? args.Result : args.Error.Message;
    };

    client.DownloadStringAsync(new Uri("http://localhost:1668/CloudData/GetTime"));
}

In the above code, the _cookieJar is scoped at module level. The important concept is to use the same instance of the CookieAwareWebClient for all calls because it manages the ticket for you. As an alternative, you could retrieve the CookieContainer value after logging in and pass it in the constructor when creating new CookieAwareWebClient instances.

The btnMvcGetData_Click code looks like a typical WebClient call. The CookieAwareWebClient makes calling endpoints secured by forms authentication painless.

WPF and WCF Service Methods

The following section applies equally to WPF, Windows Forms, console, and test projects.

WPF consuming WCF services secured with forms authentication requires a bit of extra code to manage the ticket because WCF does not provide a simple API to add or retrieve it when making service calls that use the generated proxies.

If you contrast the WPF and Windows Phone 7 calls to the same WCF service you’ll see exactly what I mean. The Windows Phone 7 proxy exposes a CookieContainer object making it easy to capture or include the ticket in all service calls.

Before you can call the AuthenticationService, you’ll need to add a service reference to the Authentication service. When adding the service reference, I changed the namespace to AuthenticationService like the below image:

AddServiceReference

FormsAuthenticationAssistant

The FormsAuthenticationAssistant is a façade for WPF clients making WCF service calls secured with forms authentication. It provides automatic ticket management for making service calls.

// Many thanks to Jonas Follesoe for this post:  
// http://jonas.follesoe.no/2008/09/12/wcf-authentication-services-silverlight-and-smelly-cookies/
// I learned how to extract the forms authentication cookie from the AuthenticationService and how to 
// reapply it subsequent service calls.

using System;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace VariousClients.Common.ServiceModel {

    public class FormsAuthenticationAssistant {

        public String TicketCookie { get; private set; }

        public FormsAuthenticationAssistant() { }

        public FormsAuthenticationAssistant(String ticket) {
            if (String.IsNullOrWhiteSpace(ticket)) throw new ArgumentNullException("ticket");
            this.TicketCookie = ticket;
        }

        public Boolean Login(Func<Boolean> method, IContextChannel serviceInnerChannel) {
            if (method == null) throw new ArgumentNullException("method");
            if (serviceInnerChannel == null) throw new ArgumentNullException("serviceInnerChannel");

            using (new OperationContextScope(serviceInnerChannel)) {
                if (!method()) {
                    this.TicketCookie = null;
                    return false;
                }
                var properties = OperationContext.Current.IncomingMessageProperties;
                var responseProperty = 
                    (HttpResponseMessageProperty)properties[HttpResponseMessageProperty.Name];
                this.TicketCookie = responseProperty.Headers[HttpResponseHeader.SetCookie];
                return true;
            }
        }

        public T Execute<T>(Func<T> method, IContextChannel serviceInnerChannel) {
            if (method == null) throw new ArgumentNullException("method");
            if (serviceInnerChannel == null) throw new ArgumentNullException("serviceInnerChannel");
            if (String.IsNullOrWhiteSpace(this.TicketCookie)) {
                throw new InvalidOperationException(
                    "Currently not logged in. Must Login before calling this method.");
            }

            using (new OperationContextScope(serviceInnerChannel)) {
                var requestProperty = new HttpRequestMessageProperty();
                OperationContext.Current.OutgoingMessageProperties.Add(
                    HttpRequestMessageProperty.Name, requestProperty);
                requestProperty.Headers.Add(HttpRequestHeader.Cookie, this.TicketCookie);
                return method();
            }
        }

        public void Execute(Action method, IContextChannel serviceInnerChannel) {
            if (method == null) throw new ArgumentNullException("method");
            if (serviceInnerChannel == null) throw new ArgumentNullException("serviceInnerChannel");
            if (String.IsNullOrWhiteSpace(this.TicketCookie)) {
                throw new InvalidOperationException(
                    "Currently not logged in. Must Login before calling this method.");
            }

            using (new OperationContextScope(serviceInnerChannel)) {
                var requestProperty = new HttpRequestMessageProperty();
                OperationContext.Current.OutgoingMessageProperties.Add(
                    HttpRequestMessageProperty.Name, requestProperty);
                requestProperty.Headers.Add(HttpRequestHeader.Cookie, this.TicketCookie);
                method();
            }
        }
    }
}

The above Login method invokes the method passed in the method argument. If the log in is successful, the ticket is extracted from the response and cached in the TicketCookie property and true is returned.

The above two Execute methods invoke the method passed in the method argument and add the TicketCookie to the request.

Your application can interface with the FormsAuthenticationAssistant in one of two ways. One is to create a single instance of the FormsAuthenticationAssistant and use that instance for all service calls. This is how the demo WPF application is written.

Another is to save the value of the TicketCookie property after a successful log in, then create a new instance of FormsAuthenticationAssistant for each call and pass the saved TicketCookie value in the constructor.

Once you’re comfortable with routing your service proxy calls through the FormsAuthenticationAssistant, you’ll notice very little differences between using this façade and calling the service proxy methods directly. Don’t forget, in real-world applications you’ll want to make these service calls or calls to the façade asynchronous so the UI thread is not blocked.

Server-Side

The PrincipalPermission attribute can be used on WCF service methods to restrict access to authenticated users. This is similar to using the Authorize attribute on the MCV3 controller action methods. Follow the three steps below:

using System;
using System.Security.Permissions;
using System.ServiceModel.Activation;
using System.Threading;
using System.Web;

namespace VariousClients.Web.Services {

    //STEP 1 - required for interop with ASP.NET
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    public class CloudData : ICloudData {

        public CloudData() {
            //STEP 2 - pass caller principal to executing threads principal 
            Thread.CurrentPrincipal = HttpContext.Current.User;
        }

        //STEP 3 - verify caller is authenticated
        [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
        public String GetTime() {
            return DateTime.Now.ToLongDateString();
        }
    }
}

Client-Side

In the below code, we will first log in against the AuthenticationService, cache the ticket, then call CloudData.GetTime.

void btnServiceLogIn_Click(Object sender, RoutedEventArgs e) {
    this.lblServiceResult.Content = "calling cloud service...";
    var authenticationService = new AuthenticationServiceClient();
    if(_faa.Login(() => authenticationService.Login(
                    Credentials.UserName, Credentials.Password, String.Empty, true),
                    authenticationService.InnerChannel)) {
        this.lblServiceResult.Content = "Log in sucessful.";
    } else {
        this.lblServiceResult.Content = "Bummer, log in not sucessful.";
    }
}

void btnServiceGetData_Click(Object sender, RoutedEventArgs e) {
    this.lblServiceResult.Content = "calling cloud service...";
    var cloudDataService = new CloudDataClient();
    try {
        this.lblServiceResult.Content = 
            _faa.Execute<String>(cloudDataService.GetTime, cloudDataService.InnerChannel);
    } catch(System.ServiceModel.Security.SecurityAccessDeniedException ex) {
        // three conditions could cause this
        //  1. client has never logged in
        //  2. ticket is expired
        //  3. instance of _faa was not the same instance used to log in
        // when this happens in your client code, re-authenticate to get a ticket
        this.lblServiceResult.Content = ex.Message;
    } catch(Exception ex) {
        this.lblServiceResult.Content = ex.Message;
    }
}

This above code is straightforward and very familiar to developers making service calls. The paradigm change is the forwarding of WCF service proxy calls through the FormsAuthenticationAssistant instance (_faa) which manages the ticket.

Windows Phone 7 and WCF Service Methods

Before you can call the AuthenticationService, you’ll need to add a service reference to the Authentication service. When adding the service reference, I changed the namespace to AuthenticationService like the below image:

AddServiceReference

For each service reference you add, you must also edit the ServiceReferences.ClientConfig file binding entry and set the enableHttpCookieContainer property to true as done below.

The Visual Studio tooling for adding service references chokes when the enableHttpCookieContainer property is set on a binding and displays a bizarre error message when adding or updating service references.

So… add all your service references, and then edit the ServiceReferences.ClientConfig file.

If you have to subsequently add, delete or modify a service reference, you’ll need to remove these properties, use the tooling and re-add the properties.

<bindings>
    <basicHttpBinding>
        <binding name="BasicHttpBinding_AuthenticationService" maxBufferSize="2147483647"
            maxReceivedMessageSize="2147483647" enableHttpCookieContainer="true">
            <security mode="None" />
        </binding>
        <binding name="BasicHttpBinding_ICloudData" maxBufferSize="2147483647"
            maxReceivedMessageSize="2147483647" enableHttpCookieContainer="true">
            <security mode="None" />
        </binding>
    </basicHttpBinding>
</bindings>

Calling the AuthenticationService Login method is just like calling other WCF service methods with a minor exception; the CookieContainer property must be set to a new instance of the CookieContainer class prior to making the call. If you forget to populate this property, the ticket will not be returned in the response.

void btnLogin_Click(Object sender, RoutedEventArgs e) {
    this.tbResult.Text = "Attempting to log in...";
    _cookieJar = new CookieContainer();
    var authenticationService = new AuthenticationServiceClient();
    authenticationService.CookieContainer = _cookieJar;
    authenticationService.LoginCompleted += (s, args) => {
        if(args.Error == null) {
            this.tbResult.Text = "Login sucessful.";

            // CookieJar can be persisted without an exception being thrown
            // either when toomstonned or shutdown.  
            // Yes, it can be read from the below stores also.
            // If required for your application, move this code to the appropriate location
            // for launching, closing, activating, and deactivating.
            //
            // uncomment to test
            // PhoneApplicationService.Current.State.Add("CookieJar", _cookieJar);
            // IsolatedStorageSettings.ApplicationSettings.Add("CookieJar", _cookieJar);

        } else {
            this.tbResult.Text = "Login failed: " + args.Error.Message;
        }
    };
    authenticationService.LoginAsync(Credentials.UserName, Credentials.Password, String.Empty, true);
}

In the above code I’ve created a module level variable to hold the CookieContainer (_cookieJar). The same CookieContainer instance must be passed in subsequent calls to other WCF service endpoints.

With Windows Phone 7 programming core concepts like tombstoning and launching must be addressed. In the above code snippet you’ll see a section of comments showing the CookieContainer persisted to storage. Based on your application needs and security requirements, you’ll need to determine when and how to persist your CookieContainer so that your users will not have to repeatedly log in.

void btnGetServiceData_Click(Object sender, RoutedEventArgs e) {
    this.tbResult.Text = "Calling service...";
    var cloudDataService = new CloudDataClient { CookieContainer = _cookieJar };
    cloudDataService.GetTimeCompleted += (s, args) => {
        if(args.Error == null) {
            this.tbResult.Text = args.Result;
        } else {
            if(args.Error is System.ServiceModel.Security.SecurityAccessDeniedException) {
                // three conditions could cause this
                //  1. client has never logged in
                //  2. ticket is expired
                //  3. the _cookieJar instance is not the same instance used when logging in
                // when this happens in your client code, re-authenticate to get a ticket
                this.tbResult.Text = args.Error.Message;
            } else {
                this.tbResult.Text = "Error: " + args.Error.Message;
            }
        }
    };
    cloudDataService.GetTimeAsync();
}

In the above code, the _cookieJar is assigned to the CookieContainer property. Contrast this API with the WPF API for the same call, this API is much simpler. It would be nice to have this exposed in the desktop API’s as well.

Running the Application

  • Set the VariousClients.Web project as the startup project.
  • Run the application (this will create your membership SQL Express database.)
  • When the below page is displayed, click the Register link.

RunningFirstTime

  • When completing the Registration page, you need to use the user name and password specified in the below Credentials class. This class provides the user name and password for the WPF and Windows Phone 7 projects when making service calls.

Credentials

  • After completing a registration page you will be redirected to the page that demonstrates making a jQuery AJAX call to a secured MVC endpoint.
  • To run the WPF and Windows Phone 7 applications, set them as the startup projects.

Download

The demo solution can be downloaded from my Sky Drive:

Various Clients Solution (1.7 MB)

Close

Hope this helps when writing your own desktop, phone and web applications that use forms authentication.

Have a great day,

Just a grain of sand on the worlds beaches.


Removing Repetitive Boiler Maker Code From View Models

November 17, 2010

While I was writing the BBQ Shack, I noticed that view model code calling into the business layer was repetitive across view models. The pattern I used was simple and widely accepted; background worker to make the asynchronous calls to the business layer.

The below code snippet was typical.  Background worker, exception checking, call to business layer, successful result code path, and exception result code path.

Old Pattern

void BackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
    e.Result = _iBLLItemCategory.Select(Convert.ToInt32(e.Argument));
}


void BackgroundWorker_RunWorkerCompleted(object sender, 
    System.ComponentModel.RunWorkerCompletedEventArgs e) {
    if (e.Error == null) {
        if (e.Result != null) {
            this.ic_ItemCategory = (ic_ItemCategory)e.Result;

        } else {
            _iViewModelUIService.MessageDialog("Data Error", 
                "Record Not Found",     
                string.Format("Item Category key: {0} not found", _objLoadKey.ToString));
        }

    } else {
        _objIViewModelUIService.ExceptionDialog(e.Error);
    }
}

I kept thinking about this, always looking for a better pattern.  I also wanted a pattern that only required a single line of code and still gave me all of the same benefits of the above code.

New Pattern

All of the above code condenses nicely down to one line of code that provides all of the above “old pattern” features.

The below repository method signatures describe the pattern.  The pattern for the method signatures is zero or more parameters, followed by the same last two; Action<T> resultCallback and Action<Exception> errorCallback.

public interface IItemRepository {

    void GetAll(Action<IEnumerable<Item>> resultCallback, Action<Exception> errorCallback);

    void Get(Int32 itemId, Action<Item> resultCallback, Action<Exception> errorCallback);

    Item Create();
}

The successful result of the method call is always calls the resultCallback delegate.

The unsuccessful result of the method call is always calls the errorCallback delegate.

This pattern is incredibly simple and leads to much cleaner code in the view model.  The errorCallback is a method in the view model base class.  This method can use a variety of techniques for displaying the error message to the user.

void LoadItems() {
    _itemRepository.GetAll(result => { this.DataItems.Source = result; }, this.DisplayException);
}

Repository Implementation – Bring on the Task Parallel Library (TPL)

The below implementation leverages the TPL Futures pattern.  Use of the TPL is optional, I’m using it here in this WPF application.  The TPL is not available in Silverlight 4 applications yet.  For Silverlight applications, change the implementation to use the common Silverlight asynchronous pattern.

The “pattern” is described in the above IItemRepository interface.  Like all interface contracts, implementation specifics can be platform specific and up to the developer.

Strongly recommend you read this book on MSDN: Parallel Programming with Microsoft .NET.  The book can also be purchased from Amazon here.  There is a book and Kindle version available.

[Export(typeof(IItemRepository))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ItemRepository : IItemRepository {

    readonly ThePhoneCompanyEntities _dataService;

    [ImportingConstructor]
    public ItemRepository(DataServiceFacade dataServiceFacade) {
        _dataService = dataServiceFacade.DataService;
    }

    void IItemRepository.GetAll(Action<IEnumerable<Item>> resultCallback,
        Action<Exception> errorCallback) {

        // This code can be refactored into a generic method
        // I left it this way to help with the learning process 
        Task<RepositoryResult<IEnumerable<Item>>> task =
            Task.Factory.StartNew(() => {
                try {
                    return new RepositoryResult<IEnumerable<Item>>(
                                _dataService.Items.ToList(), null);
                } catch(Exception ex) {
                    return new RepositoryResult<IEnumerable<Item>>(null, ex);
                }
            });

        task.ContinueWith(r => {
            if(r.Result.Error != null) {
                errorCallback(r.Result.Error);
            } else {
                resultCallback(r.Result.Package);
            }
        }, CancellationToken.None, TaskContinuationOptions.None,
            TaskScheduler.FromCurrentSynchronizationContext());
    }

    void IItemRepository.Get(Int32 itemId, Action<Item> resultCallback, 
        Action<Exception> errorCallback) {

        if(itemId != 0) {

            // This code can be refactored into a generic method
            // I left it this way to help with the learning process 
            Task<RepositoryResult<Item>> task =
                Task.Factory.StartNew(() => {
                    try {
                        return new RepositoryResult<Item>(
                            _dataService.Items.Where(
                                i => i.ItemID == itemId).FirstOrDefault(), null);
                    } catch(Exception ex) {
                        return new RepositoryResult<Item>(null, ex);
                    }
                });

            task.ContinueWith(r => {
                if(r.Result.Error != null) {
                    errorCallback(r.Result.Error);
                } else {
                    resultCallback(r.Result.Package);
                }
            }, CancellationToken.None, TaskContinuationOptions.None,
                TaskScheduler.FromCurrentSynchronizationContext());
        } else {
            resultCallback(((IItemRepository)this).Create());
        }
    }

    // I always create my entity objects in the business layer and never in the presentation layer
    Item IItemRepository.Create() {
        return new Item();
    }
}

Key Points

  • Call that does the actual work is wrapped in a try catch block.
  • The result of the asynchronous operation is wrapped in a RepositoryResult object.
  • The RepositoryResult is used to drive invoking the resultCallback or the errorCallback on the calling thread. The TaskScheduler.FromCurrentSynchronizatikonContext method call in the task.ContinueWith ensures the two callbacks are invoked on the calling thread. 

Download

This code is from one of my presentations at the patterns & practices 2010 Symposium. 

The article and video is here: http://blogs.msdn.com/b/kashiffl/archive/2010/10/21/patterns-and-practices-2010-symposium-content.aspx

This code can be downloaded from the article.

Close

I hope that this short post encourages you to look for possible refactorings in your own code and to take a look at the TPL. 

Have a great day,

Just a grain of sand on the worlds beaches.


In the Box – MVVM Training

November 7, 2010

Updated 12 Nov 2010

InTheBox

What is In the Box?

In the Box is a high quality, multi-media training that is consumed within Visual Studio 2010.  Content is navigated and delivered using a next generation computer based training (CBT) experience, the Visual Studio 2010 Feature Extension.

In the Box, is a brand name for a series of CBT Feature Extensions I’ll release that are listed in the Visual Studio 2010 Add New Project dialog; see below image.  This release is MVVM Training, the next will be Prism Training.

In the Box features:

  • Visual Studio 2010 Feature Extension
  • Content delivered as text, code, images, diagrams, video, or hyperlinks to the Internet
  • Hierarchical navigation tool window for content navigation
  • Content is viewed inside Visual Studio 2010 tool windows
  • No additional downloads or dependencies; all content is in the box.  (except online videos)
  • Installed and updated from the Visual Studio Gallery
  • Managed (disabled or uninstalled) using Visual Studio Extensions Manager (see bottom of this page)
  • Authored using Microsoft Word and the Instant Feature Builder

What is in this release of In the Box?

Please watch this video as it will answer most of your questions.

This installment of In the Box contains in-depth MVVM Training that includes an eleven assembly example solution with projects targeting developers at different levels of experience

Who is the target audience?

  • If you have never used MVVM before, this training is for you. 
  • If you have been using MVVM for a while and want to learn more, this training is for you. 
  • If you are an expert, you will enjoy the MVVM Technical Description and MVVM Scenarios content.

What are the requirements to install In the Box?

Visual Studio 2010 Professional, Premium, or Ultimate.

Expression Blend 4 WPF SDK – Free download (see below comment on SDK) (Note: this is installed with Blend also)

For those developers using Visual Studio 2010 Express or Visual Studio 2008, you can use the links in the below download section to download a .mht version of the content along with the sample solution source.  The .mht file can be viewed in your browser by double clicking the file in Windows Explorer.

Please note: I have not tested this solution with the Express version.

Windows XP and Windows Server 2003 Users

In order to run Feature Builder on either XP or Server 2003 you must create an environment variable named LocalAppData (set by default on Windows Vista and Windows 7).

How to Add LOCALAPPDATA variable in Windows XP:

  • Right-click on the My Computer icon and choose Properties
  • Click Advanced
  • Click Environment Variables
  • Under User variables section, click New
  • In the Variable name: field, type LOCALAPPDATA
  • In the Variable value: field, type %USERPROFILE%\Local Settings\Application Data

How do I install In the Box?

Click on this Visual Studio Gallery Link and install from there.

After I install it, how do I get started?

Open Visual Studio 2010; File, New Project, Visual C#, In the Box, MVVMTraining.

NewProject

Videos

In the Box – MVVM Training Introduction video

You can view the above video using the web browser plug in, or you can download and watch an HD version of the video.  To download the HD version of the video, locate the, “About this video” section (lower right side of web page) and join Vimeo; it’s free and very quick to join.

Vimeo

Downloads

In the Box – MVVM Training on the Visual Studio Gallery

Visual Studio Express and Visual Studio 2008 Developers download the written content and sample application from my:

SkyDrive click here to download.

Note the link is to a folder; you will want to download both .zip files in the folder.

Expression Blend 4 SDK Requirement

I received a report from a developer having a problem with two references to the Expression Blend 4 SDK in two of the included projects.

I thought for awhile about including just these two DLL’s, but thought that would be a disservice, only providing two DLL’s.  I have opted to add the Expression Blend 4 SDK as a requirement because a high percentage of developers already have the SDK installed by Blend or by downloading it.

Have a great day,

Just a grain of sand on the worlds beaches.


Windows Phone 7 – Application Lifecycle – Ocean for the Phone

October 17, 2010

travelreport

On October 9th 2010 I did a presentation at the Silicon Valley Code Camp on the Application Lifecycle for Windows Phone 7.  This last week was KA-ray-zeee busy at work and I didn’t want to just post the slides and code I had at code camp.  Better to wait and really do a good job, so here it is.

Introduction

First, I would like to acknowledge the outstanding decks and examples that the Microsoft Windows Phone 7 Evangelism Team has been and continues to publish.  Team members I’ve been working with are Yochay Kiriaty and Jaime Rodriguez.

I created the provided deck from several decks published by the Evangelism Team.  I edited and added content for the session I did.

For the three example applications, I studied the Evangelism Team’s material, samples on the web, added many hours of learning and ported Ocean to the Windows Phone 7.

This was a lot of fun, in-fact more pure fun than I’ve had in a while.  Don’t get my wrong, I love what I do everyday and the opportunities I’ve been blessed with at Microsoft.  Maybe because the phone is new, I didn’t really know much about it, but was able to understand it and get productive quickly.  Additionally, the Visual Studio 2010 tooling worked great. 

No other way to say it, “developing on Windows Phone 7 is a lot of fun.”  Very much looking forward to getting my own device.

I’m encouraged that Microsoft has a commitment to the XAML language and that I was able to leverage my huge investment in WPF with Silverlight 3-4, and now the Windows Phone 7.  The ability to transfer my investment across platforms and devices is a fantastic benefit to developers on Microsoft’s stacks.

Let me now turn our attention to what is in the box (the download).

Presentation Slide Deck

The deck is in the solution folder and explains each topic clearly.  The application lifecycle concepts are repeated several times in different ways so the reader can grasp tombstonning.

The code snippets in the deck come from TravelReporting project and the OceanFramework.Phone project.

ApplicationLifeCycle Project

The purpose of this application is to provide a nice clear log of the application’s constructors, methods and events as they execute.  It is critical to your success that you learn the phone’s application lifecycle.  This program can help with that.

Crack the App.xaml open and look at the CustomHyperlinkButtonStyle.  I use this to make a Hyperlink control look and act like a Button.  I do this so that I can navigate from XAML and not have to wire up a command or click event handler.  You’ll see this style used in all three applications where appropriate.

Notice how clean my debug output window is.  That’s because I’ve turned off the other messages.  To turn off the other messages, right click the debug output window while debugging.  Then unselect messages on the context menu that you do not want to see.  Below, I just want to see my phone messages, so I’ve left Program Output selected.

DebugWindow

Logger

Creating the log messages is very simple, just call Logger.Log();  You can also pass an optional notes string.

void Application_Launching(Object sender, LaunchingEventArgs e) {
    Logger.Log();
}

The magic is in the below Log method.  By creating a StackTrace object, I can extract the calling type and method from the appropriate stack frame.  This type of logging is much better than having to craft each individual log message.  I’ve used this class in each of the projects.

namespace ApplicationLifeCycle.Infrastructure {
    public class Logger {

        public static void Log(String notes = "") {
            StackTrace stackTrace = new StackTrace();

            String typeName = stackTrace.GetFrame(1).GetMethod().DeclaringType.Name;
            String methodName = stackTrace.GetFrame(1).GetMethod().Name;

            if(!String.IsNullOrEmpty(notes)) {
                notes = String.Concat(" - ", notes);
            }
            Debug.WriteLine(String.Format("WP7: {0} - {1}{2}", typeName, methodName, notes));
        }

        private Logger() {
        }
    }
}

LaunchersAndChoosers Project

This project demonstrates 3 launchers and 3 choosers.  It also provides a lot of logging so that you can see what goes on and when.  Spend some time, using the application.  Then predict the sequence of events before executing them, you’ll have the application lifecycle down quickly.

Take a look at the MainPage.xaml and MainPage.xaml.cs.  By using the CustomHyperlinkButtonStyle, I was able to run code-free in the code-behind.

TravelReporting Project

This application allows a user to enter a travel report from their phone.  The travel report has two pages of required data.  The user can’t proceed to the second page unless the first page is valid. 

For validation I’m using the declarative (attribute based) validation offered by the Ocean framework.  The validation API’s allow for multiple rule-sets.  This enabled me to have a set of rules for the first page’s fields and a set of rules to validate the entire object on page two.

IDataErrorInfo is not supported in this release of Silverlight for Windows Phone 7.  I have included the interface so that you can have it in place if and when it is supported.  In this simple application, I’m not bothering the user with messages until they press the Next or Save buttons.  When Next or Saved is pressed, the TravelReport object is validated against the appropriate rules.

To see the validation attributes in action, have a look at the TravelReport.cs file.  You’ll also see several rules added in code.

When the travel report is saved it is added to the history file. 

The History page  is accessed from the main page’s menu.  The history presents a list of travel reports and allows viewing the report in detail.

The history file simulates accessing the cloud for historical data.  When the application starts, two travel reports are added to the history file to give you something to look at.

Extras

TombstoneSupportPhoneApplicationPage (OceanFramework.Phone)

This is the base class for all my pages.  It adds automatic tombstone activating and deactivating support so that I don’t have to repeat this code on each page.

One challenge developers have is this business of focus not changing when the user presses the back, start, application button or menu.  The problem is, control data bindings do not update their source until lost focus occurs.  Since lost focus does not happen when the user does one of these actions, the user could loose that field’s data that was entered but not committed to the source.  Remember, when a user presses the start button, the application will be tombstoned and if the field’s data was not committed to the model or view model before those objects get persisted, then the data that was entered is lost.

Not to worry.  The below method handles this quirk for you.  Take note of the below TODO.  My current application only has TextBoxes that need this help.  Your applications may need additional tests for other controls.

This method also records the name of the currently focused control so that when the application comes back from tombstonning, the focus field can be restored.

protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e) {
    base.OnNavigatingFrom(e);

    // Remove previous focused element
    if (State.ContainsKey(FOCUSED_ELEMENT)) {
        State.Remove(FOCUSED_ELEMENT);
    }

    // If an input control has focus, perform an explict binding update.
    // If the user pressed the back button, start button or application button,
    // the binding expression will not process since focus did not change.
    // So... you need to force the binding expression to update so that the
    // model property will be updated.
    var focusedElement = FocusManager.GetFocusedElement() as Control;
    if (focusedElement != null) {

        if (!String.IsNullOrEmpty(focusedElement.Name)) {
            State.Add(FOCUSED_ELEMENT, focusedElement.Name);
        }

        BindingExpression be = null;

        //TODO - Developers, add additional controls here like a date picker, combobox, etc.
        if (focusedElement is TextBox) {
            be = focusedElement.GetBindingExpression(TextBox.TextProperty);
        }

        if (be != null) {
            be.UpdateSource();
        }
    }
}

IsolatedStorageFacade (OceanFramework.Phone)

This facade class provides a simple interface for interacting with the IsolatedStorageFile and IsolatedStorageSettings.  You’ll want to modify this to meet you specific needs.

I made all the methods static for ease of use.  If I was using an IOC container, I would provide an interface and inject an implementation.  This would also make testing easier since the implementation could be swapped out.

TombstoneFacade (OceanFramework.Phone)

This facade class provides a simple interface for interacting with the PhoneApplicationService temporary storage state bag.  You’ll want to modify this to meet you specific needs.

I made all the methods static for ease of use.  If I was using an IOC container, I would provide an interface and inject an implementation.  This would also make testing easier since the implementation could be swapped out.

Globalization

I met with my great friend Laurent Bugnion of MVVM-Light fame this week and spent time taking about Windows Phone 7.  I asked him about globalization of the phone apps.  He explained his solution for using .resx files and I’m sharing it with you.

The Localizer class is a front end that we can instantiate in XAML, then controls can data bind directly to the properties in the .resx file.  This wrapper is required because the .resx class, can’t be instantiated in XAML, even if it is marked public.  This may be a bug, I’ll check up on this.

// Many thanks to Laurent Bugnion http://www.galasoft.ch/ for this cool tip.
// This wrapper around the Strings.resx allows us to data bind to values in XAML
// See App.xaml and MainPage.xaml to see the moving parts.  Very simple solution.
public class Localizer {

    Strings _strings;

    public Strings Strings {
        get { return _strings; }
    }

    public Localizer() {
        _strings = new Strings();
    }
}

This XAML is from App.xaml.

<infrastructure:Localizer x:Key="LocalizedStrings" />

The below TextBlock displays a globalized string for the application title.  All strings in the TravelReports application are resources.

<TextBlock Style="{StaticResource PhoneTextNormalStyle}"
    Text="{Binding Source={StaticResource LocalizedStrings}, 
           Path=Strings.Application_Title, Mode=OneTime}" />

Download

The download is on my sky drive here.

Remember after downloading a .zip file, you’ll want to right click on the file and “Unblock” the file.  This is the mark-of-the-web that Visual Studio 2010 respects.

Have a great day.

Just a grain of sand on the worlds beaches.


Follow

Get every new post delivered to your Inbox.

Join 244 other followers