ASP.NET MVC2 Framework and Unity Dependency Injection Container

by 11. August 2009 01:48

SOURCE CODE AVAILABLE HERE

As always the new Microsoft Technologies keep rolling out the door; each one making the prior obsolete because the new technologies keep making things easier and easier.

Having been bogged down by the CompositeWPF/PRISM and ISO-15926 (on the international development team) I had no room on even my backburner for MVC - at least until now :)

As with every technology, if I can't use Dependency Injection (more specifically Unity) right out of the box then this is the first thing I must do before moving forward - MVC was no different.  

Unfortunately the existing blogs, such as David Hadens (which I found instrumental in completing my objectives) were out of date.  MVC2 will pull the rug out from under these blogs because the IControllerFactory interface for CreateController no longer provides a "type" - it provides a "string" which will simply hold the controllerName; not as easy to work with.

public interface IControllerFactory
{
    IController CreateController(RequestContext requestContext, 
                                 string controllerName);
    void ReleaseController(IController controller);
}

Since the currently documented methods won't work and I'll want to ensure maximum backwards compatibility (now and in the future) I decided to take the Unity Interception route.    I will get the current factory (line 7/8) and register it as an instance (line 13) so that later when we Resolve IControllerFactory in the CustomControllerFactory (line 14) it will be wrapped as required for interception.   Notice that we also register a named IControllerFactory so that we can resolve it to specify it as the new ControllerFactory (line 31).

GlobalBase.cs in MVCContrib\Base
    1 /// <summary>
    2 /// Initializes the ControllerFactory interception.
    3 /// </summary>
    4 private void InitializeInterception()
    5 {
    6     // Get a reference to the current controller factory
    7     IControllerFactory factory =
    8         ControllerBuilder.Current.GetControllerFactory();
    9 
   10     // Configure Interception - note we register an instance of
   11     // the factory so that we can retrieve it in CustomerControllerFactory
   12     Container.AddNewExtension<Interception>()
   13         .RegisterInstance<IControllerFactory>(factory)
   14         .RegisterType<IControllerFactory,CustomControllerFactory>("Custom")
   15         .Configure<Interception>()
   16         .AddPolicy("ControllerPolicy")
   17         .AddMatchingRule(Container.Resolve<ControllerMatchingRule>())
   18         .AddCallHandler<ControllerCallHandler>();
   19 
   20     // Use Interface Interceptor
   21     Container.Configure<Interception>()
   22         .SetInterceptorFor<IControllerFactory>(new InterfaceInterceptor());
   23 
   24     // Now we'll need the factory reference (resolved by Unity) because 
   25     // it will be wrapped as required for interception
   26     IControllerFactory unityFactory = 
   27         Container.Resolve<IControllerFactory>("Custom");
   28 
   29     // Set the new ControllerFactory to the resolved factory 
   30     // which will be configured for interception
   31     ControllerBuilder.Current.SetControllerFactory(unityFactory);
  32 }

As noted above, in our CustomControllerFactory class we'll resolve the "Original" IControllerFactory (which will be wrapped for interception) and execute the CreateController(requestContext, controllerName) on it - we let MVC2 handle processing as designed.

CustomControllerFactory.cs in MVCContrib\Factories
    1 public IController CreateController(RequestContext requestContext, string controllerName)
    2 {
    3 
    4     Logger.Log("BEFORE CREATE:: CustomControllerFactory.CreateController()", 
    5         Category.Debug, Priority.None);
    6 
    7     // IControllerFactory without name will return the original 
    8     // ControllerFactory.  We're going going to let it do it's job
    9     // and get a reference to the controller.
   10     IController controller = Container.Resolve<IControllerFactory>()
   11         .CreateController(requestContext, controllerName);
   12 
   13     Logger.Log(string.Format("AFTER CREATE:: CustomControllerFactory.CreateController() CREATED [{0}]", 
   14         controller.GetType().Name), Category.Debug, Priority.None);
   15 
   16     return controller;
   17 }

Now we can let our configured ControllerCallHandler Intercept the CreateController() method - we let it execute and upon return have a reference to our controller (line 19).   With the reference we set the Container property (to a child container)

    1 public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    2 {
    3     IMethodReturn msg;
    4     try
    5     {
    6         Logger.Log(string.Format("BEFORE::ControllerCallHandler.Invoke for {0}",
    7             input.Target.GetType().Name), 
    8             Category.Debug, Priority.None);
    9 
   10         msg = getNext()(input, getNext);
   11 
   12         // Create child container for each
   13         IUnityContainer childContainer = Container.CreateChildContainer();
   14 
   15         // If the controller is being handled by MVCContrib - implements IControllerBase
   16         // then we'll set the Container.  Attempts to do a BuildUp here do not work as
   17         // expected so the Container Setter is expected to perform a Container.Buildup(this)
   18         // See Base\ControllerBase Container setter for details
   19         if (msg.ReturnValue is IControllerBase)
   20             ((IControllerBase)msg.ReturnValue).Container = childContainer;
   21 
   22         Logger.Log(string.Format("AFTER::ControllerCallHandler.Invoke for {0}",
   23             input.Target.GetType().Name),
   24             Category.Debug, Priority.None);
   25 
   26     }
   27     catch (Exception ex)
   28     {
   29         msg = input.CreateExceptionMethodReturn(ex);
   30     }
   31 
   32     return msg;
   33 }

The ControllerBase (which controllers will derive from) will wire-up the controller - the Container setter code follows:

public IUnityContainer Container
{
    get { return _container; }
    set
    {
        // Don't set if already set
        if (_container != null && _container.GetType().ToString() == value.GetType().ToString())
            return;
 
        _container = value;
 
        // Buildup of MVCControllerBase so that we can
        // have logger and future types
        value.BuildUp(this);
 
        // We'll have a Logger now that this is built up.
        Logger.Log("ControllerBase::Container (setter) -- Performing Buildup of " + GetType().Name,
            Category.Debug, Priority.None);
 
        // The type will be that of the derived class.
        // BuildUp of the parent controller
        value.BuildUp(GetType(), this);
 
        // Notify controller
        OnContainerSet();
    }
}

With the internals covered I'll move on to my primary objective - Extensibility. I created my MVCContrib project with the notion that minimal code changes will be required to implement Unity in my ASP.NET MVC2 projects.  

Starting with the Global.ascx.cs file you'll find we derive from GlobalBase.  We'll override the virtual methods (letting the base handle some wire-up) and for the most part keep the Global.ascx.cs file intact.  Note how we subscribe to the OnApplicationStart event. 

namespace MvcApplication2
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801
 
    /// <summary>
    /// MVC Application
    /// </summary>
    public class MvcApplication : GlobalBase 
    {
        public static bool IsLoaded { get; set; }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="MvcApplication"/> class.
        /// </summary>
        public MvcApplication()
        {
            if (!IsLoaded)
            {   // Ensure we only do this once
                this.OnApplicationStart += 
                    new EventHandler<EventArgs>(MvcApplication_OnApplicationStart);
                IsLoaded = true;
            }
        }
 
        /// <summary>
        /// Handles the OnApplicationStart event of the MvcApplication control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing 
        /// the event data.</param>
        void MvcApplication_OnApplicationStart(object sender, EventArgs e)
        {
            Logger.Log("OnApplicationStart", Category.Debug, Priority.None);
 
            RegisterRoutes(RouteTable.Routes);
        }
 
        /// <summary>
        /// Registers the routes.
        /// </summary>
        /// <param name="routes">The routes.</param>
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
            routes.MapRoute(
                "Default",                                             
                "{controller}/{action}/{id}",                          
                new { controller = "Home", action = "Index", id = "" } 
            );
 
        }
 
        /// <summary>
        /// Hook into InitializeContainer
        /// </summary>
        protected override void OnInitializeContainer()
        {
            Logger.Log("OnInitalizeContainer ", Category.Debug, Priority.None);
 
            // Configures container in class external to Global.asax.cs
            // (see Container folder)
            ContainerInitialize initContainer = 
                Container.Resolve<ContainerInitialize>();
            initContainer.InitializeContainer();
        }
    }
}

In my HomeController.cs you'll find that all I really needed to do is derive from MVVCControllerBase.  When I click on the HOME link the logger reveals the following processing - note how the EventAggregator (published by service) was handled (in bold): 

Debug(None): BEFORE CREATE:: CustomControllerFactory.CreateController()
Debug(None): ControllerMatchingRule.Matches(member=[System.Web.Mvc.IControllerFactory])
Debug(None): ControllerMatchingRule.Matches(member=[System.Web.Mvc.IControllerFactory])
Debug(None): BEFORE::ControllerCallHandler.Invoke for Wrapped_IControllerFactory_0fb5a472127c4b4b932425e809665c62
DEBUG: HomeController:CTOR
Debug(None): ControllerBase::Container (setter) -- Performing Buildup of HomeController
Debug(None): HomeController::OnContainerSet()
Debug(None): AFTER::ControllerCallHandler.Invoke for Wrapped_IControllerFactory_0fb5a472127c4b4b932425e809665c62
Debug(None): AFTER CREATE:: CustomControllerFactory.CreateController() CREATED [HomeController]
Debug(High): CONTROLLER:HomeController  -- Index() 
 
Debug(None): HomeController.DataServiceHandler STATUS = [3 Records sent]
 
Debug(None): PresentationModel.Clients updated with 3 records
Debug(None): ControllerMatchingRule.Matches(member=[System.Web.Mvc.IControllerFactory])
Debug(None): ControllerMatchingRule.Matches(member=[System.Web.Mvc.IControllerFactory])
Debug(None): BEFORE::ControllerCallHandler.Invoke for Wrapped_IControllerFactory_0fb5a472127c4b4b932425e809665c62
Debug(None): AFTER::ControllerCallHandler.Invoke for Wrapped_IControllerFactory_0fb5a472127c4b4b932425e809665c62
Debug(None): CustomControllerFactory.ReleaseController() RELEASED [HomeController]
 
 
HomeController.cs in MVCApplication2\Controllers
 
namespace MvcApplication2.Controllers
{
[HandleError]
public class HomeController : MVCControllerBase
{
    public HomeController()
    {
        Debug.WriteLine("HomeController:CTOR", "DEBUG");
    }
 
    [Dependency]
    public IDataService service { get; set; }
 
    [Dependency]
    public IPresentationModel model { get; set; }
 
    [Dependency]
    public IEventAggregator Aggregator { get; set; }
 
    [Dependency]
    public IAggregatorEventTokens Tokens { get; set; }
 
    /// <summary>
    /// We don't have constructor injection since we're using CustomControllerFactory
    /// so we'll rely on notification when the container is set.  This will be processed
    /// everytime a page is hit and our EventAggregator is a singleton so we'll have to
    /// ensure we only subscribe one time.
    /// </summary>
    protected override void OnContainerSet()
    {
 
        Logger.Log("HomeController::OnContainerSet()", Category.Debug, Priority.None);
 
        // GetTokens will only return null if key is not already set
        if (Tokens.GetToken(GetType().FullName) == null)
        {
            Logger.Log("HomeController::Subscribed to DataServiceEvent -- EVENT HANDLER",
                Category.Debug, Priority.None);
 
            // Subscribe to the DataServiceEvent
            SubscriptionToken token = Aggregator.GetEvent<DataServiceEvent>()
                .Subscribe(DataServiceHandler, ThreadOption.PublisherThread, true);
 
            // Set the token so we don't subscribe more than once
            Tokens.SetToken(GetType().FullName, token);
        }
    }
 
    public void DataServiceHandler(DataServiceEventArgs e)
    {
        Logger.Log(string.Format(
            "HomeController.DataServiceHandler STATUS = [{0}]", e.Status), 
            Category.Debug, Priority.None);
    }
 
    public ActionResult Index()
    {
        ViewData["Message"] = "Welcome to ASP.NET MVC!";
        Logger.Log("CONTROLLER:HomeController  -- Index() ", 
            Category.Debug, Priority.High);
 
        // Call the data service to get client list
        model.Clients = service.GetClients();
        return View();
    }
 
    public ActionResult About()
    {
        Logger.Log("CONTROLLER:HomeController  -- About()", 
            Category.Debug, Priority.High);
 
        ViewData["ModelClients"] = string.Format("There are {0} clients loaded!", 
            model.Clients.Count);
        return View();
    }
}
}

SOURCE CODE AVAILABLE HERE

SL3 TabItem HeaderTemplate does not support DataBinding - PRISM work-around

by 6. August 2009 09:18

An excerpt from this message thread on Silverlight.net follows:

TabControl and Databinding is a sore point for a while now. You can show your support in fixing it on this codeplex bug:
http://silverlight.codeplex.com/WorkItem/View.aspx?WorkItemId=3604 

We're planning on overhauling TabControl for the Silverlight vNext release.

Sincerely,

--
Justin Angel,
Blog @ http://silverlight.net/blogs/JustinAngel
Twitter @ http://twitter.com/JustinAngel

The problem reveals itself in the PRISM View Injection QuickStart: 

Since PRISM has a TabcontrolRegionAdapter for Silverlight we have a hook into the problem (for a work-around).  Excerpt from documentation on the BootStrapper follows:

Conditional compiling is used to register the TabControlRegionAdapter only for Silverlight. This is because when you add an item in a TabControl control in WPF, this item is wrapped inside a TabItem type. This is not applicable for Silverlight, where you must manually wrap the item into the TabItem; because of that, the TabControlRegionAdapter adapter was modified to both adapt a region to the TabControl control and to automatically add this wrapping.

PRISM users can update the TabControlRegionAdapter.cs, in the Composite.Presentation project, as follows:

private static TabItem PrepareContainerForItem(object item, DependencyObject parent)
{
TabItem container = item as
TabItem;
if (container == null
)
{
    container = new
TabItem();
    container.Content = item;
    container.DataContext = GetDataContext(item);
    container.Style = GetItemContainerStyle(parent);
    container.SetValue(IsGeneratedProperty, true
);

    // BillKrat.2009.08.06 - SL3 Template binding bug workaround

    if (container.Header != null
         && container.Header.ToString().Contains("Binding:"
))
    {
     PropertyInfo modelProperty = null
;

     // Strip "Binding:" from the header - leaving model property

     string modelPropName = container.Header.ToString().Replace("Binding:", ""
);

     // Get model properties and search for the property name provided

     PropertyInfo[] propInfo = container.DataContext.GetType().GetProperties();

    
if (propInfo != null
)
        modelProperty =
         propInfo.FirstOrDefault(p => p.Name.ToString() == modelPropName);

     // If found then update the header with the value

     if (modelProperty != null
)
        container.Header = modelProperty.GetValue(modelProperty, null
);
    }
}

return
container;
}

Instead of using Value="{Binding HeaderInfo}", which won't work under SL3, you can use "Binding:HeaderInfo" which will be processed by the above method.  (note: Header will be set/available when the container.Style property is set above).  An example follows:


<Regions:TabControlRegionAdapter.ItemContainerStyle>
    <Style TargetType="Controls:TabItem">
        <Setter Property="Header" Value="Binding:HeaderInfo"/>
    </Style>
</Regions:TabControlRegionAdapter.ItemContainerStyle>

Until the Silverlight Toolkit team resolves the issue this can serve as a work-around.   With some work it could be made more dynamic but this is a good starting point.

 

The model being used follows:

namespace UIComposition.Modules.Project
{
    public class
ProjectsListPresentationModel
    {
        public ObservableCollection<BusinessEntities.Project> Projects { get; set
; }

        public static string
HeaderInfo
        {
            get { return "Current Projects"
; }
        }
    }
}

EDITED - a better solution was provided on the following link.  Although I haven't tested it yet I was impressed that it resolves the problem with one line of code - my kind of fix!   I found the above to be a great learning exercise in the power of styles so I leave it intact as is.

PRISM User forum addressing the issue HERE

Does PRISM have a performance issue?

by 31. July 2009 22:21

A developer in the PRISM forum was noting that the Shell (main app) loaded a control faster than a view (injected into a region) could load the very same control.   As THIS DEMO will verify the user control loaded much faster within the shell (bottom) and took up to five seconds to load within a module's region (top).   Once I refactored his code you will see that not only does the control load very fast but also the application (patience will be required for the demo code it is very slow).

His comment follows:

I'm working on a simple demo application using the compoiste Application Library for WPF. I have an issue in that when I inject a user control into a region in the shell there is a considerable lag in the control rendering and being visible. When I include the same control in the shell (not in a referenced IModule) it appears instantly. I understand that I must take a performance hit at the point when I load controls into my shell from other modules but the delay is quite extreme. The control itself is a dataGrid (WPFToolkit) with around 2000 rows - there is no database work being done to generate the data for the grid.

Is there any reason Im getting such poor performance? The lag in the control rendering is around 5 seconds - a complete show stopper for using prism if it cannot do something so simple. more

Source code provided by developer demonstrating the issue: CALDemoOld.zip (2.13 mb)
Refactored source code using best Practices and Patterns (P&P): CALDemo.zip (3.50 mb)

There were some inherent design flaws that introduced this issue; which have nothing to do with PRISM (as shown by the demo link above).

The following control is the topic of this discussion - note how in the code-behind that we are populating TestEntities with 1000 elements and then applying it to the data context.


Figure A. 

Below is the XAML for the Shell - note that it simply references the Control.   When the Shell is loaded the code (above) will execute effectively populating the control within the Shell.


Figure B. 

As you can see below in the sequence diagram the CreateShell process (which actually does a Shell.Show()) will execute, and display the control long before the InitializeModules() command fires (which will place the same control within the MainRegion above).   So this explains the increased performance for the Shell's load of the control


Figure C. 

So what about the lengthy delay?   If you were to put a break point after InitializeModules you would find that the Shell's control is visible and populated while the Regions control is still not visible - our processing logic within the controls code-behind is bogging down the UI thread. 

What we need to do is pull the Data Access Layer (populating 1000 elements) out of the presentation layer and have it process asynchronously on a separate thread.   In the case of my refactored code I update the model which the MainRegions control is databound to.

Applying the Model-View-Presenter (MVP) pattern against the control we now only have to have the following code to populate our model (which the view is bound to).  The PresenterBase subscribes to an event raised in the Bootstrapper when modules are initialized so that it can start the asych process of populating the model.TestEntities property (handled by OnProcessEvent below).  Since model.TestEntities is an observableCollection WPF will automatically be notified as the collection changes - which is why we pass it in as a parameter after every 15 elements we allow event processing to continue which effectively will refresh our Grid display every 15 elements (more on this below).


Figure D.

Below is our refactored BootStrapper which launches a new thread to handle the data processing


Figure E.

Note how on line 17 of Figure D above that we are using Dependency Injection (DI) to get our data access layer.   In the Services folder we implement IEntityDataService using stubbed data - later the actual data layer could eaily replace the "stub" by simply updating the modules Initialize method to reflect the new implementation - in the case of this demo we are using EntityDataStub.

