Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

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

0.00/5 (No votes)
19 Jul 2009 2  
An article showing an extremely simple implementation of CompositeWPF.

Introduction

Welcome to Part 2. If you have just landed on this article, you may want to pop off and read Part 1 first, here.

In Part 1, we went through the basics of some thinking you may want to do before starting on a CAL based application. We covered off the basics of what CAL brings to the table and how to go about structuring the foundations of a CompositeWPF application. I covered topics such as Modularity, how the container works with the MVP pattern, and topics such as Dependency Injection and how and when to use the various communication methods that can be employed in a CAL application.

Some of the other topics included how to go about compositing the application together in the dev environment and how to go about planning some aspects of coordinating the overall development effort. As I said many times in that article, it is just skimming the surface of various methodologies of how to structure a CAL application. If you have read the Composite Application Guidance (a 305 page document), a lot of that is simply beyond the scope of a CodeProject article without potentially wandering off and losing readers and so forth. Read the docs, it really is worth it.

What is Happening in Part 2?

With this article, I'm going to build on (literally) what was covered in Part 1. Assuming that you have had a play around with the application supplied with Part 1, looked through the code, and maybe even read the Composite Application Guidance documentation, you'll know that there are many paths to take. I think that is one of the wonders of software development; it's like a huge amorphous puzzle that we all know has an end somewhere, or a path of least resistance, but we're all still looking for it. Personally, I have only been a software developer for about 18 months, and frankly, I'm still like an over-excited dog constantly on the lookout for a bigger stick (that I'll no doubt brain myself with at some point)! In that time, I've progressed from a complete .NET newbie to a Technical Lead for my employer, and I think one of the best assets I have is that I know I know nothing at all. Meaning that all paths are still game-on, but I can see when something makes life a lot easier; CAL is one of those things in many respects (Sacha is probably cringing right now, however :); anyway ... I fear I'm rambling again ...

So, here is a run down of what's going to be covered in this article:

  • Asynchronous Service Calls
  • Master/Detail WPF Data Binding
  • User Settings Management
  • External Styling DLLs
  • Dynamic Skin DLL Discovery and Loading (CAL Stylee!)
  • Dynamic ToolBars
  • CAL Interfaces

New Modules for Part 2

Since the last article, the solution has grown to 21 projects. The new projects for this article include the following:

SolutionGraphic.jpg

  • JamSoft.CALDemo.Modules.MusicSearch
  • JamSoft.CALDemo.Modules.MusicSearch.Core
  • JamSoft.CALDemo.Modules.SettingsManager
  • JamSoft.CALDemo.Modules.SettingsManager.Core
  • JamSoft.CALDemo.Modules.SkinManager
  • JamSoft.CALDemo.Modules.SkinManager.Core
  • JamSoft.CALDemo.Modules.ToolBar
  • JamSoft.CALDemo.Modules.ToolBar.Core
  • JamSoft.CALDemo.UI.BlueTheme
  • JamSoft.CALDemo.UI.DefaultTheme
  • JamSoft.WpfThemes.Utils

These new projects don't have solutions of their own however (one of the main points covered in Part1), so we will have to simply rely on the main JamSoftDebugger.sln file in the root of the application directory. The MusicSearch module makes use of a Web Service provided by the MusicBrainz website to search for recording artists and the releases stored against them in the MusicBrainz database. I did toy with the idea of having a weather reports based module, but considering that I'm English and I wish to help dispel the idea that all we do is talk about the weather over cream teas, I decided against it.

So, to prevent the loss of more readers, let's dig into the new code.

Asynchronous Service Calls

A sign of any good programmer/application is the use of methods and processes that don't lock up the UI; locking up the UI doesn't just affect our application, it affects the smooth running of the whole machine whilst our task is being completed ... very bad. There are many articles already on the CodeProject site that cover this topic in more detail than I'm going to here. So, I'll just cover a few code snippets from the application, and provide a brief run down of what's happening. The first thing I did was to create a class to handle the data retrieval that sub-classes the AsyncCompletedEventArgs class. Here is the complete code from the ArtistSearchCompletedEventArgs class contained within the MusicSearch module:

public class ArtistSearchCompletedEventArgs : AsyncCompletedEventArgs
{
    public List<Artist> ArtistsSearchResults { get; private set; }

