Click here to Skip to main content
Click here to Skip to main content

Introduction to Composite WPF (CAL, Prism): Part 1

By , 12 Jun 2009
 

CALDemoSS.jpg

Introduction

If you're a WPF application developer, you've probably heard about CAL by now. It seems to have a number of names that people know it by, CompositeWPF, Composite Application Library, Composite Application Guidance (CAG), and Prism. Frankly, it's all a bit mad; from here on in, it's called CAL. Unlike the growing issue of loads of different techs that essentially do the same thing, here we have a single tech with loads of different names!

Anyway, I have now been involved in two large Composite WPF applications. One for my day-job employer, a smart desktop client application using WPF/ WCF / CAL, and one for my own project. However, from my own experience and from talking to other developers, I think Microsoft sometimes do themselves a real disservice. CAL is awesome, and the documentation is very good and getting better, but the StockTraderRI is just a bit too much to take in at first. There are some excellent things people are doing with CAL (Daniel Vaughan's Calcium, for one), but they aren't really a strip it down to basics take on things; they are complex frameworks in themselves, so I thought I'd write this really cut down demo app.

I'll dispense with the long waffly explanation of what it is, suffice to say that primarily, it is a library incorporating the following feature set:

  • Modularity
  • Dependency Injection Container (IoC)
  • ObjectBuilder2
  • Event Aggregation
  • Runtime Discovery/Static/Explicit Module Enumeration

Before CAL arrived, I had already made a start on the two applications I referred to earlier. We were using a more traditional layered approach to their construction, utilising WPF commands, .NET events, dependency properties, data binding, data templates etc ... all the normal bits and bobs that WPF and .NET provide in order to get a functional application built. However, things can get very complex very quickly in a large commercial application, and this is where CAL can really help simplify the whole programming model.

At the time, I was working on the project for my employer, along with a couple of contractors, and I cannot for the life of me remember which one of us that 'discovered' CAL and brought it to everyone's attention. We had a cursory look at it, it sparked a few communal light bulbs, and we decided that it needed further investigation. So, we then moved onto reading the Composite Application Guidance documentation and looking through the StockTrader reference implementation and the quick-starts that it comes bundled with. Oh, how we clapped with joy and bounced around the office in an almost euphoric state baring inane grins ...

I guess this paints a favourable picture all in all. CAL isn't for every scenario, so you need to do some homework to work out if it really is going to benefit you in the long run. To really nail it, there is a lot of thinking you should do up front. Some of this thinking is the reason I've put this article together. So, despite what I said above ... here stoppeth the waffeleth! (hopefully)

What I'm going to cover:

  • Basics of creating the shell
  • Dependency Injection / Inversion of Control
  • Event Aggregation
  • MVP
  • Some basics of implementing modules
  • Navigation
  • Compiling your app together in the Dev Environment

In Part 2, I will expand the application into some thing a little more interesting, and I will also cover off how to go about dynamically skinning your application in a CAL fashion.

As you'll notice from the demo code, this is both in multiple solutions and one unified solution to make it easier to get into. The demo application is made up of:

  • Shell
  • ModuleA
  • ModuleB
  • PageManager
  • Navigator
  • StatusBar

VisualStudioSS.jpg

I've done this to help illustrate how a collection of developers can co-ordinate their efforts in order to contribute to the final application. This is something that CAL helps achieve massively; in the CAG, they even talk of having off-shore teams working on modules etc. This needs a lot of co-ordination to cohesively achieve, however. There needs to be a very strong level of agreement in terms of how things are to be structured, and overall guidance is required to make sure that different teams approach the application development with a shared vision. It is very, very easy for different developers/teams to approach things differently and basically compromise the benefits that CAL offers.

Dependency Injection / Inversion of Control

This is like a lot of the things you find yourself doing in life. You do it without thinking, then all of a sudden someone comes along and refers to it as "blah-de-blah", and you go ... "Whoa ... is it???" Well, Dependency Injection is one of those things. It sounds all big and grown up and 'advanced', but it really is little more than having a class without a parameterless constructor – you inject a thing this class depends on to function. OK, that is a stupidly simple explanation, and doesn't even take into account the Unity Container, but it is the crux of the issue really. I've even made a chart!!! This is a very high level overview of just what the Unity Container is doing. In terms of using a basic MVP pattern, when a module is discovered, its Initialize() method is called; this in turn calls an optional RegisterViewsAndServices() method which registers a module's views and services inside the DI container (Unity). Then, when an object is resolved from the container, it will either make an instance of that object and inject the dependencies it finds on the constructor, or it will return a singleton-type instance if the object being resolved has been registered in the container with a ContainerControlledLiftTimeManager.

IUnityContainerInjection.png

Event Aggregation, Commanding, Shared Services - Communication

This is simply awesome in my opinion. You can read more about this in the CAG and on Martin Fowler's website. It is basically a way of providing a loosely coupled (damn, I was trying to write this doc without using that phrase!!, lost that bet!) way to provide communication between entities that are not directly linked; this is also known as indirection. The event aggregator manages a list of events and subscribers, and manages the forwarding of these events to whoever has subscribed to a given event. This is by no means the best and only way to communicate in all situations. It is very easy to misuse this. If you were in a situation where you require a response, for instance, this is not what the event aggregator is designed for; it cannot enforce such a concept, and should not be used in this situation. The event aggregator is essentially a fire and forget method of communication.

The other ways modules can communicate is with shared services within the application. These would most likely be other non-UI modules within the application that a number of other modules use as a service. You would encapsulate the logic and task based stuff inside this 'service provider' module and then expose it through registering its internal types within the UnityContainer with the corresponding interface.

Another method of communication is through commanding. The CAL DelegateCommand<> works in very much the same way a standard WPF command. However, in order for the CanExecute to be evaluated, you have to manually invoke the RaiseCanExecute() method on the command. You can have commands for the Presenters, or you could make use of Global Commands. In the demo presented here, these globally available commands would live in the Core of a module, and anything subscribing to these commands would register themselves here and await execution.

Infrastructure / Shared Cores

The StockTrader reference implementation application has an idea of an Infrastructure library that contains all the shared/common objects that form part of the application. These will range from the DI interfaces, event definitions, event payloads, commands, global commands etc. In contrast to this, my demo does not use this approach. For a large application, this single shared library approach being built / depended on by multiple developers could very easily turn into an unwieldy beast of a library; instead, my demo modules provide a core library should they need to provide access to their innards to other modules. When a module is built, it will be copied into the composite location (where the app is executable), and into a common Int directory; this is where you can reference it statically (the core not the module) in order to "get at" its innards, by placing its interface on a constructor and asking Unity to inject it, or in order to subscribe or publish events.

It is very possible for a module to be so encapsulated that it may only ever need to subscribe or publish event aggregator events, in which case, using this approach, it wouldn't even have a core since no other module needs to directly interact with it, nor it any other module. You can see this in the Navigator module in the demo code.

Shell

The shell in this demo is the main region container. This could actually be farmed off into a module, or it could be contained in the main executable of the application. For this demo and to keep it simple (this is an introduction after all), this is a view in the main executable of the application. Here is the XAML that defines that view and creates the regions and registers them with the RegionManager.

<Window 
    x:Class="JamSoft.CALDemo.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"
    Title="JamSoft Composite Application Library Demo"
    Background="DarkGray"
    Width="600" 
    Height="600">
     <Grid>
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="*" />
             <ColumnDefinition Width="*" />
         </Grid.ColumnDefinitions>
         <Grid.RowDefinitions>
             <RowDefinition Height="35" />
             <RowDefinition Height="*" />
             <RowDefinition Height="25" />
         </Grid.RowDefinitions>
        
         <ItemsControl cal:RegionManager.RegionName="NavigatorRegion"
                      HorizontalAlignment="Stretch"
                      VerticalAlignment="Stretch"
                      Grid.Column="0"
                      Grid.ColumnSpan="1"
                      Grid.Row="0"
                      Grid.RowSpan="1" />
        
         <ContentControl cal:RegionManager.RegionName="ToolBarRegion" 
                        HorizontalAlignment="Stretch" 
                        VerticalAlignment="Stretch" 
                        Grid.Column="1" 
                        Grid.ColumnSpan="1" 
                        Grid.Row="0" 
                        Grid.RowSpan="1" />

         <Border x:Name="MainRegionBorder" 
                BorderBrush="Black" 
                BorderThickness="2" 
                Grid.Row="1" 
                Grid.RowSpan="1" Grid.ColumnSpan="2" 
                CornerRadius="3" 
                Margin="5" 
                Padding="5">
            
             <ContentControl cal:RegionManager.RegionName="MainRegion"
                            HorizontalAlignment="Stretch" 
                            VerticalAlignment="Stretch" 
                            Grid.Column="0" 
                            Grid.ColumnSpan="2" 
                            Grid.Row="1" 
                            Grid.RowSpan="1" />
         </Border>
        
         <ContentControl cal:RegionManager.RegionName="StatusBarRegion"
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Grid.Column="0"
                        Grid.ColumnSpan="2"
                        Grid.Row="2"
                        Grid.RowSpan="1"/>
     </Grid>
 </Window>

The executable is also the portion of the application that is the meat of configuring the container through the JamSoftBootstrapper. This class inherits from the UnityBootstrapper. Its job is to create and configure the container and register the shell view in the container. I won't go into the details of this as there is literally a plethora of things you can do here as you would expect, all well beyond the scope of this article. There are all manners of region adaptors that could be included here; there is a rather nifty WindowRegionAdaptor, for example, in the CodePlex CompositeWPFContrib project that you can use for pop-ups and launching other windows with their own regions and views. This actually is an important part of the CAL framework; as with any good framework, it is customisable and extendable. You can even replace the Unity container with the container of your choice, if you like.

The code for this particular Bootstrapper is completely standard, and looks like this:

internal class JamSoftBootstrapper : UnityBootstrapper
{
    protected override IModuleEnumerator GetModuleEnumerator()
    {
        return new DirectoryLookupModuleEnumerator("Modules");
    }

    protected override void ConfigureContainer()
    {
        Container.RegisterType<IShellView, Shell>();

        base.ConfigureContainer();
    }

    protected override DependencyObject CreateShell()
    {
        ShellPresenter presenter = Container.Resolve<shellpresenter>();
        IShellView view = presenter.View;
        view.ShowView();
        return view as DependencyObject;
    }
}

This is kicked off by just a couple of lines in the App.xaml.cs file:

public App()
{
    JamSoftBootstrapper bootStrapper = new JamSoftBootstrapper();
    bootStrapper.Run();
}

It's worth noting here that in the App.xaml file, you'll notice that there is no value associated with the normal StartupUri as this has been farmed off to the ShellPresenter:

<Application 
    x:Class="JamSoft.CALDemo.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
         
    </Application.Resources>
</Application>

The IShellView is the shell's interface that it is registered against in the container; this is really very simple, and looks like this:

public interface IShellView
{
    void ShowView();
}

The ShellPresenter is also very simple, and looks like this:

public class ShellPresenter
{
    public IShellView View { get; private set; }


    public ShellPresenter(IShellView view)
    {
        View = view;
    }
}

Runtime Module Discovery

This is a really neat thing about CAL. The ability for it to discover the modules at runtime. There are a few other methods, and you can also manually add these through various methods in the code or configuration files. I really like the dynamic approach using the DirectoryLookupModuleEnumerator.

Basically, your app starts up, the shell does its bits, and part of that is finding and initialising the modules. With the demo code, I have used the above mentioned IModuleEnumerator. This basically looks at all the DLLs in the specified directory and loads anything that implements IModule and is decorated with ModuleAttribute.

[Module(ModuleName="ModuleA")]

Once that enumeration process is complete, it will work out the module dependencies from ModuleDependencyAttribute:

[ModuleDependency("PageManagerModule")]

Once all of that is complete, it then works through these modules, injects anything on that class' constructor (IUnityContainer at the very least in order to register types), and runs the Initialize() method to prepare the module for use.

MVP

There are loads of variations on this such as MVC, MVVM, etc ... etc ... Suffice to say that it is actually pretty simple. You have a View (V), a Model (M), and a Presenter (P); very often, the M + P are the same thing. The decision on which to use is obviously your call; however, you don't want a massively complex model in your presenter, for instance. That would also be missing the point massively really since these constructs are there to make life easier not harder. Using this as basis for your structure is great taking into account WPF data binding bits; you can end up with a really powerful set of classes with very little actual code. Also, the less code-behind in the view classes, the better.

You can see it at work in these two snippets; the first is implemented on the view that is actually a UserControl:

public interface IStatusBarView
{
    IStatusBarPresentationModel Model { set; }
}

Then, we have the PresenterModel:

public StatusBarPresentationModel(IUnityContainer container, 
                                          IEventAggregator eventAggregator, 
                                          IStatusBarView view)
{
    _container = container;
    _eventAggregator = eventAggregator;

    _view = view;
    _view.Model = this;

    _eventAggregator.GetEvent<AppStatusMessageEvent>().Subscribe(
          OnAppStatusChanged, ThreadOption.UIThread, true);

    AppStatusMessage = "Ready...";
}

What has happened here is that the IStatusBarView has a single property defined as the same type as the interface implemented by our presentation model. So, when our presentation model is resolved from the container, the container makes an instance of the view when it injects the other required bits into the presenter. Then, the presenter sets itself as the model on the view.

The bit that makes all the magic happen is the setter on the Model property in the view's code-behind:

public IStatusBarPresentationModel Model
{
    set 
    {
        this.DataContext = value;
    }
}

Now, all the various bindable bits on our presenter are available within the DataContext of the view, primed for use as WPF Bindings. There really is some great information about this in the CAG stuff, so I won't go into any more detail here.

ModuleA

Yup, I sat up all night thinking about that name ... it's largely irrelevant for this demo anyway. OK, so what we have here is two projects:

  • JamSoft.CALDemo.Modules.ModuleA
  • JamSoft.CALDemo.Modules.ModuleA.Core

In the main module assembly, we have the following bits:

  • ModuleAModule.cs
  • ModuleAPresenterModel.cs
  • ModuleAView.xaml
  • ModuleAView.xaml.cs

In this module's core, we have:

  • IModuleAPresenterModel.cs
  • IModuleAView.cs

These two interfaces could have actually been left inside the main module assembly. We know this since we also know that no other module requests IModuleAPresenterModel anywhere else in the application (you can see a functioning version of this with the Navigator). However, this does provide a nice simple demo of how you can work without an Infrastructure library bringing all these things together.

In turn, module A has static references to its own Core and to the PageManager Core. This is so that the ModuleAPresenter can request a reference to the PageManager by asking the container to inject a reference to IPageManager in order to add itself as an available page in the application. Then, the Navigator module also has a reference to the PageManager Core so it can get a reference to the ObservableCollection<IPage> in order to then make these pages available in the navigator's ListBox for navigating around the application.

Page Manager - Navigation

As you look through the code, you will see that the PageManager module is set to load at start-up so that we can be sure that it is available for presenters to register themselves as an instance of an IPage object and add themselves to the page manager. This isn't absolutely necessary, since registering a module dependency inside modules that need to register a page will ensure that the PageManager module will be initialised before any modules that declare a dependency on it. The Navigator module registers a module dependency on the PageManager as it is the service within the application that it uses to control the view in the application's MainRegion. The Navigator is an ItemsControl region that hosts a ListBox. The ListBox item template is then used to style the items, and look like traditional buttons that users can click in order to navigate to the various pages in the application.

When a page registers itself, we also have a float value that is used to order those pages in the ObservableCollection<IPage> so that we can control the order of the buttons at runtime, baring in mind that the order in which modules are enumerated cannot be relied upon to dictate this ordering in the ListBox. The following code snippet shows the PageManager class:

public class PageManager : IPageManager, INotifyPropertyChanged
{
    private ObservableCollection<IPage> _pages = 
            new ObservableCollection<IPage>();
    private IPage _currentPage;
    private readonly IEventAggregator _eventAggregator;

    public PageManager(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
        _eventAggregator.GetEvent<PageRequestEvent>().Subscribe(
         OnPageSelected, ThreadOption.UIThread);
    }

    private void OnPageSelected(IPage page)
    {
        _currentPage = page;
        _eventAggregator.GetEvent<PageSelectedEvent>().Publish(page);
    }

     public ObservableCollection<IPage> Pages
    {
        get 
        {
            SortPages();
            return _pages;
        }
    }

    public IPage CurrentPage
    {
        get { return _currentPage; }
    }

    private void SortPages()
    {
        List<IPage> p = _pages.ToList<IPage>();
        p.Sort(new PagePositionComparer());
        _pages.Clear();
        foreach (IPage page in p)
        {
            _pages.Add(page);
        }
    }

    public IPage GetPage(string pageId)
    {
        IPage selPage = null;
        for(int i = 0; i < Pages.Count(); i++)
        {
            if (Pages[i].ID == pageId)
            {
                selPage = Pages[i];
            }
        }
        return selPage;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

The following code shows the MainRegionController class:

public class MainRegionController : IMainRegionController
{
    private readonly IUnityContainer _container;
    private readonly IRegionManager _regionManager;
    private readonly IEventAggregator _eventAggregator;

    private object _currentView;
    private IRegion _mainRegion;


    public MainRegionController(IUnityContainer container,
                                IRegionManager regionManager,
                                IEventAggregator eventAggregator)
    {
        _container = container;
        _regionManager = regionManager;
        _eventAggregator = eventAggregator;


        Initialize();
    }


    private void Initialize()
    {
        _eventAggregator.GetEvent<PageSelectedEvent>().Subscribe(
                         PageSelected, ThreadOption.UIThread, true);
        _mainRegion = _regionManager.Regions["MainRegion"];
    }

    private void PageSelected(IPage page)
    {
        if (page != null)
        {
            ShowPage(page);
        }
    }

    private void ShowPage(IPage page)
    {
        object newView = page.View;
        
        if (_currentView!=null)
            _mainRegion.Remove(_currentView);

        if (newView != null)
        {
            _mainRegion.Add(newView);
            _mainRegion.Activate(newView);
        }

        _currentView = newView;
    }
}

To make this particular point complete, we also need to see the NavigatorPresentationModel.cs to see where these PageRequestEvents come from:

public class NavigatorPresentationModel : INavigatorPresentationModel
{
    private readonly INavigatorView _view;
    private readonly IPageManager _pageManger;
    private readonly IEventAggregator _eventAggregator;


    public NavigatorPresentationModel(IEventAggregator eventAggregator,
                                      IPageManager pageManger, 
                                      INavigatorView view)
    {

        _eventAggregator = eventAggregator;
        _pageManger = pageManger;


        _view = view;
        _view.ItemChangeRequest += 
             new EventHandler<PageEventArgs>(view_ItemChangeRequest);
        _view.Model = this;

        _eventAggregator.GetEvent<PageSelectedEvent>().Subscribe(
                         OnPageSelected, ThreadOption.UIThread);
    }

    private void OnPageSelected(IPage page)
    {
        _view.SelectedItem = page;
    }

    void view_ItemChangeRequest(object sender, PageEventArgs e)
    {
        OnItemChangeRequest(e.Page);
    }

    private void OnItemChangeRequest(IPage page)
    {
        _eventAggregator.GetEvent<PageRequestEvent>().Publish(page);
    }

    public INavigatorView View
    {
        get { return _view; }
    }

    public ObservableCollection<IPage> Pages
    {
        get { return _pageManger.Pages ; }
    }
}

So to summarise, the navigator publishes an event defined in the PageManager's Core assembly which the PageManager is in turn subscribed to. This in turn selects the page and then fires off another event to the MainRegionController, which does the actual view switching for the MainRegion defined in the Shell view.

ModuleB

It just contains a presenter and a view and the associated bits to make it a module. It has a static reference to the PageManager Core in order to register itself as a page in the application. However, ModuleB is able to publish a string message to the StatusBar module.

So, ModuleB has three static assembly references to the following cores:

  • JamSoft.CALDemo.Modules.ModuleB.Core
  • JamSoft.CALDemo.Modules.PageManager.Core
  • JamSoft.CALDemo.Modules.StatusBar.Core

In the status bar core, we have a single CompositeWpfEvent defined as follows:

public class AppStatusMessageEvent : CompositeWpfEvent<string>
{
}

The StatusBarPresentationModel subscribes to this event in order to update the status bar message using the EventAggregator:

_eventAggregator.GetEvent<AppStatusMessageEvent>().Subscribe(
         OnAppStatusChanged, ThreadOption.UIThread, true);

This then calls the following code to update the message variable:

private void OnAppStatusChanged(string message)
{
    AppStatusMessage = message;
}

public string AppStatusMessage 
{
    get { return _appStatusMessage; }
    set 
    { 
        _appStatusMessage = value;
        NotifyPropertyChanged("AppStatusMessage");
    }
}

When you enter text in the TextBox supplied in the view and then hit the button, the text from the textbox is "pinged" to a CompositeWpfEvent which has a payload of string. This is then forwarded to the subscribed modules (StatusBar), and each module can then use this to do the required steps. In the case of the StatusBar, it simply prints the text to the screen in the TextBlock within a StatusBarItem. Simple.

Compiling it all Together

How is this all organised? There are a couple of ways of dealing with this. The all in one solution is nice and simple, but if you are working in a team of developers, this is not the best solution.

The demo code is organised into the following directory structure when compiled.

  • bin\
  • bin\Debug
  • bin\Debug\External
  • bin\Debug\Internal
  • bin\Debug\Modules
  • bin\Release
  • bin\Release\External
  • bin\Release\Internal
  • bin\Release\Modules
  • Ext (For third party controls / CAL - basically, source you don't own)
  • Int (Core assemblies you can statically reference, or stuff you do on your own)
  • ModuleA
  • ModuleB
  • Navigator
  • PageManager
  • Shell
  • StatusBar

When you build the app, the various assemblies are distributed using post build events into the various locations within the bin\xxxx directories. External stuff such as third party control DLLs and CAL itself go into the External directory; all the cores and any other DLLs you produce (your own control source code) go into the Internal directory, and the modules themselves go into the Modules directory in order to allow the DirectoyLookupModuleEnumerator to find them at runtime.

The executable is then placed in the root directory (bin\xxxx\) along with a configuration file that contains a definition to the control the directory probes to find the various core assemblies:

<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="Internal;External"/>
    </assemblyBinding>
  </runtime>
</configuration>

Another way this can be achieved is using environment variables. Your solution structure on disk may be so that this becomes a much more manageable approach, in fact. Once environment variables are set, you are not relying on complete paths to things, nor are you replying on the macros available through the post build event editor within Visual Studio.

That's it for Now

I think I'll leave it there for now. This really is skimming the surface, and to some extent, that was the idea for this article. In the next article, I'm going to expand on this application and build in some more interesting functionality. What I will also cover in Part 2 is a module I have built that powers dynamically discovering style DLLs in much the same way as CAL discovers your modules. Your users can then pick their favourite skin at run-time.

Further Reading and Useful Stuff

License

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

About the Author

Jammer
Team Leader JamSoft
United Kingdom United Kingdom
Got into .NET development just as WPF appeared on the scene ... I've never looked back ...

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberMember 423114320-Feb-12 13:52 
Nice work, you explain things nicely.
GeneralRe: My vote of 5memberJammer24-Apr-12 1:23 
Many Thanks!
 
Jammer
<a href="http://jammer.biz/blog2/">My Blog</a> | <a href="http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=4664921">Articles</a> | <a href="http://www.jammer.biz/Projects/dmon.php">DMon</a> | <a href="http://www.jamsoft.co.uk/samplesort.php">SampleSort</a>
Generalshared classesmemberpuromtec121-Jun-10 9:36 
Gotta a question again...
 
I have a class that I want to share across modules. It just contains a set of strings, no real functions. I like the idea of having a dedicated non-gui module for it. You wrote about it here:
 

The other ways modules can communicate is with shared services within the application. These would most likely be other non-UI modules within the application that a number of other modules use as a service. You would encapsulate the logic and task based stuff inside this 'service provider' module and then expose it through registering its internal types within the UnityContainer with the corresponding interface.

 
...you wrote about other means of doing this. Is there a good reason NOT to do it this way, and instead use one of the other ways that you offered?
 
Thanks.
GeneralRe: shared classesmemberJammer22-Jun-10 5:36 
It really depends on how you have structured the rest of the application. If you have used a 'monolithic' infrastructure library the sensible thing might be to make this a static class in there with a range of strings stored in constants within the class. If you haven't then it would make sense to have this in a service provider type module and expose the strings on an interface registered in the container like you suggest.
 
What function within the application do these strings play a part of?

GeneralRe: shared classesmemberpuromtec122-Jun-10 9:56 
This class contains strings that I use to psuedo-strongly type some weak datasets to avoid magic strings embedded in my app and to also help type the column names. Just a style of mine, that I'm satisfied with.
 
Looks like this...

public class MyTables
{
public static readonly string TableOne_FirstColumn = "FirstColumn";
public static readonly string TableOne_SecondColumn = "SecondColumn";
 
public static readonly string TableTwo_FirstColumn = "FirstColumn";
//etc...
}

 
I need to read these from two modules as it turns out... I have decided to place it in a .core that references them first to get this referenced quickly by another assembly, since it seems appropos based on your discussion of the purpose of the .core's
 
It appears that the granularity or specificality of the module scopes drive refactoring that either leads to filling .core's with more shared stuff or making new (non-gui) modules. If I kept the two modules as the same module, then this would not be an issue. But, hey, I'm module happy after seeing your demo...
GeneralRe: shared classesmemberJammer2-Jul-10 23:50 
Module Happy ... like it ... seems like you are going a sensible route regarding the modularisation.

QuestionCAL RefrencesmemberAli Elmi21-May-10 20:28 
Hi there,
 
Excellent Article,
Can you give me link of CAL for adding refrence.
AnswerRe: CAL RefrencesmemberJammer23-May-10 22:30 
Thanks for the positive comment. Not sure what you're requesting here though? What references do you want to add?

AnswerRe: CAL RefrencesmemberJammer23-May-10 22:35 
Give the article a vote if you like it! Smile | :)

GeneralVisual Studio Questionmemberpuromtec118-May-10 3:56 
After searching all of the beginner tutorials out there, yours was the best. Thanks for writing this article.
 
I would like to know how you managed to affect the AppDomain.CurrentDomain.BaseDirectory so that the runtime probes /bin/debug beneath the solution folder instead of the shell project. I'd like to adopt your organization of projects/classes and especially like the way it is executed from solution/bin/debug, but I cannot figure out how to make that happen in my solution.
 
Thanks.
GeneralRe: Visual Studio Questionmemberpuromtec118-May-10 4:40 
Ok...I found it. "Debug" tab on project properties, then "Start external program" radio button, pointed to /bin/debug.
GeneralRe: Visual Studio QuestionmemberJammer18-May-10 7:52 
Thanks for the kind words about the article. Glad you found your solution as well!
 
Prism rocks!
 
Regards,

GeneralRe: Visual Studio QuestionmemberJammer18-May-10 7:53 
Drop me a vote if you like it too! Smile | :)

GeneralRe: Visual Studio Questionmemberpuromtec118-May-10 8:52 
If I could vote repeatedly for 5 on one article, this would be it.
 
Btw, I found what *might* be a misnomer in your PageManager class.
 
Line 21: The function handler should have a name like "OnPageRequest", rather than "OnPageSelected" to reflect that it is handling the request of the page change.
GeneralRe: Visual Studio QuestionmemberJammer18-May-10 23:46 
Much obliged sir! Will look at that code later and fix it up in the demo!
 
Regards,

Question"How to" for CAL [modified]memberJosephKahl28-Apr-10 5:43 
Jammer,
 
I appreciate you breaking down some of the aspects of this framework. One of the best things about software development is the way people share information and help each other. Thanks so much. I have both of your samles running.
 
But here is my request: Are you aware of any simple step by step "How to" for CAL.
 
I find that looking at other people's examples only helps me part of the way. When it is time to build my own app I sit there staring at Visual Studio and flipping back and forth from one example to another. When I paste some code it doesn't build or there are some loose ends somewhere and the module fails to load or the service cannot be called. There are a lot of steps to using this framework. To your knowledge, has anyone ever just listed the steps for using CAL?
 
Sincerely, Joe
 
I found something helpful in the MSDN:
 
Composite Application Guidance for WPF and Silverlight - October 2009
Development Activities
http://msdn.microsoft.com/en-us/library/ff649780.aspx[^]
How to: Create a Solution Using the Composite Application Library
http://msdn.microsoft.com/en-us/library/ff649780.aspx

modified on Thursday, April 29, 2010 10:13 AM

AnswerRe: "How to" for CALmemberJammer28-Apr-10 6:25 
Hi Joseph,
 
Glad you are finding my examples useful. One of the main reasons I wrote these examples was to bring things down to a basic level and illustrate how you might use CAL. I can't really think of a scenario where you could simplify it much further and cover off anything useful to be honest. CAL / Prism is not a simple framework and it involves the use of WPF which certainly isn't simple.
 
I know that isn't much use but hope you find something. Let us know if you do.
 
Regards,

GeneralRe: "How to" for CALmemberJosephKahl29-Apr-10 4:29 
I found the sort of recipe style documentation that I was looking for in the MSDN.
 
Composite Application Guidance for WPF and Silverlight - October 2009
Development Activities

 
http://msdn.microsoft.com/en-us/library/ff649780.aspx[^]
 
Joe
GeneralVery informativememberBertron 512-Jan-10 2:01 
What an excellent example of CompositeWPF! I've really struggled finding an example that doesn't want to throw every module into the Shell at the same time!
 
This is exactly what I've been looking for, thank you!
 

Rob
London, UK
GeneralRe: Very informativememberJammer12-Jan-10 5:08 
Thats exactly what I made it for! Glad it's useful for you.
 
Best,
 

GeneralModule initializations/retreive informations after loadingmemberfanoo16-Jun-09 19:54 
Hi, congratulations for this awesome CAL article !
 
i use regular module initialize model to load on demand modules, but i'm facing to a problem :
When my app is launched, this one ask a username et password, i call a web service to retrieve user informations.
 
So i display a module that load on demand 6 modules (and one of it is NewsModule) the main module represent an user dashboard. So in module news i don't know the user id when this one become loaded.
Generaly in CAL samples modules simply load static data in xml file, etc.
 
In my case in NewsModule i need to be informed that this one needs to load news data with distant access with a specific Id.
 
So my question is what the best practice to access to this from NewsModule
GeneralRe: Module initializations/retreive informations after loadingmemberJammer17-Jun-09 1:13 
Hi,
 
Thank you!
 
I would imagine that this Id exists somewhere in the application before this NewsModule is loaded (user login?). So you could expose this Id property on an interface that the NewsModule requests the container to inject. The NewsModule can then connect to the web service and recieve the correct stuff based on this Id value.
 
Another option could be the shared service approach ...
 
Assuming that users do have some form of login you could centralise this with some form of SecurityService module that manages this for you. Anything that interacts with an entity based on this Id value can then request it from the SecurityServiceModule or simply pass in a reference to this property inside the SecurityServiceModule.
 
A nice vote for this article would be appreciated!
 
Cheers,
 

GeneralRe: Module initializations/retreive informations after loadingmemberfanoo17-Jun-09 6:16 
I would imagine that this Id exists somewhere in the application before this NewsModule is loaded (user login?). So you could expose this Id property on an interface that the NewsModule requests the container to inject. The NewsModule can then connect to the web service and recieve the correct stuff based on this Id value.
 
Yes it's exactly that !
 
Actualy i do that :
 
In main module container:
Container.RegisterType<User>("ConnectedUser", new ContainerControlledLifetimeManager());
User u = Container.Resolve<User>("ConnectedUser");
u.FirstName = "Toto";
u.LastName = "Tata";
u.Id= 34;
 

And in child module :
 
public class NewsModule : IModule
{
private readonly IRegionManager _regionManager;
private readonly IUnityContainer _container;
private readonly IEventAggregator _eventAggregator;
 
public NewsModule (IRegionManager regionManager, IUnityContainer container, IEventAggregator eventAggregator)
{
_container = container;
_eventAggregator = eventAggregator;
_regionManager = regionManager;
}
 
#region IModule Members
 
public void Initialize()
{
NewsListViewViewModel vm = _container.Resolve<NewsListViewViewModel>();
NewsListView view = _container.Resolve<NewsListView>();
view.DataContext = vm;
 
User u = _container.Resolve<User>("ConnectedUser");
 

_regionManager.AddToRegion("MainRegion", view);
}
 
#endregion
}
 

So i think that it's not that you specify but i don't understand how inject an instance of my user object in my NewsModule
GeneralRe: Module initializations/retreive informations after loadingmemberJammer17-Jun-09 10:02 
If your dealing with logins to your application I would suggest that you create a module who's job it is to manage the details of the currently logged in user. That way you simplify access to the information relating to that user by encapsulating it into a freely accessible location.
 
Also from the code snippet above (assuming it is 'complete' it appears you are resolving things from the container before they have been registered.
 
It also appears that you are performing tasks in your IModule object that are really things that should be happening further down the chain (on the presenters).
 
Have a look at the demo code included with this article and you'll see what I mean.
 

QuestionHow does Composite WPF fit to AcropolismemberSteve Hawkes15-Jun-09 20:15 
Can you tell me what is the conneciton between Composite WPF and Acropolis?
I thought Acropolis was supposed to be Composite Application Block for WPF
and this was placed on hold by Microsoft.
Is Composite WPF the replacement and is this fully supported by Microsoft.
 
Great article but just as little confused over where Composite WPF is in the
whole CAB story.
 
TIA
 
Steven Hawkes

AnswerRe: How does Composite WPF fit to AcropolismemberJammer16-Jun-09 1:11 
Hi Steve,
 
I've only really encountered the Acropolis on the WindowsClient site so I'm not in the best position to comment on that. I remember downloading the sample code solutions which all appeared to be broken! I'm really not sure what Acropolis even is! From looking at the source files, it looks like it could well be a CAL type thing built into .NET.
 
As for CAL, it has a very active discussion board on CodePlex and is from the P&P team at Microsoft. They have obviously put a lot of time and investment into it.
 
Sorry, I've not been much help here!
 
Cheers,
 

GeneralRe: How does Composite WPF fit to AcropolismemberSteve Hawkes16-Jun-09 1:35 
No matter, you have stimulated my interest again to go and read mor eon CAl, thanks
 
Steven Hawkes

GeneralRe: How does Composite WPF fit to AcropolismemberJammer16-Jun-09 2:16 
No problem! If you feel like giving me a vote that would be appreciated!
 
Cheers,
 

GeneralI like it, and its book markedmvpSacha Barber13-Jun-09 5:36 
Its a great article.
 
Though sadly, I just wish I could get excited about CAL. I just do not like it.
 
However this article is ace.
 
Have a 5.
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: I like it, and its book markedmemberJammer13-Jun-09 6:17 
Thanks very much Sacha, much appreciated.
 
It really is a shame your not interested in CAL, you'd make some cool stuff for it I'm sure. I'm working on Part 2 at the moment, lots more interesting stuff in there already.
 
Cheers,
 

GeneralRe: I like it, and its book markedmvpSacha Barber13-Jun-09 8:21 
Jammer
 
My 1st WPF job was ripping out SCSF and PRISM/CAL is just not any better. I mean jesus EventAggregator/DelegateCommand are the best parts.
 
The rest sorry to say plain sucks.
 
Thats not to say you have not done a great article, cos you have.
 
Me I just like control over ALL my code. You know I will be writing a book with Daniel Vaughan who I like a lot, and he is PRISM master, so I cant disagree that much.
 
I would like to see what you think of the hand rolled solution when you see my new MVVM framework, I am most pleased with it so far. Alas you must pay for the book 1st.
 
Now go away and ditch PRISM and get laid or something, and get back to reality.
 
Word up
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: I like it, and its book markedmemberJammer13-Jun-09 8:26 
Haha! Nice ...
 
Look forward to seeing your framework! Shame I have to wait for the book!
 
Cheers,
 

GeneralExcellentmemberDaniel Vaughan12-Jun-09 9:23 
Jammer,
 
I really like this one, but then I would Wink | ;)
Great coverage of the all the main Cal topics. Awesome.
 
5 stars from me.
 
Cheers,
Daniel
 
Daniel Vaughan
Blog: DanielVaughan.Orpius.com

GeneralRe: ExcellentmemberJammer12-Jun-09 9:30 
Wow!! Thank you very much Daniel. Very much a fan of the stuff you are doing with Calcium. Very, Very Cool ...
 
Much Obliged Sir.
 
Cheers,
 

GeneralGreat job!memberSnufflufugus12-Jun-09 6:33 
I toiled for a few days on the StockTrader app when I was trying to wrap my head around this, so I bet lots of folks will find it very useful.
 
Have a 5.
GeneralRe: Great job!memberJammer12-Jun-09 8:50 
Brilliant, thanks chap.
 
Any points that you think I should expand on or add for that kind of audience?
 
Cheers,
 

GeneralVery nicememberWes Aday12-Jun-09 4:07 
I was wondering how long it would be before you did an article... Smile | :)
 
Why is common sense not common?
Never argue with an idiot. They will drag you down to their level where they are an expert.
Sometimes it takes a lot of work to be lazy
Individuality is fine, as long as we do it together - F. Burns
 
Help humanity, join the CodeProject grid computing team here

GeneralRe: Very nicememberJammer12-Jun-09 5:44 
hehe ... Smile | :)
 
