View Injection Composition QuickStart
The View Injection Composition QuickStart demonstrates how to build composite Windows Presentation Foundation (WPF) or Silverlight user interfaces composed of different views that are dynamically loaded into regions and that interact with each other in a decoupled way. The View Injection Composition QuickStart uses the same approach as the previous Composite Application Library version; the shell contains several regions that define its layout, and views are loaded by modules pushing them into the corresponding region.
Business Scenario
The View Injection Composition QuickStart is based on a fictitious resource management system. The main window represents a subset of a larger system. In this window, the user can review detailed information about employees of a company. Figure 1 illustrates the QuickStart's main window.
Figure 1
View Injection Composition QuickStart —Silverlight version
Building and Running the QuickStart
The QuickStart ships as source code—this means you must compile it before you run it. This QuickStart does not have any prerequisites.
To build and run the QuickStart
- In Visual Studio, open the solution file Quickstarts\UI Composition\ViewInjection\ViewInjectionComposition.sln.
- Make sure the desired version of the QuickStart is set as the startup project. If it is not, right-click the desired project in Solution Explorer, and then click Set as Startup Project:
- To build and run the WPF version of the QuickStart, the startup project should be the UIComposition.Desktop project in the Desktop solution folder.
- To build and run the Silverlight version of the QuickStart, the startup project should be the UIComposition.Silverlight project in the Silverlight solution folder.
- On the Build menu, click Rebuild Solution.
- Press F5 to run the QuickStart.
Walkthrough
The following procedure provides steps to explore the business scenario in the View Injection Composition QuickStart.
To explore the business scenario
In Visual Studio, open the solution file Quickstarts\UI Composition\ViewInjection\ViewInjectionComposition.sln.
Make sure the desired version of the QuickStart is set as the startup project. For more information about this, see "Building and Running the QuickStart" earlier in this topic.
On the Build menu, click Rebuild Solution.
Press F5 to run the QuickStart. The main window shows a list of employees, as illustrated in Figure 2.
Figure 2
QuickStart main window — WPF version
Select an employee. This dynamically loads a view that shows the employee details, as shown in Figure 3.
Figure 3
The General tab for a selected employee
Explore the different tabs. Each tab provides additional information about the employee. Figure 4 shows the Current Projects tab.
Figure 4
The Current Projects tab for a selected employee
Implementation Notes
The QuickStart highlights the key implementation details of an application that uses regions. Figure 5 illustrates the key artifacts in the application.
Figure 5
View Injection Composition QuickStart conceptual view
The following artifacts are illustrated in Figure 6:
- Shell window. This is the application's main window. This window contains the main region.
- Employees view. This view is the main view of the Employees module. It hosts the Employees List view and contains the Details region, where instances of the Employees Details view are loaded.
- Employees List view. This view displays a list of employees.
- Employees Details view. This view displays detailed information for an employee and contains a Tab region.
- Projects List view. This view displays the list of projects an employee is working on.
- Employees controller. This controller coordinates the interaction of the Employees module's views.
The next sections describe each artifact in greater detail.
Shell Window
The Shell window defines a single region that is the main region of the application. The region is defined in the Shell window's XAML markup file (this file is located at UIComposition.{Technology}\Shell.xaml), where {Technology} can be Desktop or Silverlight), as shown in the following code.
<ItemsControl cal:RegionManager.RegionName="{x:Static infrastructure:RegionNames.MainRegion}" />
Note that a constant is used for the region's name. This constant is defined in the RegionNames class of the UIComposition.Infrastructure.{Technology} project (the class file is located at UIComposition.Infrastructure.{Technology}\RegionNames.cs).
Note
In the Silverlight version of the QuickStart, notice that the region names are hardcoded in the XAML instead of using constants. The reason for this is that Silverlight does not support the x:Static markup extension used to reference static code entities, such as constants.
Employees View
The Employees view is a view that contains ContentControl controls where children views can be placed. The view implementation is split in multiple files inside the folder UIComposition.Modules.Employee.{Technology}\Views\EmployeesView.
The following Silverlight code shows the markup of the view where two ContentControls controls are defined.
<StackPanel>
<ContentControl x:Name="HeaderPanel"/>
<ContentControl cal:RegionManager.RegionName="DetailsRegion"/>
</StackPanel>
In the preceding code, the following ContentControl controls are defined:
- HeaderPanel. This control will hold the Employees List view.
- DetailsPanel. This control defines a region named DetailsRegion. In this region, instances of the Employees Details view will be loaded when an employee is selected in the Employees List view. In this region, only one view at a time will be displayed.
The Employees view is added to the main region by the Employee's module initializer class (located at UIComposition.Modules.Employee.{Technology}\EmployeeModule.cs) when the application starts. The following code shows the Initialize method of this class, where the view is added to the main region.
public void Initialize()
{
this.RegisterViewsAndServices();
EmployeesPresenter presenter = this.container.Resolve<EmployeesPresenter>();
IRegion mainRegion = this.regionManager.Regions[RegionNames.MainRegion];
mainRegion.Add(presenter.View);
}
Employees List View
The Employees List view displays a list of employees from which the user can select. The view implementation is in the folder UIComposition.Modules.Employee.{Technology}\Views\EmployeesListView. Figure 6 shows the Employees List view.
Figure 6
Employees List view
When an employee is selected, the view raises an event named EmployeeSelected. This event is handled by the Employees controller, which, in turn, loads the Employees Details view corresponding to the selected employee into the DetailsRegion region. For more details about the Employees controller, see "Employees Controller" later in this topic.
Note
The Employees List view is not loaded into a region; instead, it is directly added to the HeaderPanelContentControl control by the Employees view's presenter (located at UIComposition.Modules.Employee.{Technology}\Views\EmployeesView\EmployeesPresenter.cs). In this case, a region is not a requirement because there is a single instance of this view during the application lifetime, and no views, other than the Employees List view, are added or removed to the HeaderPanel control, so it is not an extensibility point. The Employees view is in fact a composite view. For more information, see "Composite Views" in the Shell and View technical concept.
Employees Details View
The Employees Details view displays detailed information for a single employee, as illustrated in Figure 7.
Figure 7
Employees Details view
This view contains a TabControl control, which is used as a region. By default, the TabControl contains two tabs (defined in the view's XAML markup code): one tab displays the employee's general information, and the other tab shows the employee's location in Live Search maps. Because the TabControl is a region, it acts as an extension point for the view because child views can be dynamically added in a decoupled way. To demonstrate this, in this region, an instance of the Project List view is dynamically added when the user selects an employee in the Employees List view.
Note
Notice that the Silverlight version of the QuickStart does not include the Location tab in the Employees Details view, because Silverlight does not support the Frame control, which is used for navigation.
The following code shows the XAML code of the view where the TabControl is defined.
<StackPanel>
<TabControl x:Name="DetailsTabControl" cal:RegionManager.RegionName="{x:Static local:RegionNames.TabRegion}">
<TabControl.ItemContainerStyle>
...
</TabControl.ItemContainerStyle>
<TabItem x:Name="GeneralTabItem" Header="General">
...
</TabItem>
<TabItem Header="Location">
...
</TabItem>
</TabControl>
</StackPanel>
The view's implementation is located in the folder UIComposition.Modules.Employee.{Technology}\Views\EmployeesDetailsView.
Note
When you add an item in a TabControl control, this item is wrapped inside a TabItem type. This is not applicable for Silverlight, where you must manually wrap the item into the TabItem. Because of this, the TabControlRegionAdapter adapter was modified to both adapt a region to the TabControl control and to automatically add this TabItem wrapping.
Projects List View
The Projects List view shows the projects on which an employee is currently working. This view is dynamically loaded inside the TabRegion region of the Employee Details view when an employee is selected in the Employees List view. Figure 8 illustrates the Projects List view.
Figure 8
Projects List view
Employees Controller
This class manages the interaction of the Employees module's views and is implemented in the file UIComposition.Modules.Employee.{Technology}\Controllers\EmployeesController.cs. The class's main logic resides in the OnEmployeeSelected method, as shown in the following code. This method is the event handler for the EmployeeSelected event raised by the Employees List view when the user selects an employee.
public virtual void OnEmployeeSelected(BusinessEntities.Employee employee)
{
IRegion detailsRegion = regionManager.Regions[RegionNames.DetailsRegion];
object existingView = detailsRegion.GetView(employee.EmployeeId.ToString(CultureInfo.InvariantCulture));
if (existingView == null)
{
IProjectsListPresenter projectsListPresenter = this.container.Resolve<IProjectsListPresenter>();
projectsListPresenter.SetProjects(employee.EmployeeId);
IEmployeesDetailsPresenter detailsPresenter = this.container.Resolve<IEmployeesDetailsPresenter>();
detailsPresenter.SetSelectedEmployee(employee);
IRegionManager detailsRegionManager = detailsRegion.Add(detailsPresenter.View, employee.EmployeeId.ToString(CultureInfo.InvariantCulture), true);
IRegion region = detailsRegionManager.Regions[RegionNames.TabRegion];
region.Add(projectsListPresenter.View, "CurrentProjectsView");
detailsRegion.Activate(detailsPresenter.View);
}
else
{
detailsRegion.Activate(existingView);
}
}
In the preceding code, the DetailsRegion region is obtained, and then an Employees Details view corresponding to the selected employee is retrieved from the region. If the view exists, it means that the employee has been previously selected; therefore, that view is activated (see the else statement). If the view does not exist, it needs to be created and shown in the user interface. The following lines (extracted from the if statement of the preceding code) contain the logic required to create and show the Employees Details view for the selected employee.
IProjectsListPresenter projectsListPresenter = this.container.Resolve<IProjectsListPresenter>();
projectsListPresenter.SetProjects(employee.EmployeeId);
IEmployeesDetailsPresenter detailsPresenter = this.container.Resolve<IEmployeesDetailsPresenter>();
detailsPresenter.SetSelectedEmployee(employee);
IRegionManager detailsRegionManager = detailsRegion.Add(detailsPresenter.View, employee.EmployeeId.ToString(), true);
IRegion region = detailsRegionManager.Regions[RegionNames.TabRegion];
region.Add(projectsListPresenter.View, "CurrentProjectsView");
detailsRegion.Activate(detailsPresenter.View);
In the preceding code, first, a presenter of the Project List view is resolved and the projects list is initialized for the selected employee. This view will be added later to the TabRegion region of the Employees Details view. Second, a presenter for the Employee Details view is resolved and the selected employee is passed to it. After that, the Employee Details view is added to the DetailsRegion region, with the employee's identifier as the view's name. By associating the employee's identifier with the view, the view can be later looked up in the region to display it if the employee is selected again.
The following line of code shows where the Employee Details view is added to the DetailsRegion region.
IRegionManager detailsRegionManager = detailsRegion.Add(detailsPresenter.View, employee.EmployeeId.ToString(), true);
Note that in the preceding code, the createRegionManagerScope parameter is set to true. This means that the added view defines a new region scope. As a result, all the regions registered by the view and child views are not registered in the Shell's region manager; instead, regions are registered in a new region manager associated to the view. In the preceding code, a reference to this new region manager is stored in the detailsRegionManager variable.
Finally, the TabRegion region of the recently created Employees Details view is obtained through the detailsRegionManager, the Projects List view is added to it, and the Employee Details view is activated. The following code shows how this logic is implemented.
IRegion region = detailsRegionManager.Regions[RegionNames.TabRegion];
region.Add(projectsListPresenter.View, "CurrentProjectsView");
detailsRegion.Activate(detailsPresenter.View);
Note
You can share context from a parent view that hosts a region to its child views. This can be done by using the RegionContext attached property. For more information, see the RegionContext section in the View Discovery Composition QuickStart.
Acceptance Tests
The View Injection Composition QuickStart includes a separate solution that includes acceptance tests. The acceptance tests describe how the application should perform when you follow a series of steps—you can use the acceptance tests to explore the functional behavior of the application in a variety of scenarios.
Some acceptance tests were developed using the testing framework White. To run these tests, you need to have White assemblies. For more information about White, including download information, see White on CodePlex.
Note
The acceptance tests have been developed and verified with the White 0.1.5.0 release. Although other releases of White might work, it is recommended to use this release to avoid any issues when running the tests.
To run the View Injection Composition QuickStart acceptance tests
- Place the assemblies required by White in the folder Source\Lib\White. The files are the following:
- Bricks.dll
- Bricks.RuntimeFramework.dll
- Castle.Core.dll
- Castle.DynamicProxy2.dll
- Core.dll
- log4net.config
- log4net.dll
- nunit.framework.dll
- White.NUnit.dll
- Xstream.Core.dll
- In Visual Studio, open the solution file Quickstarts\Visual Composition\VisualComposition.Tests.AcceptanceTest\VisualComposition.Tests.AcceptanceTest.sln.
- Right-click VisualComposition.Tests.AcceptanceTest, and then click Set as StartUp Project.
- Press F5.
Outcome
You should see the QuickStart window and the tests automatically interact with the application. At the end of the test pass, you should see that all tests have passed.
More Information
To learn about other QuickStarts included with the Composite Application Guidance, see the following topics: