MSBUILD Copy Task - Upgrading PRISM to Unity 2.0

by 29. January 2010 22:07

To ensure that the application's infrastructure is not obsolete before it is completed I am using the newest techologies available (VS2010, Azure, PRISM, MEF, Unity, etc).  

The process that follows will ensure all Third-Party libraries are built and available for the new EHR application. 

Unity 2.0 and MEF Beta 2 are not yet released but I'll want to take advantage of their new features.  When they are released I'll have to perform this process again so I'll simplify it now.  Since PRISM is dependent on Unity I'll have to upgrade it so that it is compatible with Unity 2.0.  There are no dependencies on MEF but some changes have to be made because Beta 2 clashes with the version of MEF that resides in .NET 4.0.

Once I get all issues resolved so that the various projects and solutions will build I'll use a MSBuild's Copy task to transfer the .DLLs to a unified Binaries folder (c:\_\source\Binaries\Desktop).  My EHR application will reference this Binaries folder as required. 

I could simply place the ItemGroup and Target element directly into the applicable projects but it will be far easier to create a "CopyToBin.Desktop.Targets" file and reference it with one line of code. 

 

 

[CopyToBin.Desktop.Targets] contents follow: 

http://msdn.microsoft.com/en-us/library/3e54c37h.aspx

 

<Project DefaultTargets="AfterBuild"

    xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <ItemGroup>

      <AllDll Include=".\**\*.dll" />

    <CopyToBin Include="c:\_\source\Binaries\Desktop"/>

  </ItemGroup>

 

  <Target Name="AfterBuild">

    <Message Text="Processing Copy Task"/>

      <MakeDir Directories="@(CopyToBin)"/>

      <Copy

          SourceFiles="@(AllDll)"

          DestinationFolder="@(CopyToBin)"  />

  </Target>

</Project>

 

I'll place the .targets files in the root of my source folder (where all my third-party source code files reside).

To easily edit the project file simply right click on the project and "Unload" it.  Once unloaded you can right click on the unloaded project and select "Edit" (reference right pane in the image below).   Note that I have a snippet in my Toolbox so that I can simply go to the end of the project file and insert the location of my CopyToBin.Desktop.Targets file.   I'll then "Reload" the project

I'll do the above steps for all of the Unity projects and then build the project.   The resulting DLLs will be copied to the Binaries\Desktop folder where I can now transfer them to the applicable PRISM folder.

PRISM stores the Unity files in it's LIB\Desktop\Unity folder.  You'll have to delete the existing files and copy the newly created Unity DLLs from the Binaries folder to the above PRISM folder (later I'll have a batch file do this).

You are now ready to load the PRISM solution and compile it.  There are only a few differences in Unity 1.2 and Unity 2.0 that will break the build.

  1. Two projects will complain that you require a reference to System.Xaml (assuming you are also upgrading PRISM to VS2010).
  2. There are a few Mock objects that do not implement interfaces correctly - simply implement the interfaces as applicable.
  3. There will be two sections where you'll have to remark out code for objects that no longer exists (this is okay - I verified it with the PRISM team)

You should now be able to build PRISM using the newly compiled Unity 2.0 DLLs.

Note: To build MEF under VS2010 you'll need to follow the few steps I noted in the MEF Developers forum (CLICK HERE)

Once I have all of my projects setup with the new CopyToBin.Desktop.target I can run the batch files I created.   The first, BuildBinaries.cmd, will launch the Visual Studio 2010 command window - its contents follow:

@ECHO OFF
echo Type - and hit ENTER
call %comspec% /k ""C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"" x86

This batch file is the one provided by Visual Studio which sets up the environment (otherwise the second batch file won't find MSBuild).   It effectively opens a Visual Studio Command window so that you can easily run the second batch file "-.cmd"  (dash - keep it quick/simple). 

The -.cmd batch file is based on folder structures compatible with the third-party applications.  The batch file will change directories to each of the applicable solutions and then run MSBuild against it.

Note: I renamed the folders since doing the above screenshot to ensure I wouldn't have to modify the batch file later (I'll just simply ensure updates use the names used).

The end results is a I have a Binaries folder that is populated with all of the Third-Party DLLs that I will be using for my application.  When new releases are provided I simply have to ensure they compile and then run my batch file again.

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.  

 

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();
        }

Prism/WCF - single configuration file for ASP.NET, Silverlight and WCF

by 18. February 2009 23:07

For a functional demo, with the following principals applied, see our http://www.CodePlex.com/SDMS project.

In a recent blog I noted issues with Prism/Silverlight, WCF and configuration files.  I have since resolved this issue and respond to a programmers question below.

Hi,

I've read your post about problems with wcf service reference and have a question about your solution.

What if i have different wcf services and have more than one ServiceReference.ClientConfig. It would be impossible to add them all as link to shell project.

See Related WEBCAST

I resolved my issues with configuration files by programmatically setting the Binding and Address – I now have “one” configuration file (Web.Config) that services my ASP.NET, Silverlight and WCF Service(s).    The following is my DoctorServiceDAL (Presentation Tier’s Data Layer); after I do an “Add Web Reference” or “Update Web Reference” I delete the configuration file (not required). 

        public DoctorServiceDAL(IUnityContainer container)
        {
            try
            {
                // Set container reference for class use
                Container = container; 

                IServerConfiguration config = container.Resolve<IServerConfiguration>(); 

                // Use basic HttpBinding on both Silverlight and Desktop
                BasicHttpBinding binding =
                    new BasicHttpBinding(BasicHttpSecurityMode.None); 

               binding.Name = "bndDoctorService"; 

                // Get the WebServiceURI from the unity container
                Uri baseAddress = new Uri(config.WebServiceURI); 

                EndpointAddress address = new EndpointAddress(baseAddress); 

                // Instantiate our service using configured data
                client = new wcf.DoctorServiceClient(binding, address); 

                // Subscribe to the Completed events.  The DSEAxxxxx classes that
                // will actually handle setting up the event argument (which will be
                // raised below) are in the Data project (for service references).
                client.GetProviderListCompleted += AsyncCompleted;
                client.GetConfigurationCompleted += AsyncCompleted; 

                // Note: be sure to update applicable modules, i.e., PatientModule.cs
                //       after updating the above to ensure DSEAxxxx can be resolved!
            }
            catch (Exception ex)
            {
                Error = ex;
            }
        }

        /// <summary>
        /// Requests the doctor list.  When list is available it will
        /// be directed to the AsyncCompleted event (subscribed to in
        /// the Constructor)
        /// </summary>
        /// <param name="doctorID">The doctor ID.</param>
        public void RequestProviderData(int doctorID)
        {
            client.GetProviderListAsync(doctorID);
        }

       
        /// <summary>
        /// Request the system configuration parameter string
        /// </summary>
        public void RequestConfigurationString()
        {
            client.GetConfigurationAsync();
        }

        /// <summary>
        /// Executes when async calls are completed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e"></param>
        /// instance containing the event data.</param>
        void AsyncCompleted(object sender, AsyncCompletedEventArgs e)
        {
            // All Async callbacks will end up here
            // prepare global event argument
            IDoctorServiceEventArgs args = new DoctorServiceEventArgs();

            // Retrieve the name of the process that just completed
            // and strip off the CompletedEventArgs
            string name = e.GetType().Name.Replace("CompletedEventArgs", "");

            try // Use Unity Container as a factory
            {
                // Resolve it - it must be configured in the Module(s) that
                // use this data layer (reference PatientModule.cs)
                args = Container.Resolve<IDoctorServiceEventArgs>(name);

                // Set the properties to the result
                args.SetArgProperties(e);
            }
            catch (Exception ex)
            {
                Error = new Exception(string.Format("{0} was not configured in {1} \r\n{2}{3}",
                    name, GetType().FullName, ex.Message, ex.StackTrace));
               
            }

            // If anyone is subscribing to the event (which they should be)
            // then raise the event with the data returned
            if (OnDoctorServiceEvent != null)
                OnDoctorServiceEvent(this, (DoctorServiceEventArgs) args);

        }
    }
 

My Default.aspx.cs, code-behind file, has the following which provides information I will need.   Note that I pass it into Xaml1 for Silverlight (read BLOG) and to my WCF Service DoctorServiceClient which is injected into the WCF unity container for successive use (related BLOG). 

    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            // Retrieve Connection String
            string connectionString =
                ConfigurationManager.ConnectionStrings["PRMS"].ConnectionString;

            // Retrieve Web Service URIs
            string webServiceURI_Dev =
                ConfigurationManager.AppSettings["WebServiceURI-DEV"];
            string webServiceURI_IIS =
                ConfigurationManager.AppSettings["WebServiceURI-IIS"]; 

            // Retrieve Web Service Path
            string webServicePath =
                Request.ApplicationPath == "/" ? webServiceURI_Dev : webServiceURI_IIS; 

            // Set InitParameters for Silverlight use
            Xaml1.InitParameters = string.Format(
                "InitParameters=" +
                "ServerURL=http://{0};" +
                "ServerPath={1};" +
                "ConnString={2};" +
                "WebServiceURI={3}",
                    Request.Url.Authority,
                    Server.MapPath("~"),
                    connectionString,
                    webServicePath); 

            // Send parameters to WCF Service for its use
            new DoctorServiceClient().SetConfiguration(Xaml1.InitParameters);
        }
    }


After the above code runs I can obtain my configuration information from anywhere in the application (ASP.NET, Silverlight or WCF) by resolving the IServerConfiguration. 

 IServerConfiguration config = container.Resolve<IServerConfiguration>();

Below is the class that implements IServerConfiguration and does the heaviy lifting, note that I provide a mechanism to strongly type the values stored in InitParam:

using Microsoft.Practices.Unity;
using System.Collections;
using System.Collections.Generic;
using System;
namespace Library.Interface.Configuration
{
    public class ServerConfiguration : AppBase, IServerConfiguration
    {
        private IDictionary<string, string> constructorParam;
        public IDictionary<string, string> InitParam { get; set; } 

        public ServerConfiguration(IUnityContainer container) : base(container)
        {
            try
            {
                InitParam = new Dictionary<string, string>();
                constructorParam = container.Resolve<IDictionary<string, string>>("InitParameters");
                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;
            }
        } 

        public string WebServerURL
        {
            get { return InitParam["ServerURL"]; }
        }
        public string WebServerPath
        {
            get { return InitParam["ServerPath"]; }
        }
        public string WebServiceURI
        {
            get { return InitParam["WebServiceURI"]; }
        } 
    }
}

 

 

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