Serialize / Deserialize POCO objects - Silverlight and WPF (multi-targeting code)

by 17. November 2009 21:28

In our SDMS application (source code here) we have a library that is multi-targeting (shared codebase) for ASP.NET MVC2, Silverlight and WPF, as you'll see in the image below the "only" differences will be the App.xaml and Mainxxxx.xaml files.   To pull this off we give the CAG Bootstrapper a little more work to do.

One of the jobs of the GetModuleCatalog() method in the Bootstrapper is to deserialize our parameter object (more on parameter object in my next blog).   We have to do this in GetModuleCatalog because it is called within the UnityBootstrapper's CreateContainer command which occurs prior to CreateShell (see sequence diagram here).  Note that we do this with the object extension DeserializeObject

The following code will permit us to serialize and deserialize our POCO object.   The code resides in this applications UnityContrib project in the Extensions\ObjectExtension class.

 

Creating Desktop Skins in WPF for PRISM Desktop

by 13. August 2009 10:14

Based on Creating a Skinned User Interface in WPF (excellent blog on the topic!), adapted for PRISM and dependency injection.

Creating SKINS in WPF Desktop for PRISM is relatively easy.   The first thing we want to do is create our styles.  Note in my WPF Project (Demo.WPF second arrow) that I use an Assets folder - this is to be consistent with the .NET RIA Services auto generated styles (top arrow). 

My Styles.xaml file will contain styles common to all skins - in this case I have a StylesDefault (Blue) and StylesGreen (Green).

With the Styles (skins) out of the way we can start wiring up PRISM.   We do this by Loading both of the Stylesxxxxx.xaml files and registering them in our Container - the type will be ResourceDictionary with the names set to "Blue" and Default".  The code for the Windows.cs (Shell) follows:

   22 /// <summary>
   23 /// Interaction logic for Window1.xaml
   24 /// </summary>
   25 public partial class Window1 : Window
   26 {
   27     /// <summary>
   28     /// Load resources delegate
   29     /// </summary>
   30     public delegate void LoadResourcesDelegate();
   31 
   32     [Dependency]
   33     public IUnityContainer Container { get; set; }
   34 
   35     [Dependency]
   36     public ILoggerFacade Logger { get; set; }
   37 
   38     public Window1()
   39     {
   40         InitializeComponent();
   41 
   42         this.Loaded += new RoutedEventHandler(Window1_Loaded);
   43     }
   44 
   45     void Window1_Loaded(object sender, RoutedEventArgs e)
   46     {
   47         // Load asynchronously
   48         Dispatcher.BeginInvoke(new LoadResourcesDelegate(LoadResources));
   49     }
   50 
   51     private void LoadResources()
   52     {
   53         // Load secondary styles when the XAML Build Action
   54         // is set to "Content"
   55         Container.RegisterInstance<ResourceDictionary>("Blue",
   56             (ResourceDictionary)Application.LoadComponent(
   57             new Uri("Assets/StylesGreen.xaml", UriKind.Relative)));
   58 
   59         // Load default styles (so we can restore).  The XAML
   60         // Build Action is "Page" - required because the App.XAML
   61         // shows this as the MergeDictionary
   62         Container.RegisterInstance<ResourceDictionary>("Default", 
   63             (ResourceDictionary) Application.LoadComponent(
   64             new Uri("Assets/StylesDefault.xaml", UriKind.Relative)));
   65     }
   66 }

Note how we are loading the skins on their own thread (line 48).  We don't have an immediate need for the skins since our App.xaml code is loading the StylesDefault.xaml resources for it's own use (line 8 below); we don't have to affect performance by loading skins on the UI thread.

    1 <Application x:Class="Demo.WPF.App"
    2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    4 
    5     <Application.Resources>
    6         <ResourceDictionary>
    7             <ResourceDictionary.MergedDictionaries>
    8                 <ResourceDictionary Source="Assets/StylesDefault.xaml"/>
    9             </ResourceDictionary.MergedDictionaries>
   10         </ResourceDictionary>
   11     </Application.Resources>
   12 
   13 </Application>

At this point we have successfully loaded our skins into our Unity Container and are ready to wire-up a view to utilize them.

We'll now go to our Modules folder and select the ModuleClient.WPF (WPF for Desktop) and wire-up our MenuRegion's view (MainMenu) to process the available skins.  Note below we create a context menu with two menu options "Blue" (default) and "Green".

Our code behind will handle the ContextMenu_Click by using the EventAggregator to raise a MenuItem click event (ClickEventArgs handles MenuItem, Button and CheckBox clicks).   We're done on the UI side - now we'll turn our attention to the Presenter which will handle the click event.

The PrismContrib.xxxx assemblies abstract much of the framework from us (KISS).   Within these libraries resides the PresenterBase<T> which wires everything up on lines 27-32.   Once wired-up we have virtual methods that we can override to simplify our lives, e.g., below we override the OnButtonClickEventHandler to handle our MenuItem click.

The magic happens on lines 45 and 50 as applicable.  They utilize a Extensions class which extends the IUnityContainer interface.  Note how we can use the same code (line 29 below) for both Silverlight and WPF Desktop by using the "using" statements on lines 5 and 7 (below).

Line 26 will resolve our ResourceDictionary by the name effectively giving us a reference to the applicable skin dictionary.

On line 29 we get a reference to the current resource and clear it on line 33.   All that remains is to add our updated MergedDictionary and walla!  We have successfully applied a new skin.

    1 using System.Windows;
    2 using Microsoft.Practices.Unity;
    3 
    4 #if SILVERLIGHT
    5 using wpfCollection = System.Windows.PresentationFrameworkCollection<System.Windows.ResourceDictionary>;
    6 #else
    7 using wpfCollection = System.Collections.ObjectModel.Collection<System.Windows.ResourceDictionary>;
    8 #endif
    9 
   10 
   11 using System.Collections.ObjectModel;namespace Infrastructure.Resource
   12 {
   13     /// <summary>
   14     /// Resource Utility
   15     /// </summary>
   16     public static class ResourceUtil
   17     {
   18         /// <summary>
   19         /// Sets the skin.
   20         /// </summary>
   21         /// <param name="rd">The rd.</param>
   22         /// <param name="skinName">Name of the skin.</param>
   23         public static void SetSkin(this IUnityContainer container, string skinName)
   24         {
   25 #if !WinForm
   26             ResourceDictionary skin =
   27                 container.Resolve<ResourceDictionary>(skinName);
   28 
   29             wpfCollection mergedDicts =
   30                 Application.Current.Resources.MergedDictionaries;
   31 
   32             if (mergedDicts.Count > 0)
   33                 mergedDicts.Clear();
   34 
   35             // Apply the selected skin so that all elements in the
   36             // application will honor the new look and feel.
   37             mergedDicts.Add(skin);
   38 #endif
   39 
   40         }
   41     }
   42 }

Right clicking on the Menu bar (top sky-blue region) reveals our context menu. 

Tags: , ,

WPF

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.  

 

Where is Microsoft.Expression.Interactions?

by 21. July 2009 10:09

That's the question I was asking after installing Microsoft Expression Blend 3.   It appears that two DLL references are missing - or at least misplaced.

They are located at C:\Program Files\Microsoft SDKs\Expression\Blend 3\Interactivity\Libraries. 

In my case (64bit) it was C:\Program Files(x86)\... as shown in the image that follows.

Add the references and all will be happy. 

Below you can see that after adding references our view will display.   Note Expression Blend 3 offers 140 controls! 

WPF - setting background color of control programmatically

by 26. September 2008 01:48

Searching for a way to set the background color of a WPF control programmatically seemed like more of an adventure then it should have been; there are sure a lot of complicated ways to set a background color.   Reluctant to believe that it was this complicated I kept searching until I found it.

txtStatus.Foreground = new SolidColorBrush(Colors.White);
txtStatus.Background = new SolidColorBrush(Colors.Red);

Tags: ,

WPF

CompositeWPF - Getting your view to stretch vertically as well as horizontally

by 9. August 2008 00:25

My primary view (loaded in the Shell's MainRegion) looked as follows; I want my menu bar at the top, a status bar at the bottom and the ProcessSolutionRegion contents to fill in the middle:

The result follows - not exactly what I was expection or needing...  I need my status bar to be at the bottom.

If I were to wrap my Grid with a DockPanel and specify a height I get what I'm looking for - at least until I resize my Window....

FYI, wrapping the ItemsControl in the Shell doesn't work:

<DockPanel Height="265">
<ItemsControl Name="MainRegion"
            cal:RegionManager.RegionName="{x:Static local:RegionNames.MainRegion}"/>
</DockPanel>

I have to add a Template to my ItemsControl element to get my desired results:

<Window x:Class="ShellApp.Shell"
    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"
    xmlns:local="clr-namespace:ShellApp"
    Name="myShell" Title="GWN App" Height="300" Width="300">
   
    <ItemsControl Name="MainRegion"
            cal:RegionManager.RegionName="{x:Static local:RegionNames.MainRegion}">
       
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <DockPanel/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
       
    </ItemsControl>
</Window>

 

CompositeWPF - Using a single interface for multiple services

by 30. July 2008 13:05

In the following message Multiple views of the same data CodeHulk clarifies some requirements that will allow us to exercise the power of Unity to decouple services.

The XAML code for the HelloWorldView.xaml file:

<UserControl x:Class="HelloWorld.Views.HelloWorldView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:HelloWorld"
    xmlns:cal="http://www.codeplex.com/CompositeWPF">
   
    <StackPanel>
        <TextBlock x:Name="lblResults" HorizontalAlignment="Center">HelloWorld</TextBlock>
        <Button Click="btnClick" x:Name="btnOne">Load HelloWorld1</Button>
        <Button Click="btnClick" x:Name="btnTwo">Load HelloWorld2</Button>
        <ContentControl
                     cal:RegionManager.RegionName="{x:Static local:RegionNames.HelloWorld}"/>
    </StackPanel>
</UserControl>

It produces the following output:

Both buttons subscribe to the same btnClick() method in the above XAML - this is where all the magic happens.

    public partial class HelloWorldView : UserControl
    {
        private IUnityContainer _container;
        public HelloWorldView(IUnityContainer container)
        {
            InitializeComponent();
            _container = container;
        }

        private void btnClick(object sender, System.Windows.RoutedEventArgs e)
        {
            Button button = sender as Button;
            IHelloWorldService service = _container.Resolve<IHelloWorldService>(button.Name);
            lblResults.Text = service.GetMessage();
        }
    }

The button.Name will contain the string "btnOne" or "btnTwo", depending on the button that was clicked.  Since a name is provided the container will search the registered IHelloWorldService services and resolve to the applicable class.   The classes are defined in the modules RegisterViewsAndServices() method below:

    public class HelloWorldModule : IModule
    {
        private readonly IRegionManager regionManager;
        private readonly IUnityContainer container;

        public HelloWorldModule(IRegionManager regionManager, IUnityContainer container)
        {
            this.regionManager = regionManager;
            this.container = container;
        }

        public void Initialize()
        {
            IRegion mainRegion = this.regionManager.Regions[RegionNames.MainRegion];

            RegisterViewsAndServices();
           
            mainRegion.Add( container.Resolve<HelloWorldView>());
        }
        protected void RegisterViewsAndServices()
        {
            container
                .RegisterType<IHelloWorldService, HelloWorldService1>("btnOne")
                .RegisterType<IHelloWorldService, HelloWorldService2>("btnTwo");
           
        }
    }

The actual interface,  services and button click results follow:

    public interface IHelloWorldService
    {
        string GetMessage();
    }

    public class HelloWorldService1 : IHelloWorldService
    {
        public string GetMessage()
        {
            return "********> Hello World #1 <********";
        }
    }

    public class HelloWorldService2 : IHelloWorldService
    {
        public string GetMessage()
        {
            return "======> Hello World #2 <======";
        }
    }

Source Code: HelloWorld.zip (546.51 kb)

XBAP - Deploying to CompositeWPF to IIS Server

by 30. July 2008 11:18

In the following CodePlex message Can I develop XBAP application with this framework?   sjappie alerts me to the fact that the CompositeWPF XBAP application will not deploy and run in IIS - so far I've only used it in Visual Studio 2008 Debug mode; I confirmed that what he was saying was true.

I found this blog How to run WPF - XBAP as Full Trust Application to be very thorough on explaining how to configure an XBAP application so that it can run in a full-trust environment.  After complying with the steps the CompositeWPF Command demo will now run in IIS.


Click Image to view Flash Demo

Related post by the same author:
How to run WPF-XBAP Application in Full-Trust Mode (Post #2: certificate extraction)

CompositeWPF - Sharing service among different modules

by 29. July 2008 14:08

In CodeHulk's message thread Multiple Views of the Same Data mconverti responded with the following answer:

"I recommend you to avoid having static references between modules. You can instead split in a separated project the common interfaces that your modules share. Then you can use the container to get the specific implementation."

CodeHulk's response to this was that "I don't know how to resolve this without using the concrete classes".   Since I was preparing to open up the EventAggregation solution to learn how it handles event processing, and it happened to have two modules, this served as a good environment to address CodeHulks question using mconverti's suggestion.

Module A is the view on the left and Module B is the view on the right.   I have a Listbox in Module A and a ComboBox in Module B being serviced by the same interface and service.

Since Module A is instantiated before Module B we'll register dependencies in it.  Once registered we can simply resolve it in any other module that requires it; in this example we're using a [Dependency] atttribute in the a Module B presenter.

Module B code file updates follow - updates are relative to MyComboBox: 

IActivityView.cs

    public interface IActivityView
    {
        ComboBox MyComboBox { get; set; }
        void AddContent(string content);
        string Title { set; }
        string CustomerId { set; }
    }


ActivityView.xaml.cs

    Public ComboBox MyComboBox
    {
        get { return MyComboBoxControl; }
        set { MyComboBoxControl = value; }
    }

ActivityView.xaml

    <StackPanel Name="ContentPanel">
            <Label Name="ActivityLabel" FontWeight="Bold" Padding="0" Margin="0"/>
            <ComboBox
                HorizontalAlignment="Left"
                x:Name="MyComboBoxControl"
                ItemsSource="{Binding}" DisplayMemberPath="Name"
                Width="100"
                />
    </StackPanel>


ActivityPresenter.cs

        // Replaced View with the following code
        // public IActivityView View { get; set; }

        private IActivityView _view;
        public IActivityView View
        {
            get { return _view; }
            set {
                _view = value;
                OnViewSet();
            }
        }

        private ICustomerService _customerService;
        [Dependency]
        public ICustomerService CustomerService
        {
            set { _customerService = value; }
        }

        public void OnViewSet()
        {
            _view.MyComboBox.DataContext =
                _customerService.GetCustomerList();
        }

Now let's look at how Module A was configured to make the above possible:

First we'll register our ICustomerService interface, it will resolve to the LocalCustomerService class - the only changes to existing code were the addition of the AddServices() method: 

    public class ModuleA : IModule
    {
        public ModuleA(IUnityContainer container, IRegionManager regionManager)
        {
            Container = container;
            RegionManager = regionManager;

            AddServices(container);
        }
        private void AddServices(IUnityContainer container)
        {
            container.RegisterType<ICustomerService, LocalCustomerService>();
        }

With that done you'll find that Module A's code closely resembles the code updates made to Module B


IAddFundView.cs

    public interface IAddFundView
    {
        ListBox MyListBox { get; set; }

        event EventHandler AddFund;
        string Customer { get;}
        string Fund { get;}
    }


AddFundView.xaml.cs

    public ListBox MyListBox
    {
        get { return MyListBoxControl; }
        set { MyListBoxControl = value; }
    }


AddFundView.xaml

        <Button Name="AddButton" Margin="5" Width="75" Height="25"  Style="{DynamicResource SimpleButton}" HorizontalAlignment="Left">Add</Button>
        <ListBox
            x:Name="MyListBoxControl"
            ItemsSource="{Binding}" DisplayMemberPath="Name"
            Width="100" Height="100"></ListBox>
    </StackPanel>


AddFundPresenter.cs

        public IAddFundView View
        {
            get { return _view; }
            set
            {
                _view = value;
                _view.AddFund += AddFund;
                OnViewSet();
            }
        }

        private ICustomerService _customerService;
        [Dependency]
        public ICustomerService CustomerService
        {
            set { _customerService = value; }
        }

        public void OnViewSet()
        {
            _view.MyListBox.DataContext =
                _customerService.GetCustomerList();
        }

 

Since the EventAggregation.Infrastructure project was being referenced by both ModuleA and ModuleB I placed the following classes in the projects new Services folder:


ICustomerService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EventAggregation.Infrastructure.Services
{
    public interface ICustomerService
    {
        List<CustomerInfo> GetCustomerList();
    }
}

----

CustomerInfo.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EventAggregation.Infrastructure.Services
{
    public class CustomerInfo
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Phone { get; set; }

        public CustomerInfo(int id, string name, string phone)
        {
            ID = id;
            Name = name;
            Phone = phone;
        }
    }
}

----

LocalCustomerService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EventAggregation.Infrastructure.Services
{
    public class LocalCustomerService : ICustomerService
    {

        public List<CustomerInfo> GetCustomerList()
        {
            List<CustomerInfo> returnList = new List<CustomerInfo>();
            returnList.Add(new CustomerInfo(0, "Bill", "555-1212"));
            returnList.Add(new CustomerInfo(1, "Diane", "555-1200"));
            return returnList;
        }

    }
}

 

 

Note: If you are going to add this code to your EventAggregation solution be sure to update your ModuleA and ModuleB .Tests projects to include the following references:

PresentationCore
PresentationFramework
WindowsBase

You can then implement the interfaces as applicable

Side note: In this blog entry I was focused on the statement "I don't know how to resolve this without using the concrete classes".  From reading the documentation, more specifically the Technical Concepts | Region section, it suggest that a View can be reused - the following is an exceprt:

To add a view to a region, get the region from the region manager, and call the Add method, as shown in the following code.

IRegion region = _regionManager.Regions["MainRegion"];

var ordersPresentationModel = _container.Resolve<IOrdersPresentationModel>();
var _ordersView = ordersPresentationModel.View;
region.Add(_ordersView, "OrdersView");
region.Activate
 

 

WPF and XBAP

by 20. July 2008 07:41

With the overwhelming number of things to learn, and resources you have to sift through (to determine their value), I would have to say that the following Channel 9 webcast is a gem if you are starting to work with WPF.   XBAP for the most part is underdocumented - this reveals much of it and it should raise your interest as well as your understanding.

http://channel9.msdn.com/posts/Charles/WPF-XBAP/

Imagine being able to share user controls between your Windows and Browser applications - the only differing component is the view and it's code behind (with the Presenter, controllers and model being shared by both platforms)....

While assisting jjalexrayer with his Load module in shell in WPF Browser Application issue I discovered that it is possible and practical to share controls between platforms with WPF.   Using the CompositeWPF Commanding solution I was able to reuse all of the business rules and logic in a browser application with minimal coding.  


I used the Commanding.sln and added a new WpfBrowserApp project (source code attached below).  The CommandingBootStrapper.cs file follows:

using System.ComponentModel;
using System.Windows;
using Commanding.Modules.Order;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.UnityExtensions;
using WpfBrowserApp;

namespace Commanding
{
    class CommandingBootstrapper : UnityBootstrapper
    {
        Page1 shell = null;
        public CommandingBootstrapper(Page1 page)
        {
            shell = page;
        }

        protected override DependencyObject CreateShell()
        {
            Container.BuildUp<Page1>(shell);
            return shell;
        }

        protected override IModuleEnumerator GetModuleEnumerator()
        {
            return new StaticModuleEnumerator().AddModule(typeof(OrderModule));
        }
    }
}


Page1 Code behind looks as follows (didn't need the Container - just wanted to see if it would be there - it was :)


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Practices.Unity;
using Commanding;

namespace WpfBrowserApp
{
    /// <summary>
    /// Interaction logic for Page1.xaml
    /// </summary>
    public partial class Page1 : Page
    {
        private IUnityContainer _container;
        [Dependency]
        public IUnityContainer Container
        {
            set { _container = value; }
        }

        public Page1()
        {
            InitializeComponent();

            CommandingBootstrapper bootstrapper =
                new CommandingBootstrapper(this);
            bootstrapper.Run();

        }
    }
}


I grabbed the XAML from Command projects Shell (minus the Window.Background elements) so that the CommandingBootStrapper wouldn't puke - it needs the GlobalCommandsRegion and MainRegion.   I was pretty surprised to find the Page1 XBAP code ran exactly as the Shell WPF! 

<Page x:Class="WpfBrowserApp.Page1"
    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="Page1">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="1"  Margin="10,0,10,0">
            <ItemsControl cal:RegionManager.RegionName="GlobalCommandsRegion" />
        </StackPanel>
        <Border Grid.Row="2" CornerRadius="4,4,4,4" BorderBrush="#193441" Background="#FCFFF5" BorderThickness="2,2,2,2" Margin="10,0,10,10" Padding="5">
            <StackPanel>
                <ItemsControl cal:RegionManager.RegionName="MainRegion" />
            </StackPanel>
        </Border>
        <Label HorizontalAlignment="Left" Margin="155,5,0,0" Width="Auto" Content="QuickStart" FontWeight="Normal" Foreground="#FF373737" FontSize="24" FontFamily="Corbel"/>
        <Label HorizontalAlignment="Left" Margin="10,5,0,0" Width="Auto" Content="Commanding" FontWeight="Bold" Foreground="#FF373737" FontSize="24" FontFamily="Corbel"/>
        <Separator Margin="0,0,0,0" VerticalAlignment="Bottom" Height="10" BorderBrush="#193441"/>
    </Grid>
</Page>

Walla!  I was reusing the WPF application's code in a browser application. 

Source code for WpfBrowserApp (above code) WpfBrowserApp.zip (313.91 kb)

Related Codeplex topic Can I develop XBAP application with this framework?

 

Notice

Blog videos and references to CodePlex projects are no longer valid