Welcome to Bilal Haidar [MVP, MCT] Official Blog Sign in | Join | Help

Loading XAML at runtime when using external assemblies in Silverlight

In an application I am working on, there is a need to load XAML dynamically from the server to populate a Telerik RadRibbonBar control. The XAML to be loaded is something similar to this:

<StackPanel xmlns:telerikRibbonBar="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.RibbonBar"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:cmd="clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation">
    <telerikRibbonBar:RadRibbonBar x:Name="rbnBar"
                                   ApplicationName="VBC Office Tool"
                                   Margin="0"
                                   ApplicationButtonImageSource="../AppIcon.png">
        <telerikRibbonBar:RadRibbonTab Header="VBC">
            <telerikRibbonBar:RadRibbonGroup Header="Clipboard">
                <StackPanel Orientation="Horizontal">
                    <telerikRibbonBar:RadRibbonButton x:Name="btn1"
                                                      CollapseToSmall="Never"
                                                      Size="Large"
                                                      Tag="Module 1"
                                                      cmd:Click.Command="{Binding Path=RibbonCommand}"
                                                      cmd:Click.CommandParameter="{Binding Path=Tag, RelativeSource={RelativeSource Self}}"
                                                      Text="Paste">
                        <telerikRibbonBar:RadRibbonButton.Content>
                            <Image Source="../paste.png" />
                        </telerikRibbonBar:RadRibbonButton.Content>
                    </telerikRibbonBar:RadRibbonButton>
                    <telerikRibbonBar:RadRibbonButton x:Name="btn2"
                                                      HorizontalContentAlignment="Center"
                                                      CollapseToSmall="Never"
                                                      Size="Large"
                                                      Tag="Module 2"
                                                      cmd:Click.Command="{Binding Path=RibbonCommand}"
                                                      cmd:Click.CommandParameter="{Binding Path=Tag, RelativeSource={RelativeSource Self}}"
                                                      Text="Create Document">
                        <telerikRibbonBar:RadRibbonButton.Content>
                            <Image Source="../save.png" />
                        </telerikRibbonBar:RadRibbonButton.Content>
                    </telerikRibbonBar:RadRibbonButton>
                </StackPanel>
            </telerikRibbonBar:RadRibbonGroup>
        </telerikRibbonBar:RadRibbonTab>
        <telerikRibbonBar:RadRibbonTab Header="iRisk" />
    </telerikRibbonBar:RadRibbonBar>
</StackPanel>

 

At the client-side, I am adding the XAML into a ContentControl as follows:

System.Windows.Resources.StreamResourceInfo streamInfo =
    System.Windows.Application.GetResourceStream(new Uri("ContextMenuModule;component/View/RibbonBar.xaml", UriKind.Relative));
if ((streamInfo != null) && (streamInfo.Stream != null))
{
    using (System.IO.StreamReader reader = new System.IO.StreamReader(streamInfo.Stream))
    {
        UIElement uribbon = (UIElement)XamlReader.Load(reader.ReadToEnd());
        this.ribbon.Content = uribbon;
    }
}

On the XamlReader.Load() method, I was getting the following exception:

AG_E_PARSER_BAD_TYPE 

Before I give the solution that I found on a forums’ blog post (http://forums.silverlight.net/forums/t/44877.aspx), let me explain to you the environment where the above code is executing.

It is a Silverlight-Prism application where I have the following structure:

  1. Shell app
  2. Module app

 

The above code is executing inside the Module app and proper references are added to all Telerik assemblies.

Seems that in Silverlight, when loading XAML dynamically in a class library (A) or Silverlight application (B), and then that app or class library is used by another class library (C) or Silverlight app (D), then all external assemblies that were used by (A) and (B) should also be added to (C) and (D).

Therefore, all Telerik assemblies used by Module app should also be added to the Shell application.

 

Hope this helps,
Regards

Commanding for RadContextMenu in Composite Application Library (Prism)

Download source code

Prism ships with a commanding framework that allows developers to extend current Silverlight controls, whether the ones ship with Silverlight or 3rd party controls, to support commanding. Commands are major when developing Silverlight applications based on a presentational design patterns such as Model View ViewModel (MVVM).

One of the Telerik controls that is used in most applications is the RadContextMenu. This control exposes the ItemClick event that fires when a menu item is clicked. However, the only way to handle this event is in the code-behind.

When talking about MVVM, there is always the question of what to put in the View and what to place in the ViewModel? For instance UI events could be handled in the code-behind, but definitely any functionality implemented by the event handler that might need to call a Web Service method to decide on a business rule or so, should be placed inside the ViewModel. Therefore, what can be done is expose few decision-action methods inside the ViewModel that the View can call from inside the code-behind event handlers. Another solution is attach commands on UI controls so that when an event fires on the UI a command defined inside the ViewModel gets executed. The second solution is the one we will look at in this article.

When attaching a RadContextMenu to a UIElement usually you handle the ItemClick event in the code-behind and process each menu-item clicked accordingly.

What we need to do is that when the ItemClick event is fired to execute a Command on the ViewModel. For this article, we will work with the Composite Application Library, the guidance or framework developed and released by Microsoft Patterns & Practices team which you can grab a copy from here: Composite WPF and Silverlight.

Composite Application Library provides support for Commands only for ButtonBase controls i.e. Button, RepeatButton, ToggleButton {CheckBox and RadioButton} and HyperlinkButton. At the same time, it has a very extendible Commanding framework to let developers provide commanding support for other types of controls. In this article, I will show you how to support Commanding for the Telerik RadContextMenu control by developing a command behavior for the RadContextMenu.

The type of Commanding that will be discussed in this article is the DelegateCommand that defines commands based on delegates. Initializing a command is done by providing two delegates: Execute and CanExecute:

  • Execute delegate is nothing but an Action<T> delegate
  • While CanExecute delegate is a Func<T, bool> delegate

Where T is the type of the parameter that could be passed into the Command handler.

To read more on DelegateCommands in the Composite Application Library, follow this link: Commands

 

Steps to enable Commanding for RadContextMenu as follows:

#1 – Create the RadContextMenuCommandBehavior class

The RadContextMenuCommandBehavior class adds the command behavior into the control in question by linking the fact of an event being fired to a command being executed. The class is simple and goes as follows:

public class RadContextMenuCommandBehavior : CommandBehaviorBase<RadContextMenu>
{
public RadContextMenuCommandBehavior(RadContextMenu source)
: base(source)
{
source.ItemClick += new Telerik.Windows.RadRoutedEventHandler(source_ItemClick);
}

void source_ItemClick(object sender, Telerik.Windows.RadRoutedEventArgs e)
{
var menu = sender as RadContextMenu;
if (menu != null)
{
// Set the parameter
this.CommandParameter = e;

// Execute the Command
this.ExecuteCommand();
}
}
}

The constructor of the RadContextMenuCommandBehavior class takes as input a RadContextMenu variable. Why? This class, through its constructor  hooks into the ItemClick event of the RadContextMenu variables passed as input to the constructor.

The event handler for the RadContextMenu ItemClick event simply executes the Command property found in the CommandBehaviorBase class (inherited by RadContextMenuCommandBehavior class) and filling in the command parameter to the RadRoutedEventArgs value that gives information on the menu item clicked and will be passed internally to the Command to be executed.

#2 – Attach the Command to the RadContextMenu control

Before being able to attach a Command on the RadContextMenu we need to create 2 Attached Properties as follows:

public static class MenuItemClicked
{
private static readonly DependencyProperty MenuItemClickedCommandBehaviorProperty = DependencyProperty.RegisterAttached(
"MenuItemClickedCommandBehavior",
typeof(RadContextMenuCommandBehavior),
typeof(MenuItemClicked),
null);

public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
"Command",
typeof(ICommand),
typeof(MenuItemClicked),
new PropertyMetadata(OnSetCommandCallback));

The CommandProperty property is an attached property that can be attached to a RadContextMenu. The MenuItemClickedCommandBehaviorProperty attached property is a private one and will see in few lines from here how is it used to populate the Command and CommandParameter properties of the RadContextMenuCommandBehavior class.

Notice how the CommandProperty specifies a PropertyMetaData instance.  The PropertyMetaData instance can contain a PropertyChangedCallback, a property default value or both. In our case, we will have a PropertyChangedCallback for both of those properties as follows:

private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
RadContextMenu contextMenu = dependencyObject as RadContextMenu;
if (contextMenu != null)
{
RadContextMenuCommandBehavior behavior = GetOrCreateBehavior(contextMenu);
behavior.Command = e.NewValue as ICommand;
}
}

private static RadContextMenuCommandBehavior GetOrCreateBehavior(RadContextMenu contextMenu)
{
RadContextMenuCommandBehavior behavior = contextMenu.GetValue(MenuItemClickedCommandBehaviorProperty) as RadContextMenuCommandBehavior;
if (behavior == null)
{
behavior = new RadContextMenuCommandBehavior(contextMenu);
contextMenu.SetValue(MenuItemClickedCommandBehaviorProperty, behavior);
}

return behavior;
}


To start with, the GetOrCreateBehavior main purpose is to attach the RadContextMenuCommandBehavior on the RadContextMenu and populate both of its Command and CommandParameter properties. It first gets the Dependency Attached Property “MenuItemClickedCommandBehavior” on the RadContextMenu. If null, means the property hasn’t been attached before, so create a new instance of the RadContextMenuCommandBehavior and attach it into the RadContextMenu. However, if the property has been already attached, then simply returns it.

When the Command attached property is being attached to a RadContextMenu, the OnSetComamndCallback is fired. What this method does is simply populate the Command property on the RadContextMenuCommandBehavior class.

Let us recap a little bit, when the Command property is attached on the RadContextMenu control, the following happens:

  1. RadContextMenuCommandBehavior instance is created, hence hooking into the ItemClick event of the RadContextMenu control
  2. The instance of RadContextMenuCommandBehavior created above is attached as an attached property on the RadContextMenu control
  3. RadContextMenuCommandBehavior’s Command property is populated with the Command set on the RadContextMenu control

Now going back into the event handler for the ItemClick inside the RadContextMenuCommandBehavior class, when the RadContextMenu ItemClick event is fired, the ExecuteCommand() method is executed. Command executed means, the Execute Action<T> delegate is executed.

 

#2 – Define and Initialize the Command together with the Execute Action<T> delegate

Typically, we have been trying through out this article to prepare all the needed stuff to define the Command together with its Execute() method on the ViewModel. So now you know the answer, no need to continue explaining anything :), no wait!!!! Show us how to do it!

When developing with MVVM, each View might have a single ViewModel together with the IViewModel as follows:

public interface IContextMenuViewModel : IViewModel
{
DelegateCommand<object> MenuItemClickedCommand { get; set; }

ColorNames BackgroundColor { get; }
}

The ContextMenuViewModel implements the above interface:

public class ContextMenuViewModel : ViewModelBase, IContextMenuViewModel
{
public ContextMenuViewModel(
IEventAggregator eventAggregator,
IUnityContainer container, IModuleManager manager)
: base(eventAggregator, container, manager)
{
this.MenuItemClickedCommand = new DelegateCommand<object>(OnMenuItemClicked);
}

public DelegateCommand<object> MenuItemClickedCommand { get; set; }
private ColorNames backgroundColor;
public ColorNames BackgroundColor
{
get { return this.backgroundColor; }
private set
{
if (value == this.backgroundColor)
return;

this.backgroundColor = value;
FirePropertyChanged("BackgroundColor");
}
}

protected void OnMenuItemClicked(object parameter)
{
RadRoutedEventArgs args = parameter as RadRoutedEventArgs;
var radMenuItem = args.OriginalSource as RadMenuItem;

ColorNames color = ColorNames.White;
Enum.TryParse<ColorNames>(radMenuItem.Header.ToString(), out color);

this.BackgroundColor = color;
}
}

The class above defines a new property, MenuItemClickedCommand as a DelegateCommand<object> meaning that define for me a new Command that I can bind to on the RadContextMenu on the View that expects a parameter of type object in case a parameter is passed.

In the constructor of the ViewModel, we initialize the MenuItemClickedCommand by specifying the Execute method of type Action<object>() method, OnMenuItemClicked method. This method handles the execution of the Command. Before discussing its details, there is another public property on the ViewModel that is the BackgroundColor which is bound to the Background property of the Grid control on the View.

When a command is executed, the OnMenuItemClicked method gets executed which does the following:

  1. Extracts the RadMenuItem that was clicked from the passed in parameter. Remember when defining the RadContextMenuCommandBehavior class, we set the CommandParameter to an instance of the RadRoutedEventArgs value.
  2. In our case, the Header’s property of the RadMenuItem is a color name, hence we generate a new Color instance and set it on the BackgroundColor property.
  3. The “set” of the BackgroundColor property simply notifies the Data Binding engine in Silverlight that I have a new value and hence the UI I am bound to should be updated.

On the UI, how do we set the Command?

xmlns:infra="clr-namespace:bhaidar.Infrastructure;assembly=bhaidar.RCMInPrism.Infrastructure"

<Border BorderBrush="Blue"
BorderThickness="2"
CornerRadius="3">
<Grid x:Name="LayoutRoot"
Background="{Binding BackgroundColor, Converter={StaticResource colorConverter}}">

<TextBlock Text="Right-click anywhere here"
HorizontalAlignment="Center"
VerticalAlignment="Center" />

<teleriknavigation:RadContextMenu.ContextMenu>
<teleriknavigation:RadContextMenu x:Name="cmMenu"
infra:MenuItemClicked.Command="{Binding MenuItemClickedCommand}">
<teleriknavigation:RadMenuItem Header="Red" />
<teleriknavigation:RadMenuItem Header="Blue" />
</teleriknavigation:RadContextMenu>
</teleriknavigation:RadContextMenu.ContextMenu>

</Grid>
</Border>

 

Notice first of all how a new XAML namespace has been defined reflecting the place where the Attached Properties have been defined. Once the namespace is added, notice how the RadContextMenu now has a new attached property as follows: infra:MenuItemClicked.Command=”{Binding MenuItemClickedCommand}”.

  1. The MenuItemClicked is the static class that defines the Attached Properties
  2. Command is an attached property
  3. The attached property Command is bound to the MenuItemClickedCommand command defined on the ViewModel.

The above means the following:

  1. The MenuItemClickedCommand command is initialized inside the ViewModel where the Execute method is defined.
  2. When the Command attached property is set, an instance of the RadContextMenuCommandBehavior class is instantiated and hooked into the ItemClick event of the RadContextMenu.
  3. The Command property defined on the RadContextMenuCommandBehavior object is populated with the Command defined in the ViewModel
  4. When an item on the RadContextMenu is clicked, the ItemClick is fired, hence the Command property on the RadContextMenuCommandBehavior object is executed, hence the Execute method of the MenuItemClickedCommand command defined inside the ViewModel gets called and executed.

Download source code

 

Bilal Haidar,
Regards

Happy New Year - 2010

Happy New Year my precious readers!
Hope I will be able to post regularly this year covering as much as possible from the new and latest technologies.

 

Bilal,
Best Regards

Visual Studio 2010 and Telerik Products

Telerik products support for Visual Studio 2010 Beta 2

Visual Studio 2010 and Telerik Products

 

Hope this helps,
Regards

Silverlight SDK Sample Browser

I have read the Silverlight documentation several times and never noticed the SDK Sample Browser, really interesting. You can run all the samples that are written as part of the official documentation.

Check it out here: Silverlight SDK Sample Browser

 

Hope this helps,
Regards

Posted by BilalHaidar [MVP] | 0 Comments
Filed under:

PRISM - A Developer's Introduction

I will be presenting next week a session on Composite Application Library - Prism, an introduction to developers to start using Prism in their applications.

If you like to join, please come join us at: PRISM - A Developer's Introduction

 

See you there ;)
Regards

Posted by BilalHaidar [MVP] | 0 Comments
Filed under: , ,

Telerik First to Support Microsoft Silverlight 4

Silverlight 4 Support

The First Native Silverlight 4 Controls

RadControls for Silverlight 4 CTP is the first UI component suite to natively support Microsoft Silverlight 4 Beta. Read more

Tangible Software Solutions - Language Converters

"Convert Between VB, C#, C++, & Java with the most Accurate & reliable source code converters"

 

Check out more here: Tangible Software Converters

 

Hope it helps,
Regards

Service Pack 1 released – RadControls for Silverlight/WPF Q3 2009

Telerik just released SP1 for the RadControls for Sivlerlight/WPF Q3 2009.

Read more here: Service Pack 1 released - RadControls for Silverlight/WPF Q3 2009

 

Hope this helps,
Regards

Sharing Silverlight Assemblies with .NET Apps

Here is a great article from the CLR team on how to share some of the .NET assemblies with Silverlight:

 Sharing Silverlight Assemblies with .NET Apps

 

Hope it helps,
Regards

Posted by BilalHaidar [MVP] | 0 Comments
Filed under: ,

Prism Videos by Mike Taulty

A very good resource to learn more about Prism can be found here:

Mike Taulty Video Series on Prism

 

Hope you enjoy them,
Regards

Prism V2 - View Discovery Composition Quick Start using MVVM

Before I started learning Prism V2, I was working with Silverlight & Model-View-ViewModel (MVVM) design pattern.

I read the Prism documentation and other articles/blog posts online and noticed that most of them are using other design patterns like PresentationModel and others. Even the Quick Starts that ship with the Prism V2 are all written in a design pattern different from MVVM.

I decided to implement one of the Quick Starts in MVVM, I might be doing the rest of Quick Starts in MVVM if time allows. It will be a good practice on both Prism and MVVM.

The View Discovery Composition quick start in action:

 

image

This blog post handles the View Discovery Composition Quick Start in Silverlight using MVVM. Through out the article I will be using the Prism Quick Start Kit by David Hill (Prism Quick Start Kit)

 

Create VS 2008 Solution

  1. Create a new Blank Solution inside VS 2008.
  2. Add the following projects:
    1. Prism Silverlight Shell project name it UIComposition.
    2. Silverlight Class Library name it UIComposition.Infrastructure
    3. ASP.NET Web Application name it Web
    4. Add a Folder it to the solution name it Modules
      1. To the Modules folder add the following projects:
        1. Prism Silverlight Module project name it UIComposition.Modules.Employee

The following snapshot shows the structure of each of the above created projects

image

Each of the above projects to be explained in details below.

 

UIComposition Shell

This project is the Prism Shell project. It contains the Bootstrapper and catalogues to load in the application.

 

Bootstrapper

Bootstrapper is a Prism-specific class that connects the Composite Application Library (Prism) to the Silverlight application. It has a major role in the life cycle of the Prism where it does the following:

  1. Creates the UnityContainer
  2. Configures the container with the default system Type mappings:
    1. ILoggerFacade –> TextLogger
    2. IServiceLocator –> UnityServiceLocatorAdapter
    3. IModuleInitializer –> ModuleInitializer
    4. IModuleManager –> ModuleManager
    5. RegionAdapterMappings –> RegionAdapterMappings
    6. IRegionManager –> RegionManager
    7. IEventAggregator –> EventAggregator
    8. IRegionViewRegistry –> RegionViewRegistry
    9. IRegionBehaviorFactory –> RegionBehaviorFactory
  3. Configures region adapters, currently the following region adapters are available: (You can write your own one even)
    1. TabControl –> TabControlRegionAdapter (only for Silverlight)
    2. Selector –> SelectorRegionAdapter
    3. ItemsControl –> ItemsControlRegionAdapter
    4. ContentControl –> ContentControlRegionAdapter
  4. Configures default region behaviors
  5. Registers Prism specific exception types
  6. Create the Shell
  7. Initializes the modules

The above was a summary on the major functionalities of the Bootstrapper. For each Shell application a single Bootstrapper is required. The major methods to override in this project are the following:

   1: /// <summary>
   2: /// Add any specific Type mapping for the application
   3: /// </summary>
   4: protected override void ConfigureContainer()
   5: {
   6:     // Register the DataService to be used by other modules
   7:     this.Container.RegisterType<IDataService, DataService>();
   8:  
   9:     base.ConfigureContainer();
  10: }

Inside the ConfigureContainer, a call to the base ConfigureContainer method is performed so that all the system-level Types get mapped. In addition, developer n add her own mappings as shown above where the IDataService interface is mapped to the DataService concrete implementation. This is a technique I learned from Telerik Sales Dashboard, where the DataService class plays the role of a Web service proxy class and gets registered by the Container so that any module can make use of the DataService and retrieve data.

   1: /// <summary>
   2: /// Setup all modules to be loaded
   3: /// either at start-up time or on-demand
   4: /// </summary>
   5: /// <returns>An instance of ModuleCatalog populated with modules to be loaded</returns>
   6: protected override IModuleCatalog GetModuleCatalog()
   7: {
   8:     return ModuleCatalog.CreateFromXaml(
   9:                 new Uri("/UIComposition;component/ModuleCatalog.xaml", UriKind.Relative));
  10: }

Another method to override is the GetModuleCatalog() method. This method returns a ModuleCatalog populated with all modules to be loaded on startup time and on demand. The method above reads from an XAML file that lists the modules to be used and loaded in the application. The ModuleCatalog.xaml file contains the following modules to load:

   1: <prism:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   2:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   3:     xmlns:sys="clr-namespace:System;assembly=mscorlib"
   4:     xmlns:prism="clr-namespace:Microsoft.Practices.Composite.Modularity;assembly=Microsoft.Practices.Composite">
   5:  
   6:     <prism:ModuleInfo Ref="UIComposition.Modules.Employee.xap"
   7:                       ModuleName="EmployeeModule"
   8:                       ModuleType="UIComposition.Modules.Employee.ModuleInit, UIComposition.Modules.Employee, Version=1.0.0.0"
   9:                       InitializationMode="WhenAvailable"/>
  10: </prism:ModuleCatalog>
  11:  

There is only one module we are loading which is the UIComposition.Modules.Employee.xap module. As you can notice, each XAP can represent a single or more modules. Also notice the InitializationMode which is set to WhenAvailable meaning that, when the application starts this module has to be loaded and initialized. Modules can be loaded on demand, hopefully in another blog post I will show a modular pluggable application loading modules on demand.

Finally, the CreateShell() method is to be explored:

   1: /// <summary>
   2: /// Creates the main shell of the app
   3: /// </summary>
   4: /// <returns>A DependencyObject representing application master page</returns>
   5: protected override DependencyObject CreateShell()
   6: {
   7:     // Use the container to create an instance of the shell.
   8:     ShellView view = Container.Resolve<ShellView>();
   9:  
  10:     // Display the Shell as the root visual for the application.
  11:     Application.Current.RootVisual = view;
  12:  
  13:     return view;
  14: }

The main page or master page or template or Shell of the application is created at this moment and set as a value to the Application.Current.RootVisual which is usually set from the App.xaml.cs class at the start up of the application. This means that any class that descends from the DependencyObject can be used as a Shell.

This ends discussing the Bootstrapper class. Now discussion handles the ShellView.xaml user control.

 

ShellView.xaml

   1: <UserControl x:Class="UIComposition.Views.ShellView"
   2:     ...
   3:     xmlns:cal="clr-namespace:Microsoft.Practices.Composite.Presentation.Regions;assembly=Microsoft.Practices.Composite.Presentation">
   4:  
   5:     <Grid>
   6: ...
   7:                 <ItemsControl cal:RegionManager.RegionName="MainRegion"
   8:                               Width="Auto"
   9:                               Height="Auto"
  10:                               HorizontalAlignment="Stretch"
  11:                               VerticalAlignment="Stretch" />
  12:    ...
  13:     </Grid>
  14:  
  15: </UserControl>

There are two important things to note about here:

   1: xmlns:cal="clr-namespace:Microsoft.Practices.Composite.Presentation.Regions;assembly=Microsoft.Practices.Composite.Presentation"

The markup above is importing the Rergions namespace so that we can make use of the RegionManager to decorate regions inside the ShellView.

   1: <ItemsControl cal:RegionManager.RegionName="MainRegion" ... />

The code is decorating the ItemsControl with a region name of “MainRegion”. A region is simply a placeholder that is used to place content Injected or Discoverd at run time.

The code-behind of the ShellView is as follows:

   1: public partial class ShellView : UserControl
   2: {
   3:     public ShellView(ShellViewModel viewModel)
   4:     {
   5:         InitializeComponent();
   6:  
   7:         // Set the ViewModel as this View's data context.
   8:         this.DataContext = viewModel;
   9:     }
  10: }

The ShellView is a normal UserControl that has a constructor taking as input parameter an object of type ShellViewModel class. This is the place where we inject an instance of the ViewModel (ShellViewModel) and set it as the DataContext of the View (ShellView).

There is nothing important to mention about the ShellViewModel at this stage, later on we will discuss the ViewModel in more details for other Views.

 

UIComposition.Infrastructure

This project is a Silverlight Class Library that is used usually in a Prism application to hold common classes/events/delegates/exceptions that can be used by all modules and shell in the application.

 

Interfaces

This quick start includes a single interface the IDataService.

   1: /// <summary>
   2: /// Data service interface.
   3: /// </summary>
   4: public interface IDataService
   5: {
   6:     Employees GetEmployees();
   7: }

The IDataService interface contains a single method, GetEmployees() that returns a collection of EmployeeItem objects, Employees collection.

 

Models

In the Models folder there are two main classes: EmpoyeeItem and Employees classes representing a single employee class and a collection of employees that are needed both to populate the DataGrid  later on.

 

Events

A single event has been included in this project, the EmployeeItemSelectedEvent.

   1: /// <summary>
   2: /// Event holding as payload the employee selected
   3: /// </summary>
   4: public class EmployeeItemSelectedEvent : CompositePresentationEvent<EmployeeItem>
   5: {
   6:  
   7: }

The event inherits from CompositePresentationEvent<T> class. The EmployeeItemSelectedEvent holds as payload an EmployeeItem. Later on you will see the use of this event.

 

The UIComposition.Infrastructure class library can hold any thing that is common to modules which is better to be placed in a single place.

 

UIComposition.Modules.Employee Modules

This is a Prism Module that have been created using the Prism Quick Start Kit. This module according to the View Discovery Composition quick start displays a list of employees, selecting an employee displays a TabControl having one Tab item as a general info about the employee and the other Tab item listing projects this specific selected employee belongs to.

The main class in this module is the ModuleInit.cs class. This class inherits from IModule which every module should inherit from.

   1: public class ModuleInit : IModule
   2: {
   3:     private readonly IUnityContainer _container;
   4:     private readonly IRegionManager _regionManager;
   5:     private EmployeesController _controller;
   6:  
   7:     public ModuleInit(IUnityContainer container, IRegionManager regionManager)
   8:     {
   9:         _container = container;
  10:         _regionManager = regionManager;
  11:     }
  12:  
  13:     #region IModule Members
  14:  
  15:     public void Initialize()
  16:     {
  17:         // Display the EmployeeView in the "MainRegion" region.
  18:         _regionManager.RegisterViewWithRegion(RegionNames.MainRegion, () => _container.Resolve<EmployeeView>());
  19:         //_regionManager.Regions[RegionNames.MainRegion].Add(() => _container.Resolve<EmployeeView>());
  20:  
  21:         // Display the EmployeeListView in the "SelectionRegion" region.
  22:         _regionManager.RegisterViewWithRegion(RegionNames.SelectionRegion, () => _container.Resolve<EmployeesListView>());
  23:  
  24:         // Register the IEmployeesDetailsView interface
  25:         _container.RegisterType<IEmployeesDetailsView, EmployeesDetailsViewViewModel>(new ContainerControlledLifetimeManager());
  26:  
  27:         // Create the controller.
  28:         _controller = _container.Resolve<EmployeesController>();
  29:     }
  30:  
  31:     #endregion
  32: }

This module registers the EmployeeView into the MainRegion that has been previously defined in the ShellView above.

The EmployeeView as you will notice in a while contains two main regions: SelectionRegion and DetailsRegion as shown in the code snippet below. The Initialize() method also registers the EmployeeListView to the SelectionRegion.

   1: <StackPanel>
   2:     <ContentControl x:Name="SelectionPanel"
   3:                     cal:RegionManager.RegionName="SelectionRegion" />
   4:  
   5:     <ContentControl cal:RegionManager.RegionName="DetailsRegion" />
   6: </StackPanel>

The idea is not simpler, we have the MainRegion that will hold the EmployeeView and inside the EmployeeView there are two regions, one of them is the SelectionRegion and the EmployeeListView is bound to it.

Next the Initialize() method registers with the Container an instance of the EmployeesDetailsViewViewModel class. More on this class will be looked at in the coming sections. Just notice for the time being that the EmployeesDetailsViewViewModel has been registered as a Singleton object using ContainerControlledLifetimeManager().

Finally, the Initialize() method create a new instance of the EmployeesController class using the Container.Resolve() method.

 

Creating the EmployeesController instance does the following:

   1: public class EmployeesController
   2: {
   3:     private readonly IUnityContainer _container;
   4:     private readonly IRegionManager _regionManager;
   5:     private readonly IEventAggregator _eventAggregator;
   6:  
   7:     public EmployeesController(IUnityContainer container,
   8:                         IRegionManager regionManager,
   9:                         IEventAggregator eventAggregator)
  10:     {
  11:         _container = container;
  12:         _regionManager = regionManager;
  13:         _eventAggregator = eventAggregator;
  14:  
  15:         // Subscribe to the EmployeeItemSelectedEvent
  16:         _eventAggregator.GetEvent<EmployeeItemSelectedEvent>().Subscribe(DisplayEmployeeDetails, true);
  17:     }
  18: }

The above is the constructor of the EmployeesController class. The EmployeesController subscribes to the EmployeeItemSelected event and fires the DisplayEmployeeDetails() when the EmployeeItemSelected event is published.

 

The EmployeesListView is as follows:

   1: <StackPanel>
   2:     <TextBlock Width="Auto"
   3:                Height="Auto"
   4:                Text="Select Employee:"
   5:                Padding="0,0,0,0"
   6:                FontWeight="Bold"
   7:                Margin="0,0,0,0"
   8:                HorizontalAlignment="Left"
   9:                VerticalAlignment="Top" />
  10:     <Controls:DataGrid x:Name="EmployeesList"
  11:                        ItemsSource="{Binding Path=Employees}"
  12:                        SelectedItem="{Binding Path=SelectedEmployee, Mode=TwoWay}"
  13:                        AutomationProperties.AutomationId="EmployeesListGrid"
  14:                        AutoGenerateColumns="False" IsReadOnly="True">
  15:         <Controls:DataGrid.Columns>
  16:             <Controls:DataGridTextColumn Header="First Name"
  17:                                          Binding="{Binding Path=FirstName}" />
  18:             <Controls:DataGridTextColumn Header="Last Name"
  19:                                          Binding="{Binding Path=LastName}" />
  20:         </Controls:DataGrid.Columns>
  21:     </Controls:DataGrid>
  22: </StackPanel>

The View is very simple, displaying a Grid where a user can select an employee record from.

Notice that we have two main bindings:

  1. Employees: This contains all employees to be loaded into the Grid
  2. SelectedEmployee: This contains the employee selected on the Grid

The code-behind of the above view is as follows:

   1: public partial class EmployeesListView : UserControl
   2: {
   3:     public EmployeesListView(EmployeesListViewViewModel viewModel)
   4:     {
   5:         InitializeComponent();
   6:  
   7:         // Set the ViewModel as this View's data context.
   8:         this.DataContext = viewModel;
   9:     }
  10: }

All what the constructor does is, set the control’s DataContext to the passed in ViewModel. Therefore, the two binding properties on the EmployeesListView are placed on the ViewModel that is set as a DataContext.

   1: public class EmployeesListViewViewModel : INotifyPropertyChanged
   2: {
   3:     private readonly IEventAggregator _eventAggregator;
   4:  
   5:     public EmployeesListViewViewModel(IDataService dataService, IEventAggregator eventAggregator)
   6:     {
   7:         _eventAggregator = eventAggregator;
   8:  
   9:         // Use the data service to load the model.
  10:         Employees = dataService.GetEmployees();
  11:     }
  12:  
  13:     #region View Databinding Properties
  14:     public Employees Employees { get; private set; }
  15:  
  16:     private EmployeeItem employee;
  17:     public EmployeeItem SelectedEmployee 
  18:     {
  19:         get { return this.employee; }
  20:         set 
  21:         { 
  22:             this.employee = value;
  23:             NotifyPropertyChanged("SelectedEmployee");
  24:  
  25:             // Fire an event to signal a change 
  26:             // in the employee selected
  27:             if (this.employee != null)
  28:                 _eventAggregator.GetEvent<EmployeeItemSelectedEvent>().Publish(this.SelectedEmployee);
  29:         }
  30:     }
  31:     #endregion
  32:  
  33:     #region INotifyPropertyChanged Members
  34:     public event PropertyChangedEventHandler PropertyChanged;
  35:  
  36:     private void NotifyPropertyChanged(string propertyName)
  37:     {
  38:         if (PropertyChanged != null)
  39:         {
  40:             PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  41:         }
  42:     }
  43:     #endregion
  44: }

Few hints on the above code:

  1. The Employees property is being populated inside the constructor by calling the GetEmployees() method on a data service
  2. In the set part of the SelectedEmployee property, the EmployeeItemSelectedEvent is being published to notify all subscribes that a new employee has been selected.

Before diving into the code that will be fired to show the details of an employee, we are left with two main components to explain:

 

EmployeesDetailsView

   1: <StackPanel Margin="0,5,0,5">
   2:     <Controls:TabControl AutomationProperties.AutomationId="DetailsTabControl"
   3:                          Regions:RegionManager.RegionName="TabRegion"
   4:                          Width="Auto"
   5:                          Height="Auto"
   6:                          Margin="0,5,0,0"
   7:                          HorizontalAlignment="Stretch">
   8:         <Regions:TabControlRegionAdapter.ItemContainerStyle>
   9:             <Style TargetType="Controls:TabItem">
  10:                 <Setter Property="HeaderTemplate">
  11:                     <Setter.Value>
  12:                         <DataTemplate>
  13:                             <TextBlock Text="{Binding Path=HeaderInfo}" />
  14:                         </DataTemplate>
  15:                     </Setter.Value>
  16:                 </Setter>
  17:             </Style>
  18:         </Regions:TabControlRegionAdapter.ItemContainerStyle>
  19:         <Controls:TabItem Header="{Binding Path=HeaderInfo}">
  20:             <Grid  DataContext="{Binding SelectedEmployee}"
  21:                    Width="Auto"
  22:                    Height="Auto">
  23:                 <Grid.ColumnDefinitions>
  24:                     <ColumnDefinition Width=".5*" />
  25:                     <ColumnDefinition Width=".5*" />
  26:                 </Grid.ColumnDefinitions>
  27:                 <StackPanel Grid.Column="0"
  28:                             Orientation="Vertical">
  29:                     <TextBlock Text="First Name:"
  30:                                HorizontalAlignment="Left"
  31:                                Padding="0,5,5,5"
  32:                                FontWeight="Bold" />
  33:                     <TextBox AutomationProperties.AutomationId="FirstNameTextBox"
  34:                              Text="{Binding Path=FirstName}"
  35:                              Height="Auto"
  36:                              Width="Auto"
  37:                              HorizontalAlignment="Stretch"
  38:                              Margin="0,5,100,5"></TextBox>
  39:                     <TextBlock Text="Description:"
  40:                                HorizontalAlignment="Left"
  41:                                Padding="0,5,5,5"
  42:                                FontWeight="Bold" />
  43:                     <TextBox AutomationProperties.AutomationId="DescriptionTextBox"
  44:                              Text="{Binding Path=Description}"
  45:                              Height="Auto"
  46:                              Width="Auto"
  47:                              HorizontalAlignment="Stretch"
  48:                              Margin="0,5,100,5"></TextBox>
  49:                 </StackPanel>
  50:                 <StackPanel Grid.Column="1">
  51:                     <TextBlock Text="Last Name:"
  52:                                HorizontalAlignment="Left"
  53:                                Padding="0,5,5,5"
  54:                                FontWeight="Bold" />
  55:                     <TextBox AutomationProperties.AutomationId="LastNameTextBox"
  56:                              Text="{Binding Path=LastName}"
  57:                              Height="Auto"
  58:                              Width="Auto"
  59:                              HorizontalAlignment="Stretch"
  60:                              Margin="0,5,100,5"></TextBox>
  61:                 </StackPanel>
  62:             </Grid>
  63:         </Controls:TabItem>
  64:     </Controls:TabControl>
  65: </StackPanel>

The EmployeesDetailsView displays a TabControl with a single TabItem to display details of an Employee.

The code-behind binds the EmployeesDetailsView’s DataContext to the EmployeesDetailsViewViewModel object, which has the following details:

   1: public EmployeesDetailsViewViewModel(IDataService dataService, IEventAggregator eventAggregator)
   2: {
   3:     _eventAggregator = eventAggregator;
   4: }
   5:  
   6: #region View Databinding Properties
   7: private EmployeeItem employee;
   8: public EmployeeItem SelectedEmployee
   9: {
  10:     get { return this.employee; }
  11:     set
  12:     {
  13:         this.employee = value;
  14:         NotifyPropertyChanged("SelectedEmployee");
  15:     }
  16: }
  17:  
  18: /// <summary>
  19: /// Header title of the Tab
  20: /// </summary>
  21: public string HeaderInfo
  22: {
  23:     get
  24:     {
  25:         return "General Info";
  26:     }
  27: }
  28: #endregion
  29:  
  30: #region IEmployeesDetailsView Members
  31:  
  32: /// <summary>
  33: /// Injects the employee selected
  34: /// </summary>
  35: /// <param name="employee"></param>
  36: public void SetSelectedEmployee(EmployeeItem employee)
  37: {
  38:     if (employee != null)
  39:         this.SelectedEmployee = employee;
  40: }
  41:  
  42: #endregion
  43:     }

The TabItem is bound to the SelectedEmployee property on the ViewModel class.

In addition, this class implements the IEmployeesDetailsView interface which contains a single method, SetSelectedEmployee() method.

 

It was mentioned above that the EmployeesController subscribed to the EmployeesItemSelectedEvent hence, when an employee is selected on the Grid, the DisplayEmployeeDetails() method located in the EmployeesController fires.

   1: private void DisplayEmployeeDetails(EmployeeItem employee)
   2: {
   3:     IRegion detailsRegion = _regionManager.Regions[RegionNames.DetailsRegion];
   4:     object existingView = detailsRegion.GetView(employee.Id.ToString(CultureInfo.InvariantCulture));
   5:  
   6:     // the view does not exist yet. Create it and push it into the region
   7:     IEmployeesDetailsView detailsViewModel = this._container.Resolve<IEmployeesDetailsView>();
   8:     detailsViewModel.SetSelectedEmployee(employee);
   9:  
  10:     // See if the view already exists in the region. 
  11:     if (existingView == null)
  12:     {
  13:         // Create the View
  14:         existingView = _container.Resolve<EmployeesDetailsView>();
  15:  
  16:         // Add the view into the details region
  17:         detailsRegion.Add(existingView, employee.Id.ToString(CultureInfo.InvariantCulture), true);
  18:  
  19:         // Activate the View
  20:         detailsRegion.Activate(existingView);
  21:     }
  22:     else
  24:         // The view
  23:     {
 already exists. Just show it. 
  25:         detailsRegion.Activate(existingView);
  26:     }    
  27: }

  1. Code starts by retrieving a reference to the DetailsRegion
  2. Get an employee’s view that has been already loaded into that region (if any)
  3. Gets a singleton instance of the EmployeesDetailsViewViewModel object return in the form of an instance of IEmployeesDetailsView interface
  4. Call the SetSelectedEmployee() method to fill in the selected employee on the ViewModel
  5. If the currently selected employee has no previously loaded EmployeeDetailsView then the following happens:
    1. Create a new instance of the EmployeeDetailsView. This view takes as input an interface of type IEmployeesDetailsView and hence in this case, the view will be injected the singleton object that is populated every time with the selected employee. I had to do this trick here of the singleton object since a requirement is to inject the EmployeesDetailsView, else if I could show the view from the beginning and empty, I would have only subscribed to the EmployeesItemSelectedEvent to handle setting the selected employee.
    2. Add the view (populated with the employee selected) into the DetailsRegion
    3. Activate the view
  6. Else, the code activates the existing instance of the EmployeesDetailsView

 

That was a detailed overview on how I converted the View Discovery Composition Quick Start into MVVM.

Hope you enjoyed and benefited from this post.

 

Download Link

 

Regards

More Posts Next page »