public void Initialize()
{
    container
        .RegisterType<
IMyControl, MyControl>()
        .RegisterType<
IEntityDataService, EntityDataStub>()
        .RegisterType<
IMyControlPresentationModel, MyControlPresentationModel>(
        new ContainerControlledLifetimeManager()); // Singleton

Notice on line 21 below we are borrowing a feature from System.Windows.Forms.   The Application.DoEvents() method permits other events to process.  In the absence of this the below process is hogging up the CPU cycles and will not give other events a chance to process until it is completed - on lengthy delays your form could "stop responding".  As a result of this command our grid will refresh every 15 elements which will be reflected by the behavior of the scrollbar.

Architectural note:  This demo uses a combination of the Model-View-Presenter (MVP) pattern and the Model-View-ViewModel (MVVM) pattern, aka Presentation Model and Application Model.    By having multiple views share the same model (see Martin Fowlers Presentation Model) you can effectively share the same data without having to have a lot of complex logic to maintain state.   Each view can update the model and the other views will be notified via the observer pattern (INotifyPropertyChanged).

Trying to use MVVM alone has introduced the limitations that Martin Fowler discussed in THIS ARTICLE (paragraph above Figure 11).   As he suggest, it was the limitations that introduced the need for MVP.   Combining them gives us the best of both worlds.  

 

How to use .NET RIA Services in PRISM

by 27. July 2009 21:20

For Webcast overview of process CLICK HEREImportant note: the Presenter being shown on the screen during the Webcast is actually being utilized by the Silverlight, RIA, WPF and Winforms applications (they all utilize the PRISM framework using the Model-View-Presenter and Model-View-ViewModel patterns - see architectural note at end of blog). 

Tim Heuer's presentation on RIA services for Silverlight 3
http://silverlight.net/learn/learnvideo.aspx?video=245417

Tim Heuer does an excellent presentation of RIA services on the above link.   The problem for PRISM developers is that the RIA data access layer resides in the presentation layer’s bootstrap application RIABusApp (ref image below).   Since our RIABusApp will reference our module(s) this poses a problem because now our modules cannot reference our RIA data layer (which resides in RIABusApp) because of a circular reference.   The key to making real RIA work with PRISM is to be able to have the modules reference the bootstrap application RIABusApp.

Fortunately Silverlight allows us to load assemblies dynamically.  With a few lines of code we can load the module and call its initialize mapping without ever having made a reference to the Module.  This is practical with PRISM because most of the business logic will reside in a module minimizing the requirement for any references in RIABusApp.

Code follows:

using System;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Unity;
using PrismContrib.Base;
using PrismContrib.Events;
using PrismContrib.Interfaces;

namespace RIABusApp
{
    public partial class App : Application
    {
        private IBootstrapperProcess bootstrapperProcess = null;

        [Dependency]
        public IUnityContainer Container { get; set; }
        private IUnityContainer container; 

        public App()
        {
            this.Startup += this.Application_Startup;
            this.UnhandledException += this.Application_UnhandledException;

            InitializeComponent();
        }

        void Application_Startup(object sender, StartupEventArgs e)
        {
            this.Resources.Add("RiaContext", RiaContext.Current);
            Bootstrapper<MainPage> bootstrap = new Bootstrapper<MainPage>();
            bootstrap.OnConfigureContainer += OnConfigureContainer;
            bootstrap.OnInitializeModules += OnInitializeModules;
            bootstrap.InitParams = e.InitParams;
            bootstrap.Run();
        }
        /// <summary>
        /// Dynamically load the Mapping Module because it will have
        /// a reference set to this assembly to gain access to the
        /// Services\DataLayerDefault class
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="PrismContrib.Events.BootStrapperEventArgs"/>
        /// instance containing the event data.</param>
        void OnInitializeModules(object sender, BootStrapperEventArgs e)
        {
            container = e.Container;
            WebClient client = new WebClient();

            client.OpenReadCompleted +=
                new OpenReadCompletedEventHandler(OnAssemblyOpened);

            // Requires assembly to be in the RIABusApp.Web ClientBin folder
            client.OpenReadAsync(
                new Uri("ISO15926.Module.Mapping.Silverlight.dll", UriKind.Relative));
        }
        void OnAssemblyOpened(object sender, OpenReadCompletedEventArgs e)
        {
            AssemblyPart assemblyPart = new AssemblyPart();
            Assembly assembly = assemblyPart.Load(e.Result);

            // Find our mapping module in the list returned by GetTypes
            Type mappingType = assembly.GetTypes()
                .First(m => m.FullName.Contains("MappingModule"));

            // Dynamically instantiate our module
            IModule module = (IModule) Activator.CreateInstance(mappingType);

            if (module != null)
            {
                // Buildup doesn't seem to be working on dynamically
                // created class...  Will set container manually
                // => container.BuildUp(module);
                ((IContainer)module).Container = container;
                module.Initialize();
            }
        }

        void OnConfigureContainer(object sender, PrismContrib.Events.BootStrapperEventArgs e)
        {
            e.Container.RegisterType<IControlProcessor, ControlProcessorWPF>();
        }

        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            // If the app is running outside of the debugger then report the exception using
            // a ChildWindow control.
            if (!System.Diagnostics.Debugger.IsAttached)
            {
                // NOTE: This will allow the application to continue running after an exception has been thrown
                // but not handled.
                // For production applications this error handling should be replaced with something that will
                // report the error to the website and stop the application.
                e.Handled = true;
                ChildWindow errorWin = new ErrorWindow(e.ExceptionObject);
                errorWin.Show();
            }
        }
    }
}

The application that we're using for this blog is a multi-targeting application; this means that we can have multiple platforms using the same code (as shown in the projects underlined above).  In this case RIA Services will be utilizing the Silverlight module.  Below you'll see all of the client side code that is required to return a class list, as Tim noted in his presentation all of the projection is handled for us.

The code on server side is no more complicated as shown below.

In PRISM, because of dependency injection, all we have to do to consume our service is request a reference to IDataService (constructor injection), request the classes and assign the results to the model.Classes property.

Note: The SemWeb infrastructure (which utilizes my own PRISMContrib project) handles the event notification for us.   SemWeb is scheduled to be released late August at http://www.CodePlex.com/SemWeb.

The source code for the MappingPresenter follows: 

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using ISO15926.Library;
using ISO15926.Module.Mapping.Services;
using Microsoft.Practices.Unity;
using Microsoft.Windows.Controls;
using PrismContrib.Base;
using PrismContrib.Events;

namespace ISO15926.Module.Mapping.Views.InformationModel
{
    /// <summary>
    /// Information Model presenter
    /// </summary>
    public class InformationModelPresenter : PresenterBase<IInformationModelView>
    {
        private ISemWebPresentationModel model;

        private DataGrid grdClasses {
            get { return GetControl<DataGrid>("dgClasses"); }
        }

        /// <summary>
        /// Initializes a new instance of the
        /// <see cref="InformationModelPresenter"/> class.
        /// </summary>
        /// <param name="view">The view.</param>
        /// <param name="model">The model.</param>
        /// <param name="container">The container.</param>
        /// <param name="riaDAL">The ria DAL.</param>
        public InformationModelPresenter(
            IInformationModelView view,
            ISemWebPresentationModel model,
            IUnityContainer container,
            IDataService riaDAL) : base(view, model, container)
        {
            this.model = model;

            // Populate model with ISO15926 Class
            model.Classes = riaDAL.GetClassList();
        }

        private void OnDataLoaded()
        {
            grdClasses.ItemsSource = model.Classes;
        }

        #region OVERRIDES of baseclass methods  

        #region METHOD: OnViewSizeSet
        /// <summary>
        /// Called when [view size set].  Sets the data grid dimensions
        /// to the current view
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.SizeChangedEventArgs"/> instance containing the event data.</param>
        protected override void OnViewSizeSet(object sender, SizeChangedEventArgs e)
        {
            // Filter for this view
            IInformationModelView view = sender as IInformationModelView;
            if (view == null)
                return;

            // Set grid height and width to view dimensions so that
            // we'll have scrollbars
            grdClasses.Height = e.NewSize.Height;
            grdClasses.Width = e.NewSize.Width;

            base.OnViewSizeSet(sender, e);
        }
       
        #endregion
        #region METHOD: OnButtonClickEventHandler
        /// <summary>
        /// Handles all Button/CheckBox Click events
        /// <see cref="E:ClickEventHandler"/>
        /// </summary>
        /// <param name="e">The <see cref="ClickEventArgs"/>
        /// instance containing the event data.</param>
        public override void OnButtonClickEventHandler(ClickEventArgs e)
        {
            e.IsHandled = e.Name.Contains("DetailsView_ClickMe");

            if (e.IsHandled)
                MessageBox.Show(string.Format("ISO159296 Classes record count = {0}",
                        model.Classes.Count));

            base.OnButtonClickEventHandler(e);

        }
       
        #endregion
        #region METHOD: OnDALEventHandler
        /// <summary>
        /// Raises the <see cref="E:DALEventHandler"/> event.
        /// </summary>
        /// <param name="e">The <see cref="PrismContrib.Events.DALEventArgs"/>
        /// instance containing the event data.</param>
        public override void OnDALEventHandler(DALEventArgs e)
        {
            // We're only interested in IsLoading context property changes
            if (e.IsPropertyChange && e.PropertyName.Contains("IsLoading"))
            {
                if (!e.IsLoading)
                {
                    e.IsHandled = true;  // notify logging we handled it
                    base.OnDALEventHandler(e);
                    OnDataLoaded();
                }
            }
            else  // For logging purposes
                base.OnDALEventHandler(e);

        }
       
        #endregion       
        #region METHOD: OnModelPropertyChanged 

        /// <summary>
        /// Called when [model property changed].  Intended to be overridden
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/>
        /// instance containing the event data.</param>
        public override void OnModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
#if !SILVERLIGHT
            // WPF/WinForms are using stub data so there is no callback method
            // to trigger OnDataLoaded() - we'll fire it manually on
            // ModelPropertyChanged
            if (e.PropertyName.Contains("Classes"))
                OnDataLoaded();
#endif
        }
       
        #endregion        

        #endregion
    }
}
 

Architectural note:  This application uses a combination of the Model-View-Presenter (MVP) pattern and the Model-View-ViewModel (MVVM) pattern, aka Presentation Model and Application Model.    By having multiple views share the same model (see Martin Fowlers Presentation Model) you can effectively share the same data without having to have a lot of complex logic to maintain state.   Each view can update the model and the other views will be notified via the observer pattern (INotifyPropertyChanged).

Trying to use MVVM alone has introduced the limitations that Martin Fowler discussed in THIS ARTICLE (paragraph above Figure 11).   As he suggest, it was the limitations that introduced the need for MVP.   Combining them gives us the best of both worlds.  

 

Silverlight and WPF - CompositeWPF/Prism supports multi-targeting (single shared codebase)

by 6. March 2009 11:00

Source Code available at http://www.Codeplex.com/SDMS 
Project Linker Blog w/Webcast HERE  

A recent Microsoft Codeplex forum interaction had me updating the SDMS application so that the Employee Module and all of it's views were Multi-targeting (worked on both Desktop and Silverlight).  The reasoning follows below: 

Developer writes "I'm not looking for multi-targeting support, I just want an SL app that uses WCF and is maintainable and testable."

My response follows:

I don't think they are mutually exclusive - I believe you may want/need multi-targeting support.   Let me explain by example - I just spent the last couple of hours updating the SDMS application so that the Modules folder is supported under both Desktop and Silverlight.  Why?   "maintainable and testable".   I created a Unit Test for you and checked everything in (been really wanting to do this for a while and this gave me good reason to).

The key point here is that the "only" code that will be different will be the XAML (Silverlight/WPF) and the actual WCF Service call.  I did however create my Desktop WCF Service using Async communications so you won't find any SILVERLIGHT conditional statements anywhere in the Business Logic Layer or Data Layer (they are one and the same code for both sides).   I should give a plug for the Project Linker (blogged about on my blog site w/webcast); all my time was spent creating empty WPF views and implementing the interface on them.

[TestMethod]

public void TestMethod1()

{

    // IModule does all of the heavy lifting - configures all interfaces

    // so we'll just use it to set things up.

    IModule module = Container.Resolve<IModule>();

    module.Initialize();

 

    // Resolve the EmployeeList Presenter

    EmployeeListPresenter MockView = Container.Resolve<EmployeeListPresenter>();

 

    // Give WCF Service a chance to complete

    Thread.Sleep(2000);

 

    // Cast so we can easily access presentation model

    EmployeePresentationModel model =
             (EmployeePresentationModel) MockView.Model;

 

    Assert.AreEqual(3, model.EmployeeList.Count,
                   "Employee list should have three records!");

}


Note we can put the Testing thread to sleep :)  I just tested everything short of the UI which is databound to the presentation model (nothing to test in the view) all the way through the WCF Service and back.  Since my PresentationModel implements INotifyPropertyChanged I can rest assured my View will work (assuming I did my Binding correctly).

Let's see what IModule was up to (showing the effectiveness of multi-targeting)

 

 

public class ModuleEmployee : IModule

{

    // For class use

    private readonly IUnityContainer container;

    private readonly IRegionViewRegistry regionViewRegistry;

 

    /// <summary>

    /// Constructor : Setup class

    /// </summary>

    /// <param name="container"></param>

    /// <param name="regionViewRegistry"></param>

    public ModuleEmployee(IUnityContainer container,

        IRegionViewRegistry regionViewRegistry)

    {

        this.container = container;

        this.regionViewRegistry = regionViewRegistry;

    }

 

    public void Initialize()

    {

        RegisterViewAndServices();

 

        // EmployeeModule - Views folder

        regionViewRegistry.RegisterViewWithRegion("MainRegion",

            () => container.Resolve<EmployeeMainPresenter>().View);

 

        regionViewRegistry.RegisterViewWithRegion("frmCaption",

            () => container.Resolve<frmCaptionPresenter>().View);

 

        regionViewRegistry.RegisterViewWithRegion("frmEmployeeList",

            () => container.Resolve<EmployeeListPresenter>().View);

 

        regionViewRegistry.RegisterViewWithRegion("TabInformation",

            () => container.Resolve<EmployeeInformationPresenter>().View);

 

        regionViewRegistry.RegisterViewWithRegion("TabAssigned",

           () => container.Resolve<EmployeeAssignedPresenter>().View);

 

        regionViewRegistry.RegisterViewWithRegion("TabInWork",

           () => container.Resolve<EmployeeInWorkPresenter>().View);

 

        regionViewRegistry.RegisterViewWithRegion("frmStatus",

            () => container.Resolve<frmStatusPresenter>().View);

 

    }

 

    private void RegisterViewAndServices()

    {

        container.RegisterType<IEmployeeMainView, EmployeeMainView>()

 

            // Layers

            .RegisterType<IEmployeeProviderBLL,EmployeeProviderBLL>()

            .RegisterType<IEmployeeProviderDAL,EmployeeProviderDAL>()

 

            // Views

            .RegisterType<IfrmStatusView, frmStatusView>()

            .RegisterType<IfrmCaptionView, frmCaptionView>()

            .RegisterType<IEmployeeListView, EmployeeListView>()

            .RegisterType<IEmployeeListView, EmployeeListView>()

            .RegisterType<IEmployeeInWorkView, EmployeeInWorkView>()

            .RegisterType<IEmployeeAssignedView, EmployeeAssignedView>()

            .RegisterType<IEmployeeInformationView, EmployeeInformationView>()

 

            // Services

            .RegisterType<IEmployeeService, EmployeeService>()

 

            // Models

            .RegisterType<IEmployeePresentationModel, EmployeePresentationModel>(

                            new ContainerControlledLifetimeManager());

    }

}

}


It did some pretty heavy lifting which tells me everything that will be executed during Silverlight runtime - works. 

The following is the Presenter, which is responsible for updating the Presentation Model (which the view is observing).  You can see that my Desktop Unit Test effectively exercises many, if not all, logic within the process.  

 

Note: Silverlight unit testing is done in a browser...  I'd rather take this approach.

Hope this helps in your quest to finding an architecture that works for you!

Bill


 

public class EmployeeListPresenter : PresenterBase<IEmployeeListView>

{

    readonly IEmployeeService employeeService;