Part 2 is in the offing so shouldn't be too long before the next one is up!
 
Cheers fella,
 

GeneralRe: Very nicememberWes Aday12-Jun-09 8:45 
Looking forward to it
 
Why is common sense not common?
Never argue with an idiot. They will drag you down to their level where they are an expert.
Sometimes it takes a lot of work to be lazy
Individuality is fine, as long as we do it together - F. Burns
 
Help humanity, join the CodeProject grid computing team here

GeneralRe: Very nicememberJammer12-Jun-09 8:49 
Working on it right now!
 

GeneralVery good article!memberSevenate12-Jun-09 3:04 
Thanks a lot!
GeneralRe: Very good article!memberJammer12-Jun-09 3:15 
Don't mention it. Hope you find the code useful!
 

GeneralRe: Very good article!memberJammer12-Jun-09 3:16 
If you feel like giving it a 5 that would be great too!
 

GeneralRe: Very good article!memberSevenate12-Jun-09 3:21 
Of course, I put 5 Smile | :) ! Just forgot to mention this in the comment, sorry.
GeneralRe: Very good article!memberJammer12-Jun-09 5:39 
Thanks fella! Smile | :)
 

GeneralExcellentmemberRugbyLeague11-Jun-09 22:27 
I will have to have a play around this. Thank you for the great explanation.
GeneralRe: ExcellentmemberJammer11-Jun-09 22:29 
Thanks very much, glad you like it!! I hope you get on with it well and find it easy to get on with. Any problems, drop me a line on this forum and I'll see what I can do!
 
Cheers,
 

GeneralGreat articlemvpPete O'Hanlon11-Jun-09 10:11 
Jammer me old mucker
 
This is a great article - it certainly fills a gap in the market. Have yourself a 5 sir.
 

"WPF has many lovers. It's a veritable porn star!" - Josh Smith

As Braveheart once said, "You can take our freedom but you'll never take our Hobnobs!" - Martin Hughes.

My blog | My articles | MoXAML PowerToys | Onyx



GeneralRe: Great articlememberJammer11-Jun-09 10:18 
Yo Yo Petey O!!!
 
I was stressing over whether it was worth publishing since there are some great things out there for CAL but I do think it has something to offer.
 
I just wish MS in general would include exponentially more complex illustrations for things like CAL. I'm sure people have overlooked it due to the StockTraderRI being a bit impregnable.
 
Thanks fella!
 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130617.1 | Last Updated 12 Jun 2009
Article Copyright 2009 by Jammer
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid