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

MVVM made easy with Calcium - Part 1

0.00/5 (No votes)
18 Apr 2010 1  
Learn how to create a simple MVVM pattern based application using the Calcium SDK.


CalciumSample

Contents



Introduction

If you are a WPF or Silverlight developer, you would have heard the buzz around the MVVM pattern. In a nutshell it is a pattern that helps to separate the view layer from the rest of your application. Developing a WPF or Silverlight applications can require some thought about how best to apply this pattern. In some circumstances you may want to write your own framework, but in most cases the best choice may be to utilise some of the existing open source MVVM frameworks. There is an abundance of these frameworks, and you can check out Jeremy Alles’s overview of them on his blog. Each framework has its pros and cons, and each is suited to different situations. One of the frameworks is Calcium, which is built on top of the Composite Application Library (also known as PRISM). PRISM does not explicitly provide support for the MVVM pattern, as its main purpose is to assist in building modular applications. Calcium, however, compliments the infrastructure provided by PRISM to include such things as an advanced module management, client-server logging, theme support, an Undo/Redo/Repeat task management system, and also abstraction of user messaging and file management. So if you are building modular WPF or Silverlight applications, maybe you have already discovered Calcium. Calcium is truly a gem (I may be biased here, but I know the mastermind behind it and how much effort has been going into it). Because Calcium offers a lot of infrastructure and may be perceived as being complex, there is a call for a simple guide on how to configure and customize a Calcium project. That is the purpose of this article. I will provide this guide in a FAQ like format, and demonstrate the points explained in this FAQ in the attached demo project. This article is a work in progress as Calcium is an active project and is constantly advancing. In fact because there is a lot to cover, I have split the FAQ into two articles. The first, this article, will focus mostly on how to get started with Calcium and how to customize and rebrand Calcium projects. The second article will focus on the other features implemented in the sample application. In addition to this FAQ, there are 3 detailed articles, written by Daniel, describing the Calcium framework, two articles describing the Undo/Redo/Repeat task management framework, and a getting started video.

The demo

The demo application accompanying this article is a Calcium sample project demonstrating the customization, rebranding, module development, and modal dialog support explained in this article, as well as other features such as File Service which will be covered in Part 2. The demo is a simple image designer with very little functionality, but which allows users to upload images from their local drives to the tools region of the Calcium shell. When a user clicks on an image, the selected image will be displayed in the Designer workspace. Users can then use the Designer’s Toolbar to zoom in and out of the image. Users can also use the File menu to open another Image Designer.

What you need

  • Calcium SDK - See Q1 of this FAQ how to install Calcium SDK
  • VS2008 or VS2010 - Calcium supports VS2008 and VS2010. The demo application was developed in VS2010.

Q1. How do I install Calcium SDK?

Calcium is hosted on codeplex where you have the option to either download the release version or the source version. The source version is regularly updated and thus is the most up to date version.

Installing the Release Version

Extract the content of the zip file into your chosen directory. Based on your environment, open either the VS2008 or VS2010 folder, then launch the setup.exe file to install Calcium SDK. A wizard will guide you through the installation process.

Installing from Source Code

Extract the content of the zip file into your chosen directory and navigate to the Calcium solution (Source->Calcium->Calcium). For desktop CLR open the CalciumDesktopClr solution, for Silverlight open the CalciumSilverlightClr solution (note that CalciumSilverlitghtClr installer has not yet been implemented). Change your solution configuration to Release and Rebuild the solution. Within the solution locate the Visual Studio Integration project and based on your environment right click either on Calcium.VSIntegration.VS09Setup or Calcium.VSIntegration.VS10Setup project and select the Install option as shown in Figure 1. Follow the installation wizard to install Calcium SDK.

There is no need to uninstall previous versions of Calcium, unless you wish to install an earlier version.

Figure 1: Installing Calcium

Note: When you release build the solution you may encounter this error: “The command "IF NOT Release == Release GOTO end echo Zipping templates ... If the script fails in powershell it could be script permissions. In a powershell prompt try: set-executionpolicy unrestricted...” If you are not modifying or customizing the templates then it is safe to remove the pre-build event of the Calcium.VSIntegration project that executes the Powershell script.

Q2. What does the installer do?

The installer does four things:

  • Copies the built assemblies to the installation directory (the path specified).
    • Assemblies are not copied into GAC because a developer working with the source code version (although this may be rare) would need to update the version numbers of all Calcium assemblies. This is because at run time the CLR looks to the GAC first when resolving assembly references, even when you are debugging an application.
  • Places an assembly, required by the VS Templates, into the GAC.
  • Sets a registry string with the location of the installation.
  • Copies the zipped templates to a directory.

Q3. How can I upgrade to the latest version?

Because work on Calcium is very active, quite often you will want to re-install the Calcium SDK. If you are using the source version, and wish to re-install Calcium after getting the latest version, in order for the installer to update the assemblies in the installation directory you need to ensure that the file version of the assemblies has been changed. This is the file version and not the assembly version. The windows installer ignores the assembly version and only installs a new version of an assembly if it has a different file version! The Calcium SDK solution contains T4 templates which serve to update the file version of all assemblies produced. If you modify the source version before rebuilding the installers as release build, be sure to click on the Transform All Templates button (shown in Figure 2), because this will make sure that when you re-install the latest Calcium SDK the latest assemblies will be copied to the application directory. After the installation completes, do a clean build of your existing project and your project will automatically use the latest assemblies.

Figure 2: Transform All Templates

If you are getting the latest version frequently using the source control providers on Codeplex, then the best approach is to reference assemblies in the release build of the Calcium projects that are located in the release directory of the source version of the Calcium solution. Get the latest version from source control using whatever client you use, and build as release. Then reference those assemblies in your own project to those in the bin directory of the various projects in the source. For example, the DanielVaughan assembly is under Core (Calcium\Source\Core\Core\bin\Release\DanielVaughan.dll), the DanielVaughan.Calcium.Client assembly is under Calcium.Client, (Calcium\Source\Calcium\Calcium\Calcium.Client\bin\Release\DanielVaughan.Calcium.Client.dll), Logging assembly under Clog (Calcium\Source\Clog\bin\Release\DanielVaughan.Logging.dll) etc. This way you will not need to use the installer every time you get the latest version. This will also allow you to debug the Calcium assemblies, because compiling them on your own machine means that the source assemblies and line number information will be available to the debugger. Also, by referencing the assemblies and not the projects you will probably find that your solution feels lighter, and it may allow you to work more efficiently.

Q4. How can I create a Calcium application?

Open Visual Studio and navigate to create a New Project. From the Windows category, select Calcium Project, specify a name and click OK to create a new Calcium application. If you have just installed the source version of Calcium SDK and cannot see the Calcium project template, try relaunching Visual Studio. When your new Calcium application starts, you will see it already populated with several default modules: a text editor, module manager, a web browser, and an output window. See Q7 of this FAQ for instructions how to prevent these modules from loading. For more information about creating Calcium application, watch the Calcium Getting Started Video.

Q5. How can I extend my Calcium application?

Calcium is based on PRISM which provides infrastructure for building modular applications. Thus the main way that we extend a Calcium application is to create modules. Modules can be grouped together in a project, or placed in individual projects. To create a new Calcium Module, right click on the solution and select Add -> New Project. From the Windows category select Calcium Module, rename it and click ok to create a new Calcium Module. See Figure 3. You can create as many Modules, and as many Views within each Module as you like. The sample application consists of one Module (ImageDesigner) with two Views (ImageList and ImageSelector).

Figure 3: Adding Calcium Module

Each Calcium Module comes preset with a Module, View, ViewModel, and a T4 template. The Module creates and displays Views during initialization and in response to events. Views are the containers where we can place controls that are data-bound to a ViewModel. The ViewModel contains the layout and business logic for the View. The ViewModel doesn’t know about the View. When you create a new Calcium Module, you are given naming guidance for the Module name: YourNamespace.ModuleName. This is a naming convention which is used for the automatic naming of the various generated items. For example, the Module in the demo project is called CalciumSample.ImageDesigner, and the generated items, Module, View and ViewModel were automatically named CalciumSample.ImageDesignerModule, CalciumSample.ImageDesignerView, and CalciumSample.ImageDesignerViewModel respectively.

By default, a Module’s assembly needs to be present in the bin directory of the main application. Therefore in order to enable the loading of your new module whenever your application starts, you must either add a reference to your new module project or add a post build event to copy the assembly to the bin directory. Your module will then be automatically displayed in the View menu of the application.

Add a post build event to copy the assembly to the bin directory

Right click on your module project, and go to properties. Open the Build Events tab and as shown in Figure 4, place the following code to the post-build event command line text box:

COPY "$(TargetPath)" "$(SolutionDir)ModuleProjectName\bin\$(ConfigurationName)\

Substitute the ModuleProjectName with the name of your launcher project.

Figure 4: Adding a post-build event to push the build assembly to the bin directory of your application.

The benefit of using the post-build event to push the built assembly to the bin directory of your application is that you do not have a dependency between your main project and your module project. So the module is independent. The down side is that if you plan on using ClickOnce deployment you may find that this approach doesn’t work very well, because the dependency on the module assembly will not be detected.

Q6. What is the best way to add a View to an existing project?

Calcium comes with a View template. To add a View to your Module simply right click on the Module project and select Add -> New Item and from the WPF category select Calcium View as shown in Figure 5. A new Calcium View and a ViewModel will be generated for you. In the demo application we created a new View called ImageList. In the solution you can see the automatic naming for the new View (CalciumSample.ImageListView) and ViewModel (CalciumSample.ImageListViewModel).

Figure 5: Adding a new Calcium View.

Q7. Does Calcium provide a Web Application template?

Yes, Calcium provides a web application template. In order to have some of the communication services and client server logging, you may want to create a Calcium Web Application. To create a new Calcium Web Application, right click on the solution and select Add -> New Project. Under the Web category select Calcium Web Application, give it a name and click ok. See Figure 6. The demo application does not utilise the Calcium Web Application.

Figure 6: Adding Calcium Web Application.

Q8. How can I exclude default Modules?

Calcium project already comes with several modules out of the box. Any or all default modules can be prevented from loading via the AppStarter. The AppStarter displays the splash screen, loads resources, runs the Bootstrapper, and displays the shell or main window.

In your project, open App.xaml.cs (for your information, in the source, the App.xaml.cs is located under the Calcium.Client.Launcher project). In the Application_Startup method use the ExcludedModules list to exclude any default module as shown below:

/* To exclude default modules use the ExcludedModules list. */        
var starter = new AppStarter();
starter.StartupOptions.ModuleCatalogOptions.ExcludedModules.Add(ModuleNames.TextEditor);
starter.Start();

If you wish to remove all off the default modules (OutputDisplay, ModuleManager, Communication, History, and WebBrowser) in one step, you can add the DefaultModuleNames list to the ExcluededModules like so:

starter.StartupOptions.ModuleCatalogOptions.ExcludedModules.AddRange(ModuleNames.DefaultModuleNames);

It is not however recommended to remove the ModuleManager unless you provide your own custom module management.

Modules such as the DiagramDesigner can be removed by removing the module reference from your project. For example, to remove the DiagramDesigner Module, simply delete the DanielVaughan.Calcium.DiagramDesigner reference from your project.

Figure 7: Removing the DiagramDesigner reference from the project.

You can also use the Module Manager for enabling or disabling of modules at runtime.

Q9. How can I change the Calcium default Theme?

Calcium comes with two themes, Expression Dark (default) and Bureau Blue. In the Calcium project themes can be changed in App.config. To change the default theme to the Bureau Blue Theme, simply change the Default theme to Bureau Blue as the following code shows:

<!-- Themes: "Default", "Bureau Blue" -->
<add key="Theme" value="Bureau Blue"/>

You can also use the Module Manager to activate/disable the Bureau Blue Theme at runtime.

Q10. Can I define my own theme?

The styles and the entire ControlTemplate for the Shell can be customized. There is a IThemeService that can be leveraged to register a theme at runtime. The new default theme may serve as an example of how to create a custom theme.

Q11. How can I rebrand Calcium?

A Calcium project comes out of the box with Calcium branded splash screen, application icon, title text, about box, and a title banner with a logo. All of these can be easily removed or rebranded to suit your application.

Splash screen

The splash screen can be customized via the AppStarter or by registering a Window that implements ISplashScreen.

Customizing splash screen via AppStarter

Open App.xaml.cs (for your information, in the source, the App.xaml.cs is located under the Calcium.Client.Launcher project). In the Application_Startup method use the StartupOptions as shown below:

var starter = new AppStarter();
/* To customize the splash screen image use the StartupOptions as shown below. */
starter.StartupOptions.SplashImagePackUri
        = new Uri("pack://application:,,,/Assembly;component/YourImage.gif");

Replace your Assembly and YourImage.jpg in the above code with your own. You will find your assembly name under properties of your project (in solution explorer, right click on the project and go to Properties. Under the Application tab you will find the Assembly Name). The above code assumes the YourImage.jpg file is located at the root of the project.

Or if you wish to use the metadata generation to avoid magic strings, then you can use:

starter.StartupOptions.SplashImagePackUri 
	= new Uri(Metadata.YourImageJpg. RelativePackUri);

For more information on how to utilise T4 templates see Q15 of this FAQ.

At the time of writing, the SplashScreenView is being refactored to allow for overriding the Splash Screen template.

The Application Icon

The application icon can be customized by overriding the default ShellView style. In your Calcium project, paste the following code within the <Application.Resources> element of your App.xaml.

<BooleanToVisibilityConverter x:Key="booleanToVisibilityConverter" />
		
<Style TargetType="{x:Type Gui:ShellView}">
    <Setter Property="Icon" Value="..\Icons\icon.ico" />
    <Setter Property="Title" Value="{Binding Title}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Gui:ShellView}">
                <Grid x:Name="PART_Grid_Root" 
                    Background="{DynamicResource WindowBackgroundBrush}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition x:Name="PART_ColumnDefinition_Left" 
                                MinWidth="80"/>
                        <ColumnDefinition x:Name="PART_ColumnDefinition_Center"
                                Width="2.5*"  MinWidth="80" />
                        <ColumnDefinition x:Name="PART_ColumnDefinition_Right"
                                MinWidth="80" />
                    </Grid.ColumnDefinitions>

                    <Grid.RowDefinitions>
                        <RowDefinition x:Name="PART_RowDefinition_Banner" 
                                Height="Auto"/>
                        <RowDefinition x:Name="PART_RowDefinition_Menu" 
                                Height="Auto"/>
                        <RowDefinition x:Name="PART_RowDefinition_Content" 
                                Height="2*" MinHeight="80" />
                        <RowDefinition x:Name="PART_RowDefinition_OutputDisplay"
                                MinHeight="80"/>
                        <RowDefinition x:Name="PART_RowDefinition_StatusBar" 
                                Height="Auto" />
                    </Grid.RowDefinitions>

                    <!-- Banner -->
                    <Border x:Name="PART_Border_Banner" Grid.Row="0" 
                            Grid.ColumnSpan="3" Margin="10, 0, 0, 0">
                        <Expander x:Name="PART_Expander_Banner" IsExpanded="True" 
                                ExpandDirection="Down"
                                HorizontalAlignment="Stretch" 
                                VerticalAlignment="Stretch" MinHeight="6"
                                Style="{DynamicResource ExpanderStyle}" 
                                Visibility="{Binding BannerVisible, 
                                Converter={StaticResource booleanToVisibilityConverter}}">
                            <StackPanel x:Name="PART_StackPanel_TitleBanner" 
                                    cal:RegionManager.RegionName
                                    ="{x:Static Calcium:RegionNames.Banner}"
                                    Orientation="Horizontal">
                                <Controls:ShellBanner x:Name="PART_ShellBanner" 
                                    HorizontalAlignment="Stretch" Padding="0, 0, 10, 5"
                                    Visibility="{Binding LogoVisible, 
                                    Converter
                                    ={StaticResource booleanToVisibilityConverter}}"/>
                            </StackPanel>
                        </Expander>
                    </Border>

                    <!-- Menu -->
                    <ContentControl x:Name="PART_ContentControl_Menu" Grid.Row="1" 
                                Grid.ColumnSpan="3" 
                                Margin="7,0,0,5" Padding="5" >
                        <StackPanel x:Name="PART_StackPanel_Menu">
                            <Controls:StandardMenu x:Name="PART_Menu" 
                                Margin="0, 0, 0, 0"  />
                            <!--<Controls:StandardToolBarTray x:Name="PART_ToolBar" 
                                Margin="0, 5, 0, 0"/>-->

                            <Border x:Name="Menu_Border" Width="Auto">
                                <ToolBarTray cal:RegionManager.RegionName
                                        ="{x:Static Calcium:RegionNames.StandardToolBarTray}">
                                    <ToolBar x:Name="ToolBar_Standard">
                                        <Button Command="ApplicationCommands.Save" 
                                                ToolTip="{Binding RelativeSource
                                                    ={RelativeSource Self}, 
                                                Path=Command.Text}">
                                            <Image Width="15" Height="15" 
                                            Source="{x:Static m:SavePng.Source}"  />
                                        </Button>
                                        <Button Command="{x:Static Gui:ShellView.SaveAll}" 
                                                ToolTip="{Binding RelativeSource
                                                    ={RelativeSource Self}, 
                                                Path=Command.Text}">
                                            <Image Width="15" Height="15" 
                                            Source="{x:Static m:SaveAllPng.Source}" />
                                        </Button>
                                    </ToolBar>
                                </ToolBarTray>
                            </Border>
                        </StackPanel>
                    </ContentControl>

                    <!-- Workspace -->
                    <Border x:Name="PART_Border_Workspace" 
                        Grid.Row="2" Grid.Column="1">
                        <TabControl x:Name="PART_TabControl_Workspace"
                        cal:RegionManager.RegionName
                            ="{x:Static Calcium:RegionNames.Workspace}"
                        IsSynchronizedWithCurrentItem="True"  
                        Width="Auto" Height="Auto" 
                        HorizontalAlignment="Stretch" 
                        VerticalAlignment="Stretch" 
                        Margin="0,0,0,0"/>
                    </Border>

                    <!-- Tools Left -->
                    <Border x:Name="PART_Border_Left" Grid.Row="2" Padding="0,0,1,10" 
                            Grid.RowSpan="2">
                        <Expander x:Name="PART_Expander_Left" IsExpanded="True" 
                                ExpandDirection="Left"        		 
                                Style="{DynamicResource ExpanderStyle}"
                                BorderThickness="0,0,0,0" Margin="0,0,0,0">
                            <TabControl x:Name="PART_TabControl_Left" 
                                    cal:RegionManager.RegionName
                                    ="{x:Static Calcium:RegionNames.Tools}"
                                    MinWidth="0" Margin="10,0,1,0"
                                    HorizontalAlignment="Stretch" 
                                    VerticalAlignment="Stretch"
                                    IsSynchronizedWithCurrentItem="True" 
                                    BorderThickness="0,0,0,0"/>
                        </Expander>
                    </Border>

                    <!-- ModuleManager etc., Right -->
                    <Border x:Name="PART_Border_Right" Grid.Row="2" 
                            Grid.Column="2" Grid.RowSpan="2"
                            HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                            Padding="1,0,0,10" Margin="0,0,0,0">
                        <Expander x:Name="PART_Expander_Right" 
                                IsExpanded="True" ExpandDirection="Right"
                                Style="{DynamicResource ExpanderStyle}"
                                BorderThickness="0,0,0,0">
                            <TabControl x:Name="PART_TabControl_Right" 
                                cal:RegionManager.RegionName
                                ="{x:Static Calcium:RegionNames.Properties}"
                                MinWidth="0" Margin="1,0,10,0"
                                HorizontalAlignment="Stretch" 
                                VerticalAlignment="Stretch"
                                IsSynchronizedWithCurrentItem="True" 
                                BorderThickness="0,0,0,0" 
                                Background="{x:Null}" Foreground="{x:Null}" 
                                BorderBrush="{x:Null}"/>
                        </Expander>
                    </Border>

                    <!-- OutputDisplay etc. -->
                    <Border x:Name="PART_Border_Bottom" 
                            Grid.Row="3" Grid.Column="1" 
                            HorizontalAlignment="Stretch"
                            VerticalAlignment="Stretch" 
                            Margin="0,0,0,0">
                        <Expander x:Name="PART_Expander_Bottom" IsExpanded="True"
                              MinHeight="6" Style="{DynamicResource ExpanderStyle}"
                              BorderThickness="0,0,0,0" 
                              Margin="0,0,0,0" VerticalAlignment="Stretch" 
                              HorizontalAlignment="Stretch">
                            <TabControl x:Name="PART_TabControl_Bottom" 
                                    cal:RegionManager.RegionName
                                    ="{x:Static Calcium:RegionNames.Footer}"
                                    MinHeight="80" 
                                    HorizontalAlignment="Stretch" 
                                    VerticalAlignment="Stretch"
                                    IsSynchronizedWithCurrentItem="True" 
                                    Margin="0,1,0,10" />
                        </Expander>
                    </Border>

                    <StackPanel x:Name="PART_StackPanel_StatusBar" 
                            Grid.Row="4" Grid.ColumnSpan="3"
                            cal:RegionManager.RegionName
                            ="{x:Static Calcium:RegionNames.StatusBar}"
                            Orientation="Horizontal" Margin="0,-10,0,0">
                    </StackPanel>

                    <!-- GridSplitters -->
                    <GridSplitter x:Name="PART_GridSplitter_Left" 
                              Grid.Column="0" Grid.Row="2" Grid.RowSpan="2" 
                              HorizontalAlignment="Right"
                              VerticalAlignment="Stretch" Width="4"
                              Opacity="0"  Margin="0,20,-4,6" />

                    <GridSplitter x:Name="PART_GridSplitter_Bottom" 
                              Grid.Column="1" Grid.Row="3" Visibility="Visible" 
                              HorizontalAlignment="Stretch" 
                              VerticalAlignment="Top" Height="4" 
                              Opacity="0" Grid.ColumnSpan="1" 
                              Margin="0,-4,0,0" />

                    <GridSplitter x:Name="PART_GridSplitter_Right"
                              Grid.Column="2" Grid.Row="2" Visibility="Visible" 
                              HorizontalAlignment="Left" 
                              VerticalAlignment="Stretch" Width="4"  
                              Opacity="0" Margin="-4,20,0,6" 
                              Grid.RowSpan="2" />

                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Add the following namespace references to the top of your App.xaml or let a tool like Resharper add these for you.

xmlns:Gui="clr-namespace:DanielVaughan.Calcium.Gui;assembly=DanielVaughan.Calcium.Client"
xmlns:cal="clr-namespace:Microsoft.Practices.Composite.Presentation.Regions;assembly
    =Microsoft.Practices.Composite.Presentation"
xmlns:Calcium="clr-namespace:DanielVaughan.Calcium;assembly=DanielVaughan.Calcium.Client"
xmlns:Controls="clr-namespace:DanielVaughan.Calcium.Gui.Controls;assembly
    =DanielVaughan.Calcium.Client"

Note that you can override the style for any of the Shell View type here. To ensure you always use the most up to date template, download the latest source and copy the above code from the WindowDictionary.xaml file located under Desktop CLR Client -> Calcium.Client project under Themes.

To change the application icon, replace the Icon value as shown below:

<Setter Property="Icon" Value="..\..\Icon.ico" />

Application Title

The application title can be customized either by overriding the style (as done above with the icon), or it can be done by retrieving the IShell instance from the service locator, and setting the Title in the Application_Startup as shown below:

var shell = ServiceLocatorSingleton.Instance.GetInstance<IShell>();
shell.Title = "Calcium Sample";

Setting the title in the Application_Startup may not be ideal as it is at the moment because it occurs after the window loads, but in the near future there will be a way of creating a command which will be executed before the window is shown.

Title Banner with Logo

The Shell Banner can be customized by overriding the default Shell Banner style. Copy the following code within the application resources of your App.xaml and modify it to suit your needs.

<!--Override the shell banner style-->
<Style TargetType="{x:Type Controls:ShellBanner}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Controls:ShellBanner}">
                <Image x:Name="PART_Image_Banner" Source="logo.png" 
                Stretch="None" HorizontalAlignment="Left"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

If you wish to hide the banner or the banner logo you can retrieve the IShell instance from the service locator, and set the LogoVisible and BannerVisible properties to false as shown in the following excerpt:

var shell = ServiceLocatorSingleton.Instance.GetInstance<IShell>();
shell.BannerVisible = false;
shell.LogoVisible = false;

About Box

The default AboutBox can be replaced by registering with the ServiceLocatorSingleton an IAboutBox type association. This can be done within the project or in a module. Make sure that your AboutBox implements the IAboutBox interface. In the Project, it can be done in App.xaml.cs in the Application_Startup method as shown below:

var container = new UnityContainer();
ServiceLocatorSingleton.Instance.InitializeServiceLocator(container);
ServiceLocatorSingleton.Instance.RegisterType<IAboutBox, AboutBox>();

In the module you can register the AboutBox with the ServiceLocatorSingleton during the module initialization, as shown below:

ServiceLocatorSingleton.Instance.RegisterType<IAboutBox, AboutBox>(); 

Registering the AboutBox in the module is recommended because it doesn’t require replacing the container.

Thirdly, it can be achieved by registering your IAboutBox implementation in the unity configuration App.config:

<unity>
    <typeAliases>
    </typeAliases>
    <containers>
        <container>
            <types>
                <type type=" DanielVaughan.Calcium.Gui.Windows.IAboutBox, 
                    DanielVaughan.Calcium.Client" mapTo= "
                    YourNamespace.YourAboutBox, YourAssembly" />
            </types>
            <instances>
            </instances>
        </container>
    </containers>
</unity>

Q12. How can I customize the standard menu and standard toolbar?

The StandardMenu and StandardToolBarTray controls play host to a number of regions, which can be used to add module specific Menus, MenuItems, and ToolBars.

Standard Menu

You can add menu items to the standard menus or you can create your own custom menus and add them to the standard menu.

Custom Menu Items

New menu items can be added in the module class during the module initialization like so:

ShowView(MenuNames.File, new MenuItem() {Header = "Menu Item"});

Another option is to create a custom menu control. The following example shows a simple menu control with a separator which is used in the demo.

<UserControl x:Class="CalciumSample.ImageDesigner.ImageDesignerMenu"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Menu x:Name="CustomMenu">
            <MenuItem Header="Designer">
                <MenuItem Header="Menu Item1"/>
                <Separator />
                <MenuItem Header="Menu Item2"/>
            </MenuItem>
        </Menu>
    </Grid>
</UserControl>

Using this approach give you designer support. In the code behind of the custom menu control we need to detach the menu items from the menu as shown below:

public IEnumerable<FrameworkElement> RetrieveAndDetachItems()
{
    var list = new List<FrameworkElement>();
    foreach (var item in CustomMenu.Items)
    {
        list.Add((FrameworkElement)item);
    }
    foreach (var item in list)
    {
    CustomMenu.Items.Remove(item);
    }
return list;
}

Finally, we populate the the menu region with the new menu items as shown below. We do this in the module class during module initialization:

            
/* Add custom menu items to the standard menu under File menu. */
CustomMenuItems customMenuItems = new CustomMenuItems();
var menuItems = customMenuItems.RetrieveAndDetachItems();
foreach (var frameworkElement in menuItems)
{
    ShowView(MenuNames.File, frameworkElement);
}

As you can see in the above code, we instantiate the new menu control, retreive a list of menu items and add each menu item to the File menu region. Currently the menu items are placed at the end of the menu, which for the File menu may not be desirable. A future release may have the capability for menu item ordering.

Custom Menu

You can also create a custom menu with a root menu as the following excerpt shows:

<UserControl x:Class="CalciumSample.ImageDesigner.CustomMenuItems"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Menu x:Name="CustomMenu">
            <MenuItem Header="Menu Item1"/>
            <Separator />
            <MenuItem Header="Menu Item2"/>
        </Menu>
    </Grid>
</UserControl>

Again, we need to detach the menu items from the menu in the code behind of the custom menu control like so:

public IEnumerable<FrameworkElement> RetrieveAndDetachItems()
{
    var list = new List<FrameworkElement>();
    foreach (var item in CustomMenu.Items)
    {
        list.Add((FrameworkElement)item);
    }
    foreach (var item in list)
    {
        CustomMenu.Items.Remove(item);
    }
    return list;
}

Finally we place the custom menu to the MainMenu region during module initialisation in the module class as shown below:

        
var viewService = ServiceLocatorSingleton.Instance.GetInstance();
/* Add custom menu. */
ImageDesignerMenu imageDesignerMenu = new ImageDesignerMenu();
var imageDesignerMenuItems = imageDesignerMenu.RetrieveAndDetachItems();
foreach (var frameworkElement in imageDesignerMenuItems)
{
    ShowView(MenuNames.MainMenu, frameworkElement);

    /*Show only when module is active*/
    viewService.AssociateVisibility(typeof(ImageDesignerView),
    new UIElementAdapter(frameworkElement), Visibility.Collapsed);
}

If you want to have the menu shown only when the module is present in the workspace region then you can register it with a view service as shown above.

Standard Toolbar

You can create a custom toolbar for any of your modules or you can customize the standard toolbar.

Custom Toolbar

The below example shows a simple toolbar control with two icons used in the demo application.

<UserControl x:Class="CalciumSample.ImageDesigner.ImageDesignerToolBar"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:m="clr-namespace:CalciumSample.ImageDesigner.FileMetadata.Icons.Metadata" 
     xmlns:ImageDesigner="clr-namespace:CalciumSample.ImageDesigner" 
        mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
    <Grid x:Name="LayoutRoot">
        <ToolBar  Name="toolBar_Main">
            <Button x:Name="Button_ZoomIn"ToolTip="Zoom in" 
                Command="{x:Static ImageDesigner:ImageDesignerViewModel.ZoomInCommand}">
                <Image Source="{x:Static m:ZoomInPng.Source}" Width="16" Height="16" />
            </Button>
            <Button x:Name="Button_ZoomOut" ToolTip="Zoom out" 
                Command="{x:Static ImageDesigner:ImageDesignerViewModel.ZoomOutCommand}">
                <Image Source="{x:Static m:ZoomOutPng.Source}" Width="16" Height="16" />
            </Button>
        </ToolBar>
    </Grid>
</UserControl>

(The sample icons were downloaded from here)

Because the ToolBar cannot be assigned to another parent container we define a property in the user control which detaches the ToolBar like so:

        
/// <summary>
/// Gets the tool bar by detaching it from the root control.
/// </summary>
/// <value>The tool bar.</value>
public ToolBar ToolBar
{
    get
    {
        LayoutRoot.Children.Remove(toolBar_Main);
        return toolBar_Main;
    }
}

Next step is to hook up our new toolbar to the toolbar tray. We accomplish this during module initialization in the module class like this:

/* Add the custom toolbar to the StandardToolbarTray. */
var toolBarProvider = new CustomToolBarProvider();
var toolBar = toolBarProvider.ToolBar;
ShowView(RegionNames.StandardToolBarTray, toolBar, false);

If the false parameter in the ShowView method is set to true, the view will be immediately made visibile. If it is set to false, it may or may not be made visible. For example, if the view is located in a tab control, if true, the view will be made the selected item.

If you want to have a toolbar shown only when a particular view or type of view or view model is present in the workspace region then you can register it with a view service like so:

var viewService = ServiceLocatorSingleton.Instance.GetInstance<IViewService>();

viewService.AssociateVisibility(typeof(ImageDesignerView),
new UIElementAdapter(toolBar), Visibility.Collapsed);

Customize standard toolbar.

You can override the default style for any ShellView types. Copy the default style from WindowDictionary.xaml and place it within the application resources in App.xaml and customize PART_ContentControl_Menu. In the demo application I replaced the ToolBarTray icons with my own as shown in the following excerpt:

<!-- Menu -->
<ContentControl x:Name="PART_ContentControl_Menu" Grid.Row="1" Grid.ColumnSpan="3" 
    Margin="7,0,0,5" Padding="5" >
<StackPanel x:Name="PART_StackPanel_Menu">
<Controls:StandardMenu x:Name="PART_Menu" Margin="0, 0, 0, 0"  />
<!--<Controls:StandardToolBarTray x:Name="PART_ToolBar" Margin="0, 5, 0, 0" />-->
    <Border x:Name="Menu_Border" Width="Auto">
        <ToolBarTray cal:RegionManager.RegionName="{x:Static Calcium:RegionNames.StandardToolBarTray}">
            <ToolBar x:Name="ToolBar_Standard">
                <Button Command="ApplicationCommands.Save" 
                        ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}">
                    <Image Width="15" Height="15" Source="{x:Static m:SavePng.Source}"  />
                </Button>
                <Button Command="{x:Static Gui:ShellView.SaveAll}" 
                        ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}">
                    <Image Width="15" Height="15" Source="{x:Static m:SaveAllPng.Source}" />
                </Button>
            </ToolBar>
        </ToolBarTray>
    </Border>
</StackPanel>

Q13. How can I hide the standard menu or the standard toolbar?

You can hide the default standard toolbar or the menu by overriding the default style and setting the visibility of these controls as shown below:

<!-- Menu -->
<ContentControl x:Name="PART_ContentControl_Menu" Grid.Row="1" Grid.ColumnSpan="3" 
        Margin="7,0,0,5" Padding="5" >
    <StackPanel x:Name="PART_StackPanel_Menu">
        <Controls:StandardMenu x:Name="PART_Menu" Margin="0, 0, 0, 0" Visibility="Collapsed" />
        <Controls:StandardToolBarTray x:Name="PART_ToolBar" Margin="0, 5, 0, 0" Visibility="Collapsed"/>
    </StackPanel>
</ContentControl>

Q14. How can I display a modal dialog?

Modal support for WindowRegionAdapter has just been added to Calcium. It can be used by implementing IProvider<IViewOptions> on a View or ViewModel. The demo application demonstrates the modal dialog in the ImageListView where we have the View implementing the IProvider<IViewOptions> interface and a ViewOptions class which implements the IViewOptions as shown in the following excerpt:

public partial class ImageListView : IProvider<IViewOptions>
{
    public ImageListView()
    {
        InitializeComponent();
    }

   
    ViewOptions viewOptions = new ViewOptions();
    

    public IViewOptions ProvidedItem
    {
        get
        {
            return viewOptions;
        }
    }

    class ViewOptions : IViewOptions
    {
        public void PrepareContainer(object container)
        {
            
        }

        public bool Modal
        {
            get
            {
                return true;
            }
        }
    }
}

In the ImageListViewModel, we have a UICommand and attach a handler as the following excerpt shows:

public static UICommand SelectImageCommand = new UICommand(OnSelectImage);

The handler retrieves the IViewService instance from the service locator, instantiate the view and adds it to the shell region as shown below:

static void OnSelectImage(object obj)
{
    var viewService = ServiceLocatorSingleton.Instance.GetInstance<IViewService>();
    var content = new ImageSelectorView();
    string regionName;
    viewService.ShowView(content, RegionNames.Shell, true, out regionName);
}

In the ImageListView, we bind the UICommand to a button to raise the modal dialog as the following excerpt shows:

<Button Command="{x:Static Module:ImageListViewModel.SelectImageCommand}"
           Content="Add Image" />

Q15. How can I utilise the T4 templates?

T4 templates are automatically added during Project and Module creation. They are disabled by default and to utilise them you will need to run the Custom Tool. To run the Custom Tool, simply right click on the MetadataGeneration.tt file and select Run Custom Tool as shown in Figure 8.

Figure 8: Run Custom Tool

Once the custom tool generates the metadata, you can start using it. For example, in the demo project we use metadata to specify an image source as a statically typed property like this:

<Image Source="{x:Static m:ZoomInPng.Source}" Width="15" Height="15"/>

We also need to declare the namespace in the application element like this:

xmlns:m="clr-namespace:CalciumSample.FileMetadata.Icons.Metadata”

Note that in order to use the above code you need to set the Build Action for the image to a Resource (Right click on the icon, go to Properties and select Resource form the Build Action option list).

For more general information on T4 templates you can read Daniel’s article on Project Metadata Generation using T4.

Q16. Where can I make requests for features in future releases?

The best way to request a new feature is to use the Issue Tracker on the Calcium Codeplex site.

Conclusion

In this first part of the Calcium FAQ series we focused primarily on how to get started, we learned about module and view creation as well as how to modify and rebrand Calcium projects. We also learned how to utilise Calcium modal support and T4 templates. Part 2 will explain more Calcium features such as how to utilize the File Service or how to add new Region Names. I hope you found my efforts useful. If you enjoyed my article, please rate it and maybe share your thoughts below. Thanks for reading!

History

April 2010

Initial release

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