Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / WPF

CAL: Beginners guide to Modular applications: Part 1 of n

Rate me:
Please Sign up or sign in to vote.
4.76/5 (23 votes)
13 Aug 2009CPOL5 min read 56.5K   300   59   21
A simpler series to learn the Composite application library

Introduction

There are a lot of great articles about Composite; as an example, the Calcium articles from Daniel Vaughan, which are great, well documented, well implemented, etc... But, are very complex, and I have seen a lot of people that have dropped the study of this amazing technology because of the complexity of the articles available. Well, this is a bit of a problem, right? Great technologies should make our life easier, not bring so much complexity that we have to spend days and nights to get the point.

Composite has a beautiful purpose, and despite what has been painted about it, it is very simple to understand and to put it to work properly; so now is the time for let the small talk behind and look upon the future of WPF and Silverlight applications.

Summary

Since this is a part 1 of n, the content of this article set can vary, but my intension is to follow this sequence:

  1. Creating a basic Composite WPF application (this article)
  2. Commands in CAL (fancy name yet to be announced)
  3. Creating custom region adapters (fancy name yet to be announced)
  4. Log strategies (fancy name yet to be announced)
  5. Multiple developer teams, single solution (fancy name yet to be announced)
  6. Considerations of benefit-cost ratio of a CAL application for an enterprise (fancy name yet to be announced)

With this, I think we will cover the basics that will allow us to understand better the marvelous implementations of the complex articles available.

Pre-requisites

Obviously, we have one thing we need to get, the Composite Application Library. Here is the Composite website.

After the installation of the Composite Application Guidance pack, I strongly recommend you to read the documentation; I know it is enormous, but worthy sometime of study.

Hands-on

Starting up, you have the CAL downloaded and have built it, time to start playing with your newest toy. So let's begin with the basics.

CALient.Core

CALient.Core is the core of our application, it’s the heart and soul of it, is a WPF application that will be the center of our CAL universe.

Let's create our solution. In my opinion, the best way of creating a Composite solution is to create a Visual Studio blank solution. Why? Because you can, and will, organize your solution in solution folders; this way, it becomes simpler to know where everything is on your solution.

BlankSolution.jpg

As shown in the above image, select from your "Other Project Types", the "Visual Studio Solutions", and choose the "Blank Solution". This will give you, literally, a blank solution as shown below:

BlankSolutionExplorer.jpg

Now, inside the solution, create a solution folder called Client; inside of this folder, you will create a new project, a WPF Application Project, and will call it CAL.Core.

Now is the time for some personal preferences, such as the core of the application. I think the core of an application isn't meant to be a program/executable, but a library, so you right click and open the properties of the project, and the following screen will open for you:

CorePropertyTab.jpg

On this tab, simply change the output type of your project to a class library. Now, simply delete your window1.xaml and your App.xaml, you won’t need them.

Now, we will add to the project the name of the Regions we will register our modules, like:

C#
namespace CALient.Core
{
    public class RegionMapping
    {
        public static string REGION_Content = "Region_Content",
            REGION_Menu = "Region_Menus";
    }
}

Inside this project, create a folder called DesktopShell, and inside of it, you add a Window called Shell.xaml.

Then, you add the references for the Composite Application Library, and in Shell.xaml, you add the following lines:

XML
<Window 
    x:Class="CALient.Core.DesktopShell.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:CALient="clr-namespace:CALient.Core"
    Title="Shell" Height="300" Width="300">
    <DockPanel>
        <ItemsControl 
           CAL:RegionManager.RegionName="{x:Static CALient:RegionMapping.REGION_Menu}" 
           DockPanel.Dock="Top"/>
        <ContentControl 
           CAL:RegionManager.RegionName="{x:Static CALient:RegionMapping.REGION_Content}"/>
    </DockPanel>
</Window>

The Desktop shell is ready for the scope of this article... pretty simple, right? No secrets there.

Now for the application to work, we will need something called a Bootstrapper. This will extend from the UnityBootstrapper in the Composite Application Library, so let’s do that now:

C#
class Bootstrapper : UnityBootstrapper
{
    protected override System.Windows.DependencyObject CreateShell()
    {
        var shell = new Shell();
        shell.Show();
        return shell;
    }

    protected override 
         Microsoft.Practices.Composite.Modularity.IModuleCatalog GetModuleCatalog()
    {
        // To be simpler to understand we use here
        // just a Directory Module Catalog that will search
        // for available modules in a certain directory,
        // in this case our application root directory
        var modules = new DirectoryModuleCatalog() { ModulePath = @"./" };

        return modules;
    }
}

Now, we create a class to start up everything like this:

C#
public class ApplicationStarter
{
    public static void Run()
    {
        Bootstrapper b = new Bootstrapper();
        b.Run();
    }
}

Great. Core set up and ready to go.

Now, let’s create an application to start that up.

Create another project inside the Client Solution folder, call it CALient, remove Window1.xaml and remove StartupURI="Window1.XAML" in App.xaml. Add the reference to the CALient.Core and now go to App.xaml.cs and add the following line in the CTOR method:

C#
public App()
{
    ApplicationStarter.Run();
}

Run it... and bam! A blank window =D, all this for a blank window... yep, now is the time to build the interesting part, the Modules.

How do I anyway?????

We will do the following. Let’s add a project for the modules, inside a new solution folder called Modules. In this Module, just add a WPF UserControl Library and call it any name you like; in this project, it is called CALient.One.

Then, you will delete UserControl1.xaml and add a class to this newly created assembly, call it with [name]Module, and you will have the following:

Open the file and add the following code snippet:

C#
[Module(ModuleName = "Module One")]
public class OneModule : IModule
{
    IRegionManager regionManager;

    public OneModule(IRegionManager regionManager)
    {
        this.regionManager = regionManager;
    }

    public void Initialize()
    {
        regionManager.RegisterViewWithRegion(RegionMapping.REGION_Menu, typeof(MenuView));
    }
}

This module will be composed of three Views and a menu that calls the 3 Views, like this:

MenuView.jpg

Module1.jpg

Module2.jpg

So the MenuView will be like:

XML
<UserControl x:Class="CALient.One.MenuView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >
    <StackPanel Orientation="Horizontal">
        <Button Margin="10" Width="100" Click="Button1_Click">
            <Button.Content>
                <Image Source="/CALient.One;component/Images/imgOK.png" />
            </Button.Content>
        </Button>
        <Button Margin="10" Width="100" Click="Button2_Click">
            <Button.Content>
                <Image Source="/CALient.One;component/Images/imgCancelar.png" />
            </Button.Content>
        </Button>
    </StackPanel>
</UserControl>

In the code behind (since our focus here is to explain this approach and the plan is for using commands only, in the next article, we won't use commands here, but if you like, you can use them just fine =D):

C#
//... removed for clarity ...
UserControl current;

private IRegionManager regionManager;

public MenuView(IRegionManager regionMgr
{
    InitializeComponent();
    regionManager = regionMgr;
}

private void Button1_Click(object sender, RoutedEventArgs e)
{
    if(current != null)
        regionManager.Regions[RegionMapping.REGION_Content].Remove(current);
    current = new ModuleView1();
    regionManager.Regions[RegionMapping.REGION_Content].Add(current);
}

private void Button2_Click(object sender, RoutedEventArgs e)
{
    if (current != null)
        regionManager.Regions[RegionMapping.REGION_Content].Remove(current);
    current = new ModuleView2();
    regionManager.Regions[RegionMapping.REGION_Content].Add(current);
}
//... removed for clarity ...

Another important issue is that once we build the application, the module won't be deployed in the correct position (in the root folder); for that to happen, you will have to add the following line in the Post-build event commandline in the module project:

xcopy "$(TargetDir)*.*" "$(SolutionDir)CALient\bin\$(ConfigurationName)\" /Y

As you have seen, our Views are just static UserControls containing different background colors and different content text.

Run it again

As you can see now, by running the solution again, our solution has been built successfully, and that by clicking the buttons on our MenuView of the Module, we start up the View, without knowing what is it, or from where it comes from; that’s the beauty of Composite; decoupled development at its maximum.

Finish up

Hope this turns out to be useful and that you like it. See you in the next article (sooner this time I hope).

License

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


Written By
Architect
Brazil Brazil
Senior Software Architect from Brazil.

Comments and Discussions

 
GeneralMy vote of 3 Pin
LucianPopescu29-Nov-10 1:32
LucianPopescu29-Nov-10 1:32 
GeneralGreat Pin
BigNat19-Jan-10 2:38
BigNat19-Jan-10 2:38 
GeneralRe: Great Pin
Raul Mainardi Neto20-Jan-10 7:48
Raul Mainardi Neto20-Jan-10 7:48 
GeneralWell done! Pin
Daniel Vaughan31-Aug-09 8:54
Daniel Vaughan31-Aug-09 8:54 
GeneralRe: Well done! Pin
Raul Mainardi Neto31-Aug-09 9:10
Raul Mainardi Neto31-Aug-09 9:10 
GeneralUnit Tests Pin
Yazid30-Aug-09 10:39
Yazid30-Aug-09 10:39 
GeneralRe: Unit Tests Pin
Raul Mainardi Neto30-Aug-09 17:17
Raul Mainardi Neto30-Aug-09 17:17 
First of all thanks for your interest in this article.

Now for your question... This implementation was not actually intended to be the final one, and the initial idea is to explain unit test in the next article that shall be delivered next week, in the scope of this first article we are considering that everything works fine (so we actually don't test).. in the second part (don't like to talk about it before the deliver) we will put commands into work, for that to happen we will have to handle the possible errors due to a miss deployed module, like without a prerequisite module for example... so I ask for your patience.. it will be out there in a couple of days... OK? Hope that you like and can put it into use.

Cheers
GeneralRe: Unit Tests Pin
Yazid30-Aug-09 21:45
Yazid30-Aug-09 21:45 
GeneralGreat intro Pin
Daniel McGaughran17-Aug-09 16:28
Daniel McGaughran17-Aug-09 16:28 
GeneralRe: Great intro Pin
Raul Mainardi Neto18-Aug-09 1:47
Raul Mainardi Neto18-Aug-09 1:47 
GeneralExpecting part II Pin
ucha14-Aug-09 4:13
ucha14-Aug-09 4:13 
GeneralRe: Expecting part II Pin
Raul Mainardi Neto14-Aug-09 4:51
Raul Mainardi Neto14-Aug-09 4:51 
GeneralThanks so much Pin
yoktobit14-Aug-09 3:01
yoktobit14-Aug-09 3:01 
GeneralRe: Thanks so much Pin
Raul Mainardi Neto14-Aug-09 3:41
Raul Mainardi Neto14-Aug-09 3:41 
GeneralBest Article Pin
Thanigainathan.S13-Aug-09 19:22
Thanigainathan.S13-Aug-09 19:22 
GeneralRe: Best Article Pin
Raul Mainardi Neto14-Aug-09 1:59
Raul Mainardi Neto14-Aug-09 1:59 
Generalgood 1st article on PRISM Pin
Sacha Barber12-Aug-09 22:10
Sacha Barber12-Aug-09 22:10 
GeneralRe: good 1st article on PRISM Pin
Raul Mainardi Neto13-Aug-09 2:01
Raul Mainardi Neto13-Aug-09 2:01 
GeneralRe: good 1st article on PRISM PinPopular
Sacha Barber13-Aug-09 2:52
Sacha Barber13-Aug-09 2:52 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.