    public ArtistSearchCompletedEventArgs(List<Artist> artistsSearchResults)
        : this(artistsSearchResults, null, false, null)
    {
    }

    public ArtistSearchCompletedEventArgs(List<Artist> artistsSearchResults, object userState)
        : this(artistsSearchResults, null, false, userState)
    {
    }

    public ArtistSearchCompletedEventArgs(Exception ex, bool cancelled, object userState)
        : this(null, ex, cancelled, userState)
    {
    }

    public ArtistSearchCompletedEventArgs(List<Artist> artistsSearchResults, 
           Exception ex, bool cancelled, object userState)
        : base(ex, cancelled, userState)
    {
        ArtistsSearchResults = artistsSearchResults;
    }
}

Once you have your async 'payload' defined, you can make use of the Action<> delegate to construct your asynchronous method like this:

public void PerformArtistQueryAsync(Action<object, 
       ArtistSearchCompletedEventArgs> callback, object userState)
{
    ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
    {
        try
        {
            _eventAggregator.GetEvent<AppStatusMessageEvent>().Publish(
                       "Getting Artists: " + _artistSearchTerm);

            Query<Artist> results = Artist.Query(_artistSearchTerm);

            callback(this, new ArtistSearchCompletedEventArgs(
                               new List<Artist>(results), userState));

        }
        catch (Exception ex)
        {
            callback(this, new ArtistSearchCompletedEventArgs(ex, false, userState));
        }
    }));
}

As you can see, we are using a WaitCallBack and the ThreadPool to perform the work; this is more or less like using a BackgroundWorker since the call should be a fairly short lived process, so it's ideal for this kind of thing. The first thing we do is pop off a message to the status bar to inform the user that the search is in progress. The _artistSearchTerm is the private backing field for a two way bound public property on our MusicSearchPresenterModel bound to the text box in the UI, so whatever is in the text box is also available to this method on the presenter model via this property.

So, to make use of this in the application, I have also created a CAL DelegateCommand<object>. This command is on the MusicSearchPresenterModel and is bound to the 'Search for Artists' button in the MusicSearchView. The command has both an Executed and CanExecute delegate associated with it like this:

private void SearchForArtistCommandExecuted(object obj)
{
    _canSearchForArtists = false;
    _searchForArtistCommand.RaiseCanExecuteChanged();

    PerformArtistQueryAsync(ArtistSearchCallback, new object());
}

private bool SearchForArtistCommandCanExecute(object obj)
{
    return _canSearchForArtists;
}

Now, for the callback. The PerformArtistQueryAsync method invocation is where we register our callback method that will be invoked when the query has been completed and the data is ready for processing within the application. This is where we consume the ArtistsSearchQueryCompletedEventArgs detailed above.

public void ArtistSearchCallback(object sender, ArtistSearchCompletedEventArgs args)
{
    if (args.Error == null)
    {
        MusicSearchObjectConverter objConverter = new MusicSearchObjectConverter();
        Artists = objConverter.Convert(args.ArtistsSearchResults);
    }

    _canSearchForArtists = true;
    _searchForArtistCommand.RaiseCanExecuteChanged();

    _eventAggregator.GetEvent<AppStatusMessageEvent>().Publish("Ready...");
}

One job that is being completed here is the wrapping of the returned results as they are not ideal for binding to the UI. In some respects, this is also a good idea as it separates your application domain objects from the objects you don't have direct control over. If we were using WCF here, this would be a good idea since WCF is very version tolerate whereas your UI/Application will not be happy if properties/data start randomly disappearing from your objects.

So once this service call / wrapping is completed, the ObservableCollection<BindableArtist> is set to the Artists property on our presenter model, and the ListBox in the UI is updated with the collection of artists received from the MusicBrainz Web Service. Job done. Obviously, exactly the same process is completed when selecting an artist and then the 'Search for Releases' button is clicked in the UI, so I won't repeat the explanation of that process here. So, let's have a look at this Master/Detail binding in the MusicSearchView.

Master/Detail WPF Data Binding

WPF makes the job of using the master/detail data binding a complete breeze. The data binding prowess of WPF really is an awesome thing to use, and it's actually really simple in the MusicSearchView. Let's start by going back to the source of the data being used. It's a simple ObservableCollection<> on our presenter model exposed to the XAML in a property called Artists, like this:

private ObservableCollection<BindableArtist> _artists;
public ObservableCollection<BindableArtist> Artists
{
    get { return _artists; }
    private set 
    { 
        _artists = value;
        NotifyPropertyChanged("Artists");
    }
}

This is consumed in the XAML ListBox like this:

<ListBox x:Name="lstbArtists" 
 ItemsSource="{Binding Artists}"
 SelectedValue="{Binding SelectedArtist, Mode=TwoWay}"
 DisplayMemberPath="Name"
 Grid.Row="1" 
 Grid.RowSpan="1" 
 Grid.Column="0" 
 Grid.ColumnSpan="1" />

You'll notice in the XAML that the ListBox.SelectedValue is bound to another property on the presenter class that represents the currently selected BindableArtist object. This is in turn used by the methods for getting the artists releases when the 'Search for Releases' button is clicked.

Now, to use this particular master/detail binding, we have two text boxes in the UI that are bound to the ListBox.SelectedItem property in order to display the contents of the BindableArtist object like this:

<TextBox x:Name="SelectedArtistName" 
     Style="{DynamicResource JamSoftTextBoxStyle}" 
     Text="{Binding Path=SelectedItem.Name, ElementName=lstbArtists, Mode=OneWay}" />
         
<TextBox x:Name="SelectedArtistId" 
     Style="{DynamicResource JamSoftTextBoxStyle}" 
     Text="{Binding Path=SelectedItem.Id, ElementName=lstbArtists, Mode=OneWay}" />

And, that really is all that is required in this particular scenario to achieve the desired master/detail binding. The magic is all handled by WPF. The text box elements are bound to the list box using ElementName; then, we access the SelectedItem property on the ListBox, and then in turn they access the properties on the BindableArtist object Name and Id. Done!

User Settings Management

For this application, I created a very simple settings management idea that does not use the .NET provided Properties.Settings.Default methodology. It's a very simple module, and not intended for production, but it does the job of illustrating how you could approach this in a CAL application and make use of the EventAggregator for saving settings and a simple generic call on the interface T GetSettingDefaultValue<T>(string key); to get your settings back out of the SettingsManager module. The SettingsView.xaml is very simple, and simply contains the ComboBox used to provide the user with a way of selecting the desired skin. The view looks like this:

<UserControl 
    x:Class="JamSoft.CALDemo.Modules.SettingsManager.SettingsView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    >
    <Grid>
        <StackPanel>
            <ComboBox x:Name="cmbSkinPicker" 
                      ItemsSource="{Binding Skins}" 
                      SelectedValue="{Binding Path=CurrentSkin, Mode=TwoWay}" 
                      DisplayMemberPath="Name" />
        </StackPanel>
    </Grid>
</UserControl>

When the SettingsManagerPresentationModel is created, one of the things it requests from the container is a reference to the ISkinManager in order to provide this as the DataContext for the combo box at run-time. This means that the SettingsView actually has two Models, one for general settings and one for the skin manager. This interface looks like this:

public interface ISettingsView
{
    ISettingsManagerPresentationModel Model { set; }
    ISkinManager SkinPickerModel { set; }
}

In the settings manager core library, we also have an EventAggregator event defined along with a custom object that can be used to persist the settings in the serialized settings file. This custom object is very simple, and contains just a name and object properties for providing a new setting value for persistence.

public class SettingChangedEventArgs : EventArgs
{
    public string SettingName { get; private set; }
    public object SettingValue { get; private set; }

    public SettingChangedEventArgs(string name, object value)
    {
        SettingName = name;
        SettingValue = value;
    }
}

I'm not going to go into any more detail on this module as I have already said this isn't something that was intended for production code, but it does illustrate how to go about some management tasks in a CAL application by centralizing shared constructs and 'funneling' common tasks into a single location. The SettingsManager class is the fella that provides the logic, which in turn uses a PropertiesCollection object that provides the ability to serialise a NameValueCollection to a custom XML settings file.

External Styling DLLs

As has been said many times, WPF is great for providing separation of logic and the 'look and feel' of the UI itself through the use and application of Style declarations. It is also very simple to remove these ResourceDictionary files into a completely separate DLL file which can then be shared by the various applications you build. If you were adding in these resources to your main executable project, you would have the MyStyle.xaml file inside your application project, and then you would have something like this in your App.xaml:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="ButtonStyles.xaml"/>
            <ResourceDictionary Source="ListBoxStyles.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

If you moved all of these resource dictionaries into a separate project, you would then need to augment that XAML and change it into something like this:

<Application.Resources>
    <ResourceDictionary Source="/MyDLLName;component/MyResources.xaml" />
</Application.Resources>

The key difference here is the use of the /MyDLLName;component/ construct to inform the runtime that the file you are looking for is in the DLL specified in the path. However, in this application, we have gone a step further than this. The style DLLs are discovered at run-time in the same way that CAL discovers the modules using DirectoryLookupModuleEnumerator. This brings us on nicely to the next section that explores this idea.

Dynamic Skin DLL Discovery and Loading

In this version of the application, we have a new module called the SkinManager. When this module is initialised at application start up, it performs an enumeration of all *.dll files in the 'Skins' directory and stores in a List<Skin> any DLL that is decorated with AssemblySkinNameAttribute and AssemblySkinDescriptionAttribute. These are two attributes that are defined in the JamSoft.Wpf.Themes.Utils DLL included in the solution. These are very simple custom attribute classes that look like this:

public class AssemblySkinNameAttribute : Attribute
{
    public string AssemblySkinName { get; set; }

    public AssemblySkinNameAttribute(string assemblySkinName)
    {
        AssemblySkinName = assemblySkinName;
    }
}

public class AssemblySkinDescriptionAttribute : Attribute
{
    public string AssemblySkinDescription { get; set; }

    public AssemblySkinDescriptionAttribute(string assemblySkinDescription)
    {
        AssemblySkinDescription = assemblySkinDescription;
    }
}

Each DLL that is found in the 'Skins' directory is evaluated to discover if it is decorated with these attributes; if they are present, the DLL is treated as a skin and added to the list of available skins for the user to select. Also, the contents of these attributes are extracted and used in the UI for identifying the different skins. This is performed in the IsSkin(FileInfo file) method in the SkinsFinder class. This method looks like this:

private bool IsSkin(FileInfo file)
{
    bool isSkin = false;
    Assembly skinAssembly = Assembly.LoadFrom(file.FullName);
    AssemblySkinNameAttribute[] skinNames = null;
    AssemblySkinDescriptionAttribute[] skinDescriptions = null;

    try
    {
        skinNames = (AssemblySkinNameAttribute[])
          skinAssembly.GetCustomAttributes(typeof(AssemblySkinNameAttribute), true);
        skinDescriptions = (AssemblySkinDescriptionAttribute[])
          skinAssembly.GetCustomAttributes(typeof(AssemblySkinDescriptionAttribute), true);
    }
    catch (Exception ex)
    {
        // log it
    }

    if (skinNames.Length == 1 && skinDescriptions.Length == 1)
    {
        _skinFriendlyName = skinNames[0].AssemblySkinName;
        _skinDescription = skinDescriptions[0].AssemblySkinDescription;
        isSkin = true;
    }
    return isSkin;
}

So, once this DLL evaluation is complete and all the skins have been loaded, we have a set of skins that are available for use with our application. As you can remember from the settings management section above, the ISkinManager is used as the DataContext of the combo box in the settings view. That combo box has its ItemSource property bound to the Skins property of the SkinManager. The SkinManager also inherits from DependencyObject as the CurrentSkin property is a DependencyProperty. I should state here that the actual skin loading mechanism is not my own. It was provided in another CodeProject article by Tomer Shamam. You can read his article and see the original implementation in the code provided by Tomer here, it really is excellent work. The complete implementation of this DependencyProperty is this:

public Skin CurrentSkin
{
    get { return (Skin)GetValue(CurrentSkinProperty); }
    set { SetValue(CurrentSkinProperty, value); }
}
    
public static readonly DependencyProperty CurrentSkinProperty =
                   DependencyProperty.Register(
                   "CurrentSkin",
                   typeof(Skin),
                   typeof(SkinManager),
                   new UIPropertyMetadata(Skin.Null, OnCurrentSkinChanged, 
                                          OnCoerceSkinValue));
                   
private static object OnCoerceSkinValue(DependencyObject d, object baseValue)
{
    if (baseValue == null)
    {
        return Skin.Null;
    }
    return baseValue;
}

private static void OnCurrentSkinChanged(DependencyObject d, 
                    DependencyPropertyChangedEventArgs e)
{
    try
    {
        Skin oldSkin = e.OldValue as Skin;
        oldSkin.Unload();
        Skin newSkin = e.NewValue as Skin;
        newSkin.Load();
    }
    catch (SkinException ex)
    {
        // log it 
    }
}

WPF themes can be huge gluts of XAML code spread over many, many files inside a DLL. On one commercial application I have worked on, the theme DLL has literally thousands and thousands of lines of XAML dedicated to styling the application. It is something that needs lots of organisation to make maintaining it as easy as possible. With this application demo, I have created two simple themes, one is the 'Default' theme and one is a 'Blue' theme. The only real differences between these two DLLs is the colours. Obviously, the idea of themeing an application can go to the extreme where the application is rendered completely differently depending on the theme selected.

I have organised the XAML files based on the control / element they target. There is also a XAML file containing the lions share of the most significant colours used within each theme to make it easy to reassign these by simply visiting one resource file. The files included inside the theme DLLs are:

  • ButtonStyle.xaml
  • Colours.xaml
  • ComboBoxStyle.xaml
  • ListBoxStyle.xaml
  • ScrollBarStyle.xaml
  • ShutDownButtonStyle.xaml
  • StatusBarStyle.xaml
  • TextBoxStyle.xaml
  • ToolBarStackPanelStyle.xaml
  • WindowStyle.xaml

In the XAML shown below, there are a couple of examples of the kinds of colours that you may want to treat as significant in terms of making the application look and feel consistent; also included is a small section taken from the TextBox style showing the use of one of these significant colours:

<!-- The main application Background Colour -->
    <SolidColorBrush x:Key="AppBackground" Color="#FF434343"/>
    
    <!-- Application Default Text Colour -->
    <SolidColorBrush x:Key="DefaultText" Color="#FFFFE883"/>

    <Style x:Key="JamSoftTextBoxStyle" TargetType="{x:Type TextBox}">
        <Setter Property="BorderBrush" Value="#FFA5ACB2"/>
        <Setter Property="Foreground" Value="{DynamicResource DefaultText}"/>
        <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>

You can see these two themes in action in the two screenshots below. You'll notice that this application makes use of the pretty cool border less windows that WPF makes so easy to apply. One little trick include in order to make the window draggable is actually pretty simple:

In the shell window, we make use of MouseLeftButtonDown:

<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"
    AllowsTransparency="True"
    WindowStyle="None" 
    MouseLeftButtonDown="Window_MouseLeftButtonDown"
    WindowStartupLocation="CenterScreen"
    Width="800"
    Height="600"
    Style="{DynamicResource JamSoftWindowStyle}" >

Then, in the code-behind for the shell window, we have:

    private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    this.DragMove();
}

So, we now have a nice skinnable windowless application that we can also drag around like a normal bordered window. We can also drop in new skin DLLs and have them made available to the user without having to recompile anything in order to do so. Pretty neat stuff.

BlueTheme.jpg

DefaultTheme.jpg

Dynamic ToolBars

Another feature of this version of the demo app also allows modules to dynamically add controls to the toolbar when its view is visible in the main region of the application. Again, this isn't code destined for your production app, but it serves to illustrate how this could be done. In order to power this, an additional property has been added to the original IPage interface contained in JamSoft.CALDemo.PageManager.Core. This interface now looks like:

public interface IPage
{
    string ID { get; }
    float Position { get; }
    object View { get; }
    bool IsActivePage { set; }
}

The new property is a boolean value called IsActivePage. Pretty self explanatory, really. So, how do we make use of this and perform the required tasks? How do we get notified that a page is either active of inactive? We use the event aggregator! We already have an event being fired that provides this; all we have to do is ensure that any page that needs to react to being activated or de-activated subscribes to this event and performs the relevant tasks. The only module that actually performs this kind of dynamic behaviour is the MusicSearch module. So, in MusicSearchPresenter, we subscribe to the PageSelectedEvent like this:

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

Now, because the model for the music search page is a pretty big one, that has been separated out into a model in its own right. That model has two methods on it that activate and deactivate it when the page is selected, so our OnPageSelected handler looks like this:

private void OnPageSelected(IPage page)
{
    if (page == this)
    {
        _model.ActivateModel();
    }
    else
    {
        _model.DeactiveModel();
    }
}

Now, we have a page that can be informed that it is the active page and can perform tasks when it is shown in the main region or removed from the main region. In our particular situation, when the model is activated, we create a button bound to a DelegateCommand that resets the music search view ready for a new artist search process. We use the reference to the IToolBarPresentationModel to register a control in the toolbar. The ToolBarPresentationModel contains two methods for providing this functionality that look like this:

public void AddToolBarItem(Control control)
{
    if (!_view.DynamicToolPanel.Children.Contains(control))
    {
        _registeredToolControls.Add(control);
        _view.DynamicToolPanel.Children.Insert(0, control);
        NotifyPropertyChanged("RegisteredToolControls");
    }
}

public void RemoveToolBarItem(Control control)
{
    if (_view.DynamicToolPanel.Children.Contains(control))
    {
        _registeredToolControls.Remove(control);
        _view.DynamicToolPanel.Children.Remove(control);
        NotifyPropertyChanged("RegisteredToolControls");
    }
}

That is pretty much it for the tool bars. It's a ridiculously simple implementation, and would require a lot of work before making it anywhere near a production application, but it does nicely illustrate how things can be done using CAL.

CAL Interfaces

I've been wondering about this topic since I started writing the code for the demo application supplied with Part 1. It's a really tricky thing to put into black and white print. I have seen people get the use of interfaces in a CAL application really wrong, and wanted to try and include something here that might help clear it up for some people.

One of the main problems I've seen is through the definition of the CAL interfaces as if they were 'normal' interfaces. If you were defining a custom collection, you might implement the IList interface, or in a resource intensive situation, you might implement IDisposable on an object to ensure that clean-up happens. The key point is that these interfaces are designed for reuse, as shown in the following diagram:

PolymorphicInterfaces.jpg

So here, we have IMyNormalInterface that is implemented by MyObjectA, MyObjectB, and MyObjectC. We then have a collection defined that can host any of these objects because they each implement the IMyNormalInterface. This kind of behaviour is commonly known as polymorphism. In contrast to this, the kind of interfaces that we create for CAL are not intended for this kind of behaviour. They are generally 'paired-up' with a single type, and registered as a pair into the container for later resolution from the container. Consider this example:

public interface IPresenterA
{
    void DoSomething();
}

public class PresenterA : IPresenterA
{
    public MyPresenter()
    {
    }
    
    public void DoSomething()
    {
        // does something ...
    }
}

This would be registered into the container like:

_container.RegisterType<IPresenterA, PresenterA>(new ContainerControlledLifetimeManager());

The IPresenterA is only ever going to be implemented by the concrete type PresenterA, so even though we are using an interface, the reason for it is not to allow its use in a polymorphic way, nor is it there to ensure that the PresenterA adheres to a specific set of functionality. That said, if our concrete type neglects to implement something on the interface, we will obviously still get a compiler error, but this isn't the primary driving force behind the interface usage. The primary use of IPresenterA in the CAL context is to expose the internals of PresenterA to the rest of the world in a loosely coupled fashion. Here is another diagram to help illustrate this point:

CALInterfaces.jpg

So in this diagram, we are showing two presenters registered in the container, the second IPresenterB requests a reference to IPresenterA as it needs to be able to invoke the DoSometing() method that is defined on the IPresenterA interface and implemented by the PresenterA type.

If the developer designs a CAL interface in the same way a normal interface is designed, you could end up exposing everything a type is capable of without a need or real desire to do so. You could also run the risk of compromising a strict communication methodology by exposing a method that is also a handler for an EventAggregator event among other things.

Wrap Up

I think that pretty much wraps it up for this demo application and article series. I hope people find it useful and get some ideas from this for their own applications. Thanks for reading.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here