    readonly IEventAggregator aggregator;

    readonly IEmployeePresentationModel model;

 

    /// <summary>

    /// Constructor : setup class

    /// </summary>

    /// <param name="container"></param>

    /// <param name="view"></param>

    public EmployeeListPresenter(

        IEmployeeListView view,

        IEmployeePresentationModel model,

        IUnityContainer container,

        IEventAggregator aggregator,

        IEmployeeService service) : base(view,model,container)

    {

        this.aggregator = aggregator;

        this.employeeService = service;

        this.model = model;

 

        // Subscribe to ListBoxChanged event and

        aggregator.GetEvent<ListBoxChangedEvent>()
                  .Subscribe(ListBoxChangedEventHandler, true);

        aggregator.GetEvent<EmployeeEvent>()
                  .Subscribe(EmployeeEventHandler, true);

 

        // Async call to service to populate employee list. 
        // The EmployeeListEventHandler
will be called when
        // data is received

        employeeService.GetEmployeeList();

    }

 

    /// <summary>

    /// Subscribed to in constructor - updates the model's
    /// SelectedEmployee property every
time a new employee is selected

    /// </summary>

    /// <param name="args"></param>

    private void ListBoxChangedEventHandler(SelectionChangedEventArgs args)

    {

        model.SelectedEmployee = args.AddedItems[0] as Employee_Data;

 

        StatusBarEvent sbEvent = aggregator.GetEvent<StatusBarEvent>();

        if (sbEvent != null)

            aggregator.GetEvent<StatusBarEvent>().Publish(

                new StatusBarData

                {

                    Message = string.Format("You clicked {0}",
                                    model.SelectedEmployee.DisplayValue),

                    Panel = StatusPanel.Left

                });

    }

 

    /// <summary>

    /// Handler for when Employee list is returned by service call to

    /// GetEmployeeList()

    /// </summary>

    /// <param name="args"></param>

    private void EmployeeEventHandler(EmployeeEventArgs args)

    {

        model.EmployeeList = args.EmployeeList;

    }

 }

CompositeWPF/Prism - Adding modules after BootStrapper

by 27. February 2009 06:32

The following sequence diagram shows us that the InitializeModules() method is the last process run http://global-webnet.net/UML/prism.htm

Examining the baseclass code we find that the following is what actually loads the modules; suggesting that it is safe to run again because of  the statement in bold:

       private void LoadModuleTypes(IEnumerable<ModuleInfo> moduleInfos)
       {
            if (moduleInfos == null)
            {
                return;
            }

            foreach (ModuleInfo moduleInfo in moduleInfos)
            {
                if (moduleInfo.State == ModuleState.NotStarted)
   
             {
                    if (this.ModuleNeedsRetrieval(moduleInfo))
                    {
                        this.BeginRetrievingModule(moduleInfo);
                    }
                    else
                    {
                        moduleInfo.State = ModuleState.ReadyForInitialization;
                    }
                }
            }
            this.LoadModulesThatAreReadyForLoad();
        }


I did a quick Proof of Concept with the StockTraderRI application to see if it was as easy as what was suggested to add a module after the BootStrapper process;  I added the line in bold below to the App() class

namespace StockTraderRI
{
    public partial class App : Application
    {
        public App()
        {
            this.Startup += this.Application_Startup;
            this.Exit += this.Application_Exit;
            this.UnhandledException += this.Application_UnhandledException;

            InitializeComponent();
        }

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            //this.RootVisual = new Shell();
            var bootstrapper = new StockTraderRIBootstrapper();
            bootstrapper.Run();

            // Watch module will not load without this line
            bootstrapper.PostCatalogProcessing(); 
        }

I then updated the StockTraderRIBootstrapper as follows. 

namespace StockTraderRI
{
    public partial class StockTraderRIBootstrapper : UnityBootstrapper
    {
        protected override IModuleCatalog GetModuleCatalog()
        {
            var catalog = new ModuleCatalog();
            catalog.AddModule(typeof(MarketModule))
                .AddModule(typeof(PositionModule), "MarketModule");
                //.AddModule(typeof(WatchModule), "MarketModule");   <= remarked out
                //.AddModule(typeof(NewsModule));                                <= remarked out

            return catalog;
        }

        public void PostCatalogProcessing()
        {
            ModuleCatalog catalog = (ModuleCatalog)Container.Resolve<IModuleCatalog>();
           
            catalog.AddModule(typeof(WatchModule), "MarketModule");
            catalog.AddModule(typeof(NewsModule));
            InitializeModules();
        }

Passing Server information to Silverlight Client

by 12. January 2009 12:02

Get Source Here (upgraded to Feb 2009 release of CompositeWPF/Prism)

Uses the CompositeWPF/Prism, Unity and the PresentationModel

Many of us ASP.NET Developers are accustomed to querying our HttpContext.Current object for information about our environment, i.e., we can use it's Server.MapPath("~"); to find our current folder path.

The problem with Silverlight is that it isn't running on the Server - it is running on the client (users computer); it doesn't have access to the HttpContext object.   This can result in having to hardcode information in a configuration file.

We can communicate with the server via WCF services, but it comes with it's own burden of configuration files (created by "Add Web Service") which can become unmanagable quickly in an enterprise level application with 20+ modules.   In recent communications with the Silverlight WCF Services Team I was notified that this is an issue they hope to resolve in SL3 (within the context of providing a SvcUtil.exe for Silverlight).

There is a way we can easily pass information to our Silverlight modules without having to hit the server again - after all, it was the server that served up the page.

THE KEY POINT IN OUR DEMO IS NO CONFIGURATION FILE IS REQUIRED BY OUR MODULE 

The data flow for our server information follows (once data hits the Unity Container it will be available to all modules):

 

For our demo we are working with two property values

  1. ServerURL (http://...) 
  2. ServerPath (C:\..)

Within the context of the attached project, our website's Default.aspx code behind file will load the InitParameters dynamically as follows:

    public partial class _Default : System.Web.UI.Page

    {

        protected void Page_Load(object sender, EventArgs e)

        {

            Xaml1.InitParameters = string.Format(

                "InitParameters="+

                "ServerURL=http://{0};"+

                "ServerPath={1}",

                    Request.Url.Authority,

                    Server.MapPath("~"),

                    ClientQueryString);

        }

    }

 

InitParameters is available via the Silverlight object (defaults to ID=Xaml1 in the default.aspx)

 

<html xmlns="http://www.w3.org/1999/xhtml" style="height:100%;">

<head id="Head1" runat="server">

    <title>UnityContrib.Demo.Silverlight</title>

</head>

<body style="height:100%;margin:0;">

    <form id="form1" runat="server" style="height:100%;">

        <asp:ScriptManager ID="ScriptManager1"
                           runat="server"></asp:ScriptManager>

        <div  style="height:100%;">

            <asp:Silverlight ID="Xaml1"
                 runat="server"
                 Source="~/ClientBin/UnityContrib.Demo.Silverlight.xap"
                
 MinimumVersion="2.0.31005.0" Width="100%" Height="100%" />

        </div>

    </form>

</body>

</html>

The Application_Startup is passed in this value during application startup and assigned to the Bootstrappers.InitParams property:

    public partial class App : Application

    {

        private void Application_Startup(object sender, StartupEventArgs e)

        {

            //this.RootVisual = new Page();

            // Replace default page instantiation with bootstrapper

            Bootstrapper bootstrapper = new Bootstrapper();

            bootstrapper.InitParams = e.InitParams;

            bootstrapper.Run();

        }

When the bootStrapper.Run() command is executed, the BootStrapper's CreateShell() command is executed - it is overridden as shown so that we can load our shell (note that IConfigInfo is registered to use the ConfigInfo class - it processes the InitParameters for us):

protected override DependencyObject CreateShell()

{

    // Singletons

    Container

        .RegisterType<IAppBase, AppBase>

            (new ContainerControlledLifetimeManager())

        .RegisterType<IConfigInfo, ConfigInfo>

            (new ContainerControlledLifetimeManager()) ;

 

    Container.RegisterInstance<IDictionary<string,string>>

        ("InitParameters", InitParams);

 

    Shell shell = this.Container.Resolve<Shell>();

 

#if SILVERLIGHT

    Application.Current.RootVisual = shell;

#else

    shell.Show();

#endif

    return shell;

}

Once an instance of the parameters is registered with the Unity Container it will be available anywhere in the application via Dependency Injection.  We'll show all of the code that was required in the module to generate the following output:

PresentationModel:  (two properties)

public class UploadPresentationModel : ModelBase, IUploadPresentationModel

{

    private string _serverURL;

    public string ServerURL

    {

        get { return _serverURL; }

        set

        {

            _serverURL = value;

            OnPropertyChanged("ServerURL");

        }

    }

 

    private string _serverPath;

    public string ServerPath

    {

        get { return _serverPath; }

        set

        {

            _serverPath = value;

            OnPropertyChanged("ServerPath");

        }

    }

}

VIEW (binds to the ServerURL and ServerPath on it's model):

<UserControl x:Class="Module.Upload.Views.Main.MainView"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Width="400" Height="300">

 

    <Grid x:Name="LayoutRoot">

      <Grid.Background>

        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">

          <GradientStop Color="#FFF8F5F5"/>

          <GradientStop Color="#FF1C6960" Offset="1"/>

        </LinearGradientBrush>

      </Grid.Background>

        <Grid.RowDefinitions>

            <RowDefinition Height="25"/>

            <RowDefinition/>

            <RowDefinition Height="25"/>

        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>

            <ColumnDefinition />

        </Grid.ColumnDefinitions>

 

        <TextBlock Text="{Binding ServerURL}" Margin="5"/>

        <TextBlock Text="{Binding ServerPath}" Grid.Row="1"/>

    </Grid>

</UserControl>

PRESENTER (uses dependency injection, i.e., IConfigInfo, to obtain configuration info):

public class MainViewPresenter : PresenterBase<IMainView>

{

    IAppBase app;

    IUploadPresentationModel uploadModel;

 

    public MainViewPresenter(

                IConfigInfo configInfo,

                IMainView view,

                IModelBase model,

                IAppBase app)

        // Assign the view and model instances (created by

        // dependency injection) and update base View and Model

        : base(view,model)

    {

        try

        {

            this.app = app;

 

            // Cast the model to this presenter's Model so we

            // can update the applicable properties

            uploadModel = (IUploadPresentationModel) model;

 

            // Update model to reflect current information

            uploadModel.ServerURL = configInfo.WebServerURL;

            uploadModel.ServerPath = configInfo.WebServerPath;

 

        }

        catch (Exception ex)

        {

            throw ex;

        }

    }

 

}

 

Finally - the ConfigInfo class that processes the InitParameters string in its constructor by obtaining an instance of Unity Container via dependency injection (constructor injection).  I will use this so that it can obtain the InitParameters value:

public class ConfigInfo : AppBase, IConfigInfo

{

    private IDictionary<string, string> constructorParam;

    public IDictionary<string, string> InitParam { get; set; }

 

    public ConfigInfo(IUnityContainer container)

    {

        try

        {

            // Instantiate our public Parameter collection

            InitParam = new Dictionary<string, string>();

 

            // Retrieve parameters (saved in Bootstrapper) and place

            // in temporary constructorParam variable

            constructorParam = container

                .Resolve<IDictionary<string, string>>("InitParameters");

 

            // Retrieve from constructorParam the InitParameters value

            // it will be a KeyNameValuePair format.  Split and assign

            // to InitParam public property

            string paraString = constructorParam["InitParameters"];

            string[] paraList = paraString.Split(';');

            foreach (string para in paraList)

            {

                string[] keyValue = para.Split('=');

                InitParam.Add(new

                    KeyValuePair<string, string>(keyValue[0], keyValue[1]));

            }

        }

        catch (Exception ex)

        {

            Error = ex;

        }

    }

 

    /// <summary>

    /// Centralize the retrieval of parameters for error trapping

    /// </summary>

    /// <param name="key"></param>

    /// <returns></returns>

    private string GetParameter(string key)

    {

        try

        {

            return InitParam[key];

        }

        catch (Exception ex)

        {

            Error = ex;

            return string.Empty;

        }

    }

 

    /// <summary>

    /// Web Server URL

    /// </summary>

    public string WebServerURL

    {

        get { return GetParameter("ServerURL"); }

    }

 

    /// <summary>

    /// Web Server Path

    /// </summary>

    public string WebServerPath

    {

        get { return GetParameter("ServerPath"); }

    }

}

Silverlight / CompositeWPF / Prism regions

by 18. December 2008 14:54

Suggested reading: Martin Fowler on the PresentationModel 

Source Code available  http://www.CodePlex.com/SDMS  

CompositeWPF/Prism regions permit you to easily design a system that clearly has a Separation of Concerns.  In the figure below you see the XAML code that defines the regions for the Main Employee Manager screen:

This is particularly useful for tabs because you don't have to kludge up your TabControl with alot of controls - above you see all the XAML code required for the Information, In Work and Assigned tabs.

Below you'll see where the Module's Initialize method will register the types, resolve each presenter (instantiate it) and then apply the presenter's view to the applicable regions:


Click HERE for Video Clip Demo (Windows 7 users may have to right click then Save Target As)


The code for the Status Bar's Presenter, View and Interface is below.  Note: all of the Employee Module views share a single presentation model.  

Below is the source code for the EmployeeModule\Views\frmStatus\frmStatusPresenter.cs

using EmployeeModule.PresentationModels;

using Microsoft.Practices.Unity;

using Microsoft.Practices.Composite.Events;

using EmployeeModule.Events;

using System.Windows.Controls;

using System;

 

namespace EmployeeModule.Views.frmStatus

{

    public class frmStatusPresenter

    {

        [Dependency]

        public IEventAggregator aggregator { get; set; }

 

        /// <summary>

        /// View reference

        /// </summary>

        public IfrmStatusView View { get; set; }

 

        /// <summary>

        /// Constructor : setup class

        /// </summary>

        /// <param name="container"></param>

        /// <param name="view"></param>

        public frmStatusPresenter(IUnityContainer container,

            IfrmStatusView view, IEventAggregator aggregator)

        {

            this.View = view;

            this.aggregator = aggregator;

 

            // Provide the presentation model to the view so that

            // it can set it's DataContext to it

            view.Model =
                 container.Resolve<EmployeePresentationModel>();

 

            // Subscribe to the StatusBar event

            aggregator.GetEvent<StatusBarEvent>()

                 .Subscribe(StatusBarEventHandler, true);

 

        }

 

        public void StatusBarEventHandler(StatusBarData panel)

        {

            switch (panel.Panel)

            {

                case StatusPanel.Left:

                    View.FindControl<TextBlock>("stsLeft").Text =
                             panel.Message;

                    View.FindControl<TextBlock>("stsRight").Text =
                             string.Format("ID:{2} -- {0} {1}",

                        View.Model.SelectedEmployee.FirstName,

                        View.Model.SelectedEmployee.LastName,

                        View.Model.SelectedEmployee.EmployeeID);

                    break;

 

                case StatusPanel.Right:

                    View.FindControl<TextBlock>("stsRight").Text =
                            panel.Message;

                    break;

 

            }

        }

    }

}

 

AG_E_PARSER_BAD_TYPE Adding Silverlight Toolkit control to module

by 10. December 2008 03:22

All was going smoothly creating the infrastructure for our next solution using the CompositeWPF/Prism package until I added a Silverlight Toolkit Treeview control (all of the standard controls worked just fine).  After adding the Treeview control to the ReportView.xaml file I got the following runtime error:

It was the following link that gave light to the problem (FYI, the designer was happy):
http://silverlight.net/forums/p/12448/48559.aspx#48559 

Which suggested there are issues with inherited controls - troubleshooting this concept I added the Treeview control to my Shell.xaml (ZLCAdmin.Silverlight project) and surprisingly my problem went away; I removed my control from the shell and the solution continued to work!

In the case of the CompositeWPF/Prism infrastructure the fix was easy - simply add a reference to the Microsoft.Windows.Controls.dll in the Main project; in the case above the ZLCAdmin.Silverlight project.   

 

MEF - CompositeWPF under one roof

by 29. September 2008 10:36

MEF and the CompositeWPF both offer distinct and very valuable services.  MEF allows us to easily create Add-in components and the CompositeWPF makes it easy to decouple our services and views.   Today I started integrating the two (reference MEF forum message).

Note: I am currently working on the Proof-Of-Concept (POC), when I have it completed it I will start TDD and refactor it as applicable.  See FLASH DEMO HERE   FULL SOURCE HERE (7meg) see notes under image.

2010.02.06 - Updated blog entry - was notified by reader that the "Full Source Here" link was invalid.  Updated to point to the last update I made while I was running the MEFContrib project (had to turn it over since contract requirements was no longer permitting me to maintain it). 

--------------------------------------------------------------------------------------------------

My objective will be to make it as simple as possible to implement the integration without making any changes to the CompositeWPF or MEF libraries - all of the wiring up will be done in the MEFContrib.Library project.

Below you'll see the only code required in the App.xaml.cs code behind file:

Notice that I have the MEF, CompositeWPF and Unity source code under SRC - these are the complete source code packages.  When the smoke settles I'll only upload DLLs, for now it will be helpful to step into code when it comes time to refactor.

Above the Parts folder contains MEF parts, they are compiled to the bin\Extensions folder.  The bootstrapper references our MEFContrib.Library.

The bootstrapper code that pertains to the CompositeWPF follows below.  The LoadReferencedModules  class below tells the CompositeWPF to load all modules referenced by the BasicWPFApp - in our case it will be the RSSFeed module that I copied/pasted from a CompositeWPF demo I wrote earlier.

namespace MEFContrib.Library.Base
{
    public class MEFBootStrapper : UnityBootstrapper
    {
        private Assembly _assembly;

        private Application _application;
        private MEFCompositionContainer _container;
 
/// <summary>
/// CompositeWPF
/// </summary>
/// <returns></returns>
protected override DependencyObject CreateShell()
{
    _application.MainWindow.Show();
    return _application.MainWindow;
}
 
/// <summary>
/// CompositeWPF
/// </summary>
/// <returns></returns>
protected override IModuleEnumerator GetModuleEnumerator()
{
    Container.RegisterType<ILogger, TraceToolLogger>();
 
    return LoadReferencedModules.GetModules(
        _application.MainWindow.GetType());
}
 
 
/// <summary>
/// MEF
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public MEFBootStrapper(object sender, BootStrapperEventArgs e)
{
    _application = sender as Application;
    _assembly = e.Assembly;
 
    _application.Exit += new ExitEventHandler(_application_Exit);
 
    if (Compose())
        Run();
    else
        _application.Shutdown();
}

The Window1.xaml.cs code-behind file contains an earlier POC so I left it as is.  Note there are no references to the MessageBoxLogger nor DebugLogger in our BasicWPFApp.  MEF will load whatever it finds in the bin\extensions folder which happens to contain these two logging features.

The Windows XAML below has two region names "MainRegion" and "MainView". 

<Window x:Class="BasicWPFApp.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cal="http://www.codeplex.com/CompositeWPF"
    Title="Window1" Height="400" Width="600">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="25"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
 
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="2*" />
        </Grid.ColumnDefinitions>
 
        <StackPanel Orientation="Horizontal" Grid.ColumnSpan="2">
            <TextBox Name="textBox1" Width="120">Hello World</TextBox>
            <Button Name="button1"  Width="57" Click="button1_Click">Debug</Button>
            <Button Name="button2" Width="55" Click="button2_Click">MB</Button>
        </StackPanel>
        <GridSplitter Grid.Row="1" Width="10"/>
        <ItemsControl Grid.Row="1" Name="MainRegion"  
                      cal:RegionManager.RegionName="MainRegion" Margin="0,0,6,0">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <DockPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
 
        <ItemsControl Grid.Row="1" Grid.Column="1" Name="MainView"  
                      cal:RegionManager.RegionName="MainView">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <DockPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
</Window>

When the RSSFeed module loads it will populate the MainRegion with the RSSFeedView, which in turn will populate the MainView RSSViewer.

namespace RSSFeed
{
    [Module(ModuleName = "RSSFeedModule")]
    public class RSSFeedModule : ModuleBase
    {
        /// <summary>
        /// Injection Constructor
        /// </summary>
        /// <param name="regionManager">Registered IRegionManager</param>
        /// <param name="container">Registered IUnityContainer</param>
        /// <param name="presenter">Registered IMainPresenter</param>
        public RSSFeedModule(IRegionManager regionManager,
            IUnityContainer container)
        {
            RegionManager = regionManager;
            Container = container;
        }
 
        protected override void RegisterViewsAndServices()
        {
            base.RegisterViewsAndServices();
 
            // Add view to region
            IRegion mainRegion = RegionManager.Regions["MainRegion"];
 
            // Name it so we can move it later in MainController.cs
            mainRegion.Add(Container.Resolve<RSSFeedView>(), "RssFeed");
        }
 
 
        public override void Initialize()
        {
            base.Initialize();
        }
    }
}

Notice

Blog videos and references to CodePlex projects are no longer valid