Learn Prism WPF View Composition

49 minute read

Learn the WPF View Composition (https://prismlibrary.com/docs/wpf/view-composition.html) section from the PRISM LIBRARY Documentation.

It’s a translation of that, but as a memo.
If you don’t speak Japanese, go back to English and understand it. .. ..

Creating a user interface using Prism Library for WPF

image.png

Composing the User Interface Using the Prism Library for WPF

Creating a user interface using Prism Library for WPF

image.png

A composite application user interface (UI) is composed from loosely coupled visual components known as views that are typically contained in the application modules, but they do not need to be.
If you divide your application into modules, you need some way to loosely compose the UI, but you might choose to use this approach even if the views are not in modules.
To the user, the application presents a seamless user experience and delivers a fully integrated application.

The composite application user interface (UI) consists of loosely coupled visual components called views.
These are usually included in the application module, but they don’t have to be.
If you want to split your application into modules, you need some way to roughly configure the UI, but you may choose to use this approach even if the view is not inside the module.
For users, the application provides a seamless user experience and a fully integrated application.

image.png

To compose your UI, you need an architecture that allows you to create a layout composed of loosely coupled visual elements generated at run time.
Additionally, the architecture should provide strategies for these visual elements to communicate in a loosely coupled fashion.

To configure the UI, you need an architecture that allows you to create layouts that consist of loosely coupled visual elements that are generated at run time.
In addition, the architecture needs to provide a strategy for these visual elements to communicate in a loosely coupled manner.

image.png

An application UI can be built by using one of the following paradigms:

・ All required controls for a form are contained in a single Extensible Application Markup Language(XAML) file, composing the form at design time.

・ Logical areas of the form are separated into distinct parts, typically user controls.
The parts are referenced by the form, and the form is composed at design time.

・ Logical areas of the form are separated into distinct parts, typically user controls.
The parts are unknown to the form and are dynamically added to the form at run time.
Applications that use this methodology are known as composite applications using UI composition patterns.

The application UI can be built using one of the following paradigms:

· All the controls required for a form are contained in a single Extensible Application Markup Language (XAML) file that configures the form at design time.

· The logical area of the form is divided into separate parts, which are usually user controls.
Parts are referenced by forms, which are constructed at design time.

· The logical area of the form is divided into separate parts, which are usually user controls.
The part is not recognized by the form and is dynamically added to the form at run time.
Applications that use this methodology are called composite applications that use UI configuration patterns.

image.png

Below is a picture of an app.
It is composed by loading multiple views that come from different modules into regions exposed by the shell, as shown in the following illustration.

Below is a photo of the app.
It consists of loading multiple views from different modules into the area exposed by the shell, as shown in the following figure.

image.png

UI Layout Concepts

image.png

The shell is typically the main application window and it is comprised of regions.
Regions are mostly contained within ContentControl's, ItemControl's and TabControl's, and the shell has no knowledge of what is implemented in the region.
Inside the region are views.
The view is the implementation of a specific portion of the UI and is decoupled from other parts of the application.

The following sections introduce the high-level core concepts for composite application development.

The shell is usually the main application window and consists of regions.
Regions are primarily contained in ContentControl, ItemControl, and TabControl, and the shell is unaware of what is implemented in the region.
There is a view inside the area.
Views are implementations of specific parts of the UI that are separate from the rest of the application.

The next section introduces the high-level core concepts of composite application development.

Shell
image.png

The shell is the application root object that contains the primary UI content.
In a Windows Presentation Foundation (WPF) application, the shell is the Window object.

The shell plays the role of a master page providing the layout structure for the application.
The shell contains one or more named regions where modules can specify the views that will appear.
It can also define certain top-level UI elements, such as the background, main menu, and toolbar.

The shell is the application root object that contains the primary UI content.
In Windows Presentation Foundation (WPF) applications, the shell is a Window object.

The shell acts as a master page that provides the layout structure for your application.
The shell contains one or more named areas where you can specify the view in which the module will be displayed.
You can also define specific top-level UI elements such as backgrounds, main menus, and toolbars.

image.png

The shell defines the overall appearance of the application.
It might define styles and borders that are present and visible in the shell layout itself, and it might also define styles, templates, and themes that will be applied to the views that are plugged into the shell.

Typically, the shell is a part of the WPF application project.
The assembly that contains the shell might or might not reference the assemblies that contain the views to be loaded in the shell's regions.

The shell defines the overall look of your application.
You can also define the styles and borders that exist in the shell layout itself and are displayed, as well as the styles, templates, and themes that apply to the views that are plugged into the shell.

The shell is usually part of a WPF application project.
An assembly that contains a shell may or may not reference an assembly that contains a view that is loaded into the area of the shell.

Views
image.png

Views are the main unit of UI construction within a composite application.
You can define a view as a user control, page, data template, or custom control.
A view encapsulates a portion of your UI that you would like to keep as decoupled as possible from other parts of the application.
You can choose what goes in a view based on encapsulation or a piece of functionality, or you can choose to define something as a view because you will have multiple instances of that view in your application.

Because of the content model of WPF, there is nothing specific to the Prism Library required to define a view.
The easiest way to define a view is to define a user control.
To add a view to the UI, you simply need a way to construct it and add it to a container.
WPF provides mechanisms to do this. The Prism Library adds the ability to define a region into which a view can be dynamically added at run time.

Views are the primary unit of UI construction within a composite application.
Views can be defined as user controls, pages, data templates, or custom controls.
The view encapsulates the part of the UI that you want to keep as separate as possible from the rest of your application.
You can choose the content of the view based on encapsulation or part of its functionality, or you can choose to define something as a view because your application has multiple instances of that view.

Due to the WPF content model, there is nothing specific to the prism library required to define a view.
The easiest way to define a view is to define a user control.
To add a view to the UI, you need a way to create the view and add it to the container.
WPF provides a mechanism to do this. The Prism Library adds the ability to define areas where views can be dynamically added at run time.

Composite Views
image.png

A view that supports specific functionality can become complicated.
In that case, you might want to divide the view into several child views and have the parent view handle constructing itself by using the child views as parts.
The application might do this statically at design time, or it might support having modules add child views through a contained region at run time.
When you have a view that is not fully defined in a single view class, you can refer to that as a composite view.
In many situations, a composite view is responsible for constructing the child views and for coordinating the interactions between them.
You can design child views that are more loosely coupled from their sibling views and their parent composite view by using the Prism Library commands and the event aggregator.

Views that support certain features can be complex.
In that case, you can split the view into several child views and use the child views as parts to build the parent view handle itself.
Your application may support doing this statically at design time or letting the module add child views through the space it contains at run time.
If you have a view that is not fully defined in a single view class, you can reference that view as a composite view.
In many situations, composite views are responsible for creating child views and coordinating the interactions between them.
You can use Prism Library commands and event aggregators to design loosely coupled child views from sibling views and their parent composite views.

Regions
image.png

Regions are enabled in the Prism Library through a region manager, regions, and region adapters.

The Prism Library enables regions through Region Manager, Regions, and Region Adapters.

Region Manager
image.png

The RegionManager class is responsible for creating and maintaining a collection of regions for the host controls. 
The RegionManager uses a control-specific adapter that associates a new region with the host control.
The following illustration shows the relationship between the region, control, and adapter set up by the RegionManager.

The RegionManager class is responsible for creating and maintaining a collection of regions for host controls.
RegionManager uses a control-specific adapter that associates the new region with the host control.
The following figure shows the relationships between regions, controls, and adapters set by RegionManager.

image.png

image.png

The RegionManager can create regions in code or in XAML.
The RegionManager.RegionName attached property is used to create a region in XAML by applying the attached property to the host control.

Applications can contain one or more instances of a RegionManager.
You can specify the RegionManager instance into which you want to register the region.
This is useful if you want to move the control around in the visual tree and do not want the region to be cleared when the attached property value is removed.

The RegionManager provides a RegionContext attached property that permits its regions to share data.

RegionManager can create regions in code or in XAML.
Create a region in XAML by using the RegionManager.RegionName attached property and applying the attached property to the host control.

Your application can contain one or more instances of RegionManager.
You can specify a RegionManager instance to register the region with.
This is useful if you need to move the control within the visual tree so that the area is not cleared when the attached property value is deleted.

RegionManager provides RegionContext attachment properties that allow regions to share data.

Region Implementation
image.png

A region is a class that implements the IRegion interface.
The term region represents a container that can hold dynamic data that is presented in a UI.
A region allows the Prism Library to place dynamic content contained in modules in predefined placeholders in a UI container.

Regions can hold any type of UI content.
A module can contain UI content presented as a user control, a data type that is associated with a data template, a custom control, or any combination of these.
This lets you define the appearance for the UI areas and then have modules place content in these predetermined areas.

A region is a class that implements the IRegion interface.
The term area refers to a container that can hold the dynamic data displayed in the UI.
Regions allow the Prism Library to place dynamic content contained in modules in predefined placeholders within UI containers.

Regions can hold any type of UI content.
Modules can contain user controls, data types associated with data templates, custom controls, or UI content that appears as any combination of these.
This allows you to define the appearance of UI areas and have the module place content in these predefined areas.

image.png

A region can contain zero or more items.
Depending on the type of host control the region is managing, one or more of the items could be visible.
For example, a ContentControl can display only a single object.
However, the region in which it is located can contain many items, and an ItemsControl can display multiple items.
This allows each item in the region to be visible in the UI.

In the following illustration, the sample app shell contains four regions: MainRegion, MainToolbarRegion, ResearchRegion, and ActionRegion.
These regions are populated by the various modules in the application—the content can be changed at any time.

A region can contain zero or more items.
You may see one or more items, depending on the type of host control that the region manages.
For example, ContentControl can only display one object.
However, the area where it is located can contain many items, and the ItemsControl can display multiple items.
This allows you to display each item in the area in the UI.

In the following figure, the sample app shell contains four regions: MainRegion, MainToolbarRegion, ResearchRegion, and ActionRegion.
The various modules of the application are populated in these areas. You can change the content at any time.

image.png

image.png

Module User Control to Region Mapping
To demonstrate how modules and content are associated with regions, see the following illustration.
It shows the association of WatchModule and the NewsModule with the corresponding regions in the shell.

The MainRegion contains the WatchListView user control, which is contained in the WatchModule.
The ResearchRegion also contains the ArticleView user control, which is contained in the NewsModule.
Module user control to area mapping

See the following figure to show how modules and content are associated with regions.
This shows the association between the WatchModule and NewsModule and the corresponding areas in the shell.

The MainRegion contains the WatchListView user control included in the WatchModule.
The ResearchRegion also includes the ArticleView user control included in the NewsModule.

image.png

In applications created with the Prism Library, mappings like this will be a part of the design process because designers and developers use them to determine what content is proposed to be in a specific region.
This allows designers to determine the overall space needed and any additional items that must be added to ensure that the content will be viewable in the allowable space.

For applications created with the Prism Library, such mappings are part of the design process. This is because designers and developers use them to determine what content is suggested to be in a particular area.
This allows the designer to determine the overall space required and the additional items that need to be added to ensure that the content is displayed in the space allowed.

image.png

Default Region Functionality
image.png

While you do not need to fully understand region implementations to use them, it might be useful to understand how controls and regions are associated and the default region functionality:
for example, how a region locates and instantiates views, how views can be notified when they are the active view, or how view lifetime can be tied to activation.

The following sections describe the region adapter and region behaviors.

You do not need to have a complete understanding of the region implementation to use them, but it can be helpful to understand how controls and regions are related and how the default regions work.
For example, how a region finds and instantiates a view, notifies the view when the view becomes an active view, and associates the lifetime of the view with activation.

The next section describes how region adapters and regions work.
image.png

Region Adapter
To expose a UI control as a region, it must have a region adapter.
Region adapters are responsible for creating a region and associating it with the control.
This allows you to use the IRegion interface to manage the UI control contents in a consistent way.
Each region adapter adapts a specific type of UI control. The Prism Library provides the following three region adapters:

・ ContentControlRegionAdapter. 
This adapter adapts controls of type System.Windows.Controls.ContentControl and derived classes.
・ SelectorRegionAdapter.
This adapter adapts controls derived from the class System.Windows.Controls.Primitives.Selector, such as the System.Windows.Controls.TabControl control.
・ ItemsControlRegionAdapter.
This adapter adapts controls of type System.Windows.Controls.ItemsControl and derived classes.

Region Adapter
A region adapter is required to expose the UI control as a region.
The region adapter creates a region and associates it with a control.
This allows you to manage the content of UI controls in a consistent way using the IRegion interface.
Each region adapter adapts a particular type of UI control. The Prism Library offers three region adapters:

-ContentControlRegionAdapter.
This adapter matches controls and derived classes of type System.Windows.Controls.ContentControl.
-SelectorRegionAdapter.
This adapter adapts controls derived from the System.Windows.Controls.Primitives.Selector class, such as the System.Windows.Controls.TabControl control.
-ItemsControlRegionAdapter.
This adapter matches controls and derived classes of type System.Windows.Controls.ItemsControl.

image.png

Region Behaviors
The Prism Library introduces the concept of region behaviors.
These are pluggable components that give a region most of its functionality.
Region behaviors were introduced to support view discovery and region context (described later in this topic).
Additionally, behaviors provide an effective way to extend a region's implementation.

Region Behaviors
The Prism Library introduces the concept of region behavior.
These are pluggable components that provide most of the functionality of a region.
Region behavior was introduced to support view discovery and region context (discussed later in this topic).
In addition, the behavior provides an effective way to extend the implementation of a region.

image.png

A region behavior is a class that is attached to a region to give the region additional functionality.
This behavior is attached to the region and remains active for the lifetime of the region.
For example, when an AutoPopulateRegionBehavior is attached to a region, it automatically instantiates and adds any ViewTypes that are registered against regions with that name.
For the lifetime of the region, it keeps monitoring the RegionViewRegistry for new registrations.
It is easy to add custom region behaviors or replace existing behaviors, either on a system-wide or a per-region basis.

The next sections describe the default behaviors that are automatically added to all regions.
One behavior, the SelectorItemsSourceSyncBehavior, is only attached to controls that derive from the Selector.

Region behavior is a class that is attached to a region to provide additional functionality to the region.
This behavior is associated with the region and remains active for the life of the region.
For example, when an AutoPopulateRegionBehavior is attached to a region, all ViewTypes registered for the region with that name are automatically instantiated and added.
Monitor the RegionViewRegistry for new registrations for the life of the region.
It’s easy to add custom region behavior or replace existing behavior for the entire system or for each region.

The next section describes the default behavior that is automatically added to all regions.
One behavior, SelectorItemsSourceSyncBehavior, is attached only to controls derived from selectors.

image.png

Registration Behavior
The RegionManagerRegistrationBehavior is responsible for making sure that the region is registered to the correct RegionManager.
When a view or control is added to the visual tree as a child of another control or region, any region defined in the control should be registered in the RegionManager of the parent control.
When the child control is removed, the registered region is unregistered.

Registration Behavior
The RegionManagerRegistrationBehavior is responsible for ensuring that the region is registered with the correct RegionManager.
If a view or control is added to the visual tree as a child of another control or region, the region defined by the control must be registered with the parent control’s RegionManager.
When the child control is deleted, the registered area is unregistered.
image.png

Auto-Population Behavior
There are two classes responsible for implementing view discovery.
One of them is the AutoPopulateRegionBehavior.
When it is attached to a region, it retrieves all view types that are registered under the name of the region.
It then creates instances of those views and adds them to the region.
After the region is created, the AutoPopulateRegionBehavior monitors the RegionViewRegistry for any newly registered view types for that region name.

If you want to have more control over the view discovery process, consider creating your own implementation of the IRegionViewRegistry and the AutoPopulateRegionBehavior.

Auto-Population Behavior
There are two classes responsible for implementing view detection.
One of them is the AutoPopulateRegionBehavior.
If attached to a region, get all view types registered with the name of the region.
Then instantiate those views and add them to the region.
After the region is created, AutoPopulateRegionBehavior monitors the RegionViewRegistry for the newly registered view type for that region name.

If you want more control over the view discovery process, consider creating your own implementation of the IRegionViewRegistry and AutoPopulateRegionBehavior.

image.png

Region Context Behaviors
The region context functionality is contained within two behaviors: the SyncRegionContextWithHostBehavior and the BindRegionContextToDependencyObjectBehavior.
These behaviors are responsible for monitoring changes to the context that were made on the region, and then synchronizing the context with a context dependency property attached to the view.

Region Context Behaviors
The Region Context feature is included in two behaviors: SyncRegionContextWithHostBehavior and BindRegionContextToDependencyObjectBehavior.
These actions are responsible for monitoring changes to the context made in the region and synchronizing the context with the context dependency properties associated with the view.

image.png

Activation Behavior
The RegionActiveAwareBehavior is responsible for notifying a view if it is active or inactive.
The view must implement IActiveAware to receive these change notifications.
This active aware notification is one-directional (it travels from the behavior to the view).
The view cannot affect its active state by changing the active property on the IActiveAware interface.

Activation Behavior
RegionActiveAwareBehavior tells the view whether the view is active or inactive.
You must implement IActiveAware in your view to receive these change notifications.
This active-enabled notification is one-way (goes from behavior to view).
Changing the active property of the IActiveAware interface does not affect the active state of the view.
image.png

Region Lifetime Behavior
The RegionMemberLifetimeBehavior is responsible for determining if an item should be removed from the region when it is deactivated.
The RegionMemberLifetimeBehavior monitors the region's ActiveViews collection to discover items that transition into a deactivated state.
The behavior checks the removed items for IRegionMemberLifetime or the RegionMemberLifetimeAttribute (in that order) to determine if it should be kept alive on removal.

Region Lifetime Behavior
RegionMemberLifetimeBehavior determines if an item should be removed from the region when it is deactivated.
RegionMemberLifetimeBehavior monitors the region’s ActiveViews collection to detect items that transition to an inactive state.
This behavior checks the deleted items in the IRegionMemberLifetime or RegionMemberLifetimeAttribute (in that order) to determine if they should be retained after being deleted.

image.png

If the item in the collection is a System.Windows.FrameworkElement, it will also check its DataContext for IRegionMemberLifetime or the RegionMemberLifetimeAttribute.

The region items are checked in the following order:

1. IRegionMemberLifetime.KeepAlive value
2. DataContext's IRegionMemberLifetime.KeepAlive value
3. RegionMemberLifetimeAttribute.KeepAlive value
4. DataContext's RegionMemberLifetimeAttribute.KeepAlive value

If the item in the collection is System.Windows.FrameworkElement, check the IRegionMemberLifetime or RegionMemberLifetimeAttribute in that DataContext.

Region items are checked in the following order:

  1. IRegionMemberLifetime.KeepAlive value
  2. DataContext IRegionMemberLifetime.KeepAlive value
  3. RegionMemberLifetimeAttribute.KeepAlive value
  4. DataContext RegionMemberLifetimeAttribute.KeepAlive value

image.png

Control-Specific Behaviors
The SelectorItemsSourceSyncBehavior is used only for controls that derive from Selector, such as a tab control in WPF.
It is responsible for synchronizing the views in the region with the items of the selector, and then synchronizing the active views in the region with the selected items of the selector.

Control-Specific Behaviors
SelectorItemsSourceSyncBehavior is only used for selector-derived controls, such as WPF tab controls.
Synchronizes the views in the region with the selector’s items and the active views in the region with the selector’s selected items.

Extending the Region Implementation
Region implementation extensions
image.png

The Prism Library provides extension points that allow you to customize or extend the default behavior of the provided APIs.
For example, you can write your own region adapters, region behaviors, or change the way the Navigation API parses URIs.

The Prism Library provides extension points where you can customize or extend the default behavior of the provided APIs.
For example, you can describe your own region adapter or region behavior, or change the way the Navigation API parses URIs.

View Composition
image.png

View composition is the constructing of a view. In composite applications, views from multiple modules have to be displayed at run time in specific locations within the application UI.
To achieve this, you need to define the locations where the views will appear and how the views will be created and displayed in those locations.

Views can be created and displayed in the locations either automatically through view discovery, or programmatically through view injection.
These two techniques determine how individual views are mapped to named locations within the application UI.

View composition is the construction of a view. Composite applications need to display views of multiple modules at specific locations within the application UI at run time.
To achieve this, you need to define where the views are displayed and how they are created and displayed.

Views can be created and displayed in locations automatically through view discovery or programmatically through view insertion.
These two techniques determine how individual views are mapped to named locations in the application UI.

View Discovery
image.png

In view discovery, you set up a relationship in the RegionViewRegistry between a region's name and the type of a view.
When a region is created, the region looks for all the ViewTypes associated with the region and automatically instantiates and loads the corresponding views.
Therefore, with view discovery, you do not have explicit control over when the views that correspond to a region are loaded and displayed.

For view discovery, the RegionViewRegistry sets the relationship between the region name and the view type.
When a region is created, it looks for all ViewTypes associated with that region and automatically instantiates and loads the corresponding views.
Therefore, view discovery does not give you explicit control over when the view corresponding to a region is loaded and displayed.

View Injection
image.png

In view injection, your code obtains a reference to a region, and then programmatically adds a view into it.
Typically, this is done when a module initializes or as a result of a user action.
Your code will query a RegionManager for a specific region by name and then inject views into it.
With view injection, you have more control over when views are loaded and displayed.
You also have the ability to remove views from the region. However, with view injection, you cannot add a view to a region that has not yet been created.

In view injection, the code gets a reference to the area and programmatically adds the view to the area.
This is typically done when the module is initialized or as a result of a user action.
The code queries RegionManager by name for a particular area and inserts a view into that area.
View injection gives you more control over when a view is loaded and displayed.
You can also remove the view from the region. However, inserting a view does not allow you to add a view to an area that has not yet been created.

Navigation
image.png

The Prism Library 7.1 contains Navigation APIs.
The Navigation APIs simplify the view injection process by allowing you to navigate a region to an URI.
The Navigation API instantiates the view, adds it to the region, and then activates it.
Additionally, the Navigation API allows navigating back to a previously created view contained in a region. For more information about the Navigation APIs, see Navigation.

Prism Library 7.1 includes a navigation API.
The Navigation API simplifies the view injection process by allowing you to navigate regions to URIs.
The Navigation API instantiates the view, adds it to the region, and then activates it.
In addition, you can use the Navigation API to return to the previously created view contained in the area. For more information on the navigation API, see Navigation.
When to Use View Discovery vs. View Injection

image.png

hoosing which view loading strategy to use for a region depends on the application requirements and the function of the region.

Use view discovery in the following situations:
・ Automatic view loading is desired or required.
・ Single instances of a view will be loaded into the region.

Use view injection in the following situations:
・ Your application uses the Navigation APIs.
・ You need explicit or programmatic control over when a view is created and displayed, or you need to remove a view from a region; for example, as a result of application logic or navigation.
・ You need to display multiple instances of the same views in a region, where each view instance is bound to different data.
・ You need to control which instance of a region a view is added to. For example, you want to add a customer detail view to a specific customer detail region. (This scenario requires implementing scoped regions as described later in this topic.)

The choice of view loading strategy to use for a region depends on your application requirements and the capabilities of the region.

Use View Discovery in the following situations:
-Automatic view loading is desired or required.
· A single instance of the view is loaded into the region.

Use View Injection in the following situations:
-Your application uses the navigation API.
· You need to explicitly or programmatically control when a view is created and displayed, or you need to remove a view from an area. For example, as a result of application logic or navigation.
· You need to display multiple instances of the same view in a region. Each view instance is bound to different data.
· You need to control the instance of the region to which you want to add the view. For example, suppose you want to add a customer detail view to a specific customer detail area. (In this scenario, you need to implement a scope area, as described later in this topic.)

UI Layout Scenarios
image.png

In composite applications, views from multiple modules are displayed at run time in specific locations within the application UI.
To achieve this, you need to define the locations where the views will appear and how the views will be created and displayed in those locations.

The decoupling of the view and the location in the UI in which it will be displayed allows the appearance and layout of the application to evolve independently of the views that appear within the region.

The next sections describe the core scenarios you will encounter when you develop a composite application.

In composite applications, views of multiple modules are displayed at specific locations within the application UI at run time.
To achieve this, you need to define where the views are displayed and how they are created and displayed.

By separating the view from its location in the UI, you can expand the look and layout of your application independently of the view displayed in the area.

The following sections describe the key scenarios that occur when developing composite applications.

Implementing the Shell
image.png

The shell is the application root object in which the primary UI content is contained.
In a WPF application, the shell is the Window object.

A shell can contain named regions where modules can specify the views that will appear.
It can also define certain top-level UI elements, such as the main menu and toolbar. The shell defines the overall structure and appearance for the application, and is similar to an ASP.NET master page control.
It could define styles and borders that are present and visible in the shell layout itself, and it could also define styles, templates, and themes that are applied to the views that are plugged into the shell.

The shell is the application root object that contains the primary UI content.
In WPF applications, the shell is a Window object.

The shell can contain a named area where you can specify the view in which the module will be displayed.
You can also define specific top-level UI elements such as main menus and toolbars. The shell defines the overall structure and appearance of your application and is similar to an ASP.NET master page control.
It exists in the shell layout itself and allows you to define the styles and borders that are displayed. You can also define styles, templates, and themes that apply to views connected to the shell.

image.png

You do not need to have a distinct shell as part of your application architecture to use the Prism Library.
If you are building a completely new composite application, implementing a shell provides a well-defined root and initialization pattern for setting up the main UI of your application.
However, if you are adding Prism Library features to an existing application, you do not have to change the basic architecture of your application to add a shell.
Instead, you can alter your existing window definitions or controls to add regions that can pull in views as needed.

You can also have more than one shell in your application.
If your application is designed to open more than one top-level window for the user, each top-level window acts as shell for the content it contains.

You do not need to have a separate shell as part of your application architecture to use the Prism Library.
If you are building a completely new composite application, implementing the shell provides a well-defined route and initialization pattern for setting up the application’s main UI.
However, if you want to add Prism Library functionality to an existing application, you do not need to change the basic architecture of the application to add a shell.
Instead, you can modify an existing window definition or control to add more space to capture the view as needed.

You can also include multiple shells in your application.
If your application is designed to open multiple top-level windows to your users, each top-level window acts as a shell for the content it contains.

Sample Shell
image.png

This sample has a shell as its main window.
In the following illustration, the shell and views are highlighted.
The shell is the main window that appears when the app starts and which contains all the views.
It defines the regions into which modules add their views and a couple of top-level UI items, including the title and the Watch List tear-off banner.

This sample has a shell as the main window.
In the following figure, the shell and view are highlighted.
The shell is the main window that appears when you launch the app and contains all the views.
It defines the area where the module adds views and some top-level UI items such as titles and watchlist tear-off banners.

image.png

image.png

The shell implementation in the app is provided by Shell.xaml, its code-behind file Shell.xaml.cs, and its view model ShellViewModel.cs.
Shell.xaml includes the layout and UI elements that are part of the shell, including definitions of regions to which modules add their views.

The following XAML shows the structure and main XAML elements that define the shell.
Notice that the RegionName attached property is used to define the four regions and that the window background image provides a background for the shell.

The shell implementation of the app is provided by Shell.xaml, its code-behind file Shell.xaml.cs, and the view model ShellViewModel.cs.
Shell.xaml contains layout and UI elements that are part of the shell, such as defining areas to which modules add views.

The following XAML shows the structure that defines the shell and the main XAML elements.
Use the RegionName attached property to define four regions and note that the window background image provides the shell background.

<!--Shell.xaml (WPF) -->
<Window x:Class="StockTraderRI.Shell">

    <!--shell background -->
    <Window.Background>
        <ImageBrush ImageSource="Resources/background.png " Stretch="UniformToFill"/>
    </Window.Background>

    <Grid>

        <!-- logo -->
        <Canvas x:Name="Logo" ...>
            <TextBlock Text="CFI" ... />
            <TextBlock Text="STOCKTRADER" .../>
        </Canvas>

        <!-- main bar -->
        <ItemsControl 
            x:Name="MainToolbar"
            prism:RegionManager.RegionName="{x:Static inf:RegionNames.MainToolBarRegion}"/>

        <!-- content -->
        <Grid>
            <Controls:AnimatedTabControl
                x:Name="PositionBuySellTab"
                prism:RegionManager.RegionName="{x:Static inf:RegionNames.MainRegion}"/>
        </Grid>

        <!-- details -->
        <Grid>
            <ContentControl
                x:Name="ActionContent"
                prism:RegionManager.RegionName="{x:Static inf:RegionNames.ActionRegion}"/>
        </Grid>

        <!-- sidebar -->
        <Grid x:Name="SideGrid">
            <Controls:ResearchControl
                prism:RegionManager.RegionName="{x:Static inf:RegionNames.ResearchRegion}" />
        </Grid>

    </Grid>
</Window>

image.png

The implementation of the Shell code-behind file is very simple.
The Shell is exported so that when your App object creates it, its dependencies will be and added.

Implementing a shell code-behind file is very easy.
The shell is exported, so when the app object creates the shell, its dependencies are added.

// Shell.xaml.cs
[Export]
public partial class Shell : Window
{
    public Shell()
    {
        InitializeComponent();
    }
}

image.png

The minimal code in the code-behind file illustrates the power and simplicity of the composite application architecture and loose coupling between the shell and its constituent views.

The minimal code in the code-behind file demonstrates the power and simplicity of a composite application architecture, and the loose coupling between the shell and its component views.

Defining Regions
image.png

You define where views will appear by defining a layout with named locations, known as regions.
Regions act as placeholders for one or more views that will be displayed at run time.
Modules can locate and add content to regions in the layout without knowing how and where the region is displayed.
This allows the layout to change without affecting the modules that add the content to the layout.

Define where the view is displayed by defining the layout in a named location called a region.
Regions act as placeholders for one or more views that are displayed at run time.
Modules can find and add areas in the layout without knowing how and where they are displayed.
This allows you to change the layout without affecting the modules that add content to the layout.
image.png

Regions are defined by assigning a region name to a WPF control, either in XAML as shown in the previous Shell.xaml file or in code.
Regions can be accessed by their region name. At run time, views are added to the named Region control, which then displays the view or views according to the layout strategy that the views implement.
For example, a tab control region will lay out its child views in a tabbed arrangement.
Regions support the addition or removal of views.
Views can be created and displayed in regions either programmatically or automatically.
In the Prism Library, the former is achieved through view injection and the latter through view discovery.
These two techniques determine how individual views are mapped to the named regions within the application UI.

A region is defined by assigning a region name to a WPF control in the XAML or code shown in the previous Shell.xaml file.
You can access the region by region name. At run time, the view is added to the named region control to display one or more views according to the layout strategy that the view implements.
For example, the tab control area lays out its child views in a tabbed layout.
Regions support adding or removing views.
Views can be created and displayed within an area programmatically or automatically.
In the Prism Library, the former is achieved through view injection and the latter through view discovery.
These two techniques determine how individual views are mapped to named areas in the application UI.

image.png

The shell of the application defines the application layout at the highest level;
for example, by specifying the locations for the main content and the navigation content, as shown in the following illustration.
Layout within these high-level views is similarly defined, allowing the overall UI to be recursively composed.

The application shell defines the layout of the application at the highest level.
For example, specify the location of the main and navigation content, as shown in the following figure.
Layouts within these high-level views are defined as well, allowing you to recursively configure the entire UI.
image.png

image.png

Regions are sometimes used to define locations for multiple views that are logically related.
In this scenario, the region control is typically an ItemsControl-derived control that will display the views according to the layout strategy that it implements, such as in a stacked or tabbed layout arrangement.

Regions can also be used to define a location for a single view;
for example, by using a ContentControl. In this scenario, the region control displays only one view at a time, even if more than one view is mapped to that region location.

Regions may be used to define the location of multiple logically related views.
In this scenario, the area control is typically a control derived from the ItemsControl and displays the view according to the layout strategy you implement, such as a stacked or tabbed layout arrangement.

Regions can also be used to define the location of a single view.
For example, use ContentControl. In this scenario, the region control displays only one view at a time, even if multiple views are mapped to the location of the region.

Sample App Shell Regions
image.png

image.png

A multiple-view layout is also demonstrated in the example app ui when the application is buying or selling a stock.
The Buy/Sell area is a list-style region that shows multiple buy/sell views (OrderCompositeView) as part of its list, as shown in the following illustration.

The multi-view layout is also shown in the sample app ui when the application buys and sells stocks.
The purchase / sale area is a list-style area that displays multiple OrderCompositeViews as part of the list, as shown in the following figure.
image.png

image.png

The shell's ActionRegion contains the OrdersView.
The OrdersView contains the Submit All and Cancel All buttons as well as the OrdersRegion.
The OrdersRegion is attached to a ListBox control which displays multiple OrderCompositeViews.

The shell’s Action Region contains an OrdersView.
The OrdersView contains the Submit All and Cancel All buttons, as well as the Orders Region.
OrdersRegion is attached to a ListBox control that displays multiple OrderCompositeViews.

Adding a Region in XAML
image.png

A region is a class that implements the IRegion interface.
The region is the container that holds content to be displayed by a control.

The RegionManager supplies an attached property that you can use for simple region creation in XAML.
To use the attached property, you must load the Prism Library namespace into the XAML and then use the RegionName attached property.
The following example shows how to use the attached property in a window with an AnimatedTabControl.

Notice the use of the x:Static markup extension to reference the MainRegion string constant.
This practice eliminates magic strings in the XAML.

A region is a class that implements the IRegion interface.
A region is a container that holds the content displayed by the control.

RegionManager provides attached properties that you can use to create simple regions in XAML.
To use the attached property, you must load the Prism Library namespace into XAML and then use the RegionName attached property.
The following example shows how to use the attached property in a window with AnimatedTabControl.

Note the use of the x: Static markup extension to refer to the MainRegion string constant.
This will remove the XAML magic string.

<!-- (WPF) -->
<Controls:AnimatedTabControl 
    x:Name="PositionBuySellTab"
    prism:RegionManager.RegionName="{x:Static inf:RegionNames.MainRegion}"/>

Adding a Region by Using Code
image.png

The RegionManager can register regions directly without using XAML.
The following code example shows how to add a region to a control from the code-behind file.
First a reference to the region manager is obtained.
Then, using the RegionManager static methods SetRegionManager and SetRegionName, the region is attached to the UI's ActionContent control and then that region is named ActionRegion.

RegionManager can register regions directly without using XAML.
The following code example shows how to add space to a control from a code-behind file.
First, you get a reference to the space manager.
Then use the RegionManager static methods SetRegionManager and SetRegionName to attach the region to the ActionContent control in the UI and name the region ActionRegion.

IRegionManager regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
RegionManager.SetRegionManager(this.ActionContent, regionManager);
RegionManager.SetRegionName(this.ActionContent, "ActionRegion");

Displaying Views in a Region When the Region Loads
image.png

With the view discovery approach, modules can register views (view models or presentation models) for a specific named location.
When that location is displayed at run time, any views that have been registered for that location will be created and displayed within it automatically.

Modules register views with a registry.
The parent view queries this registry to discover the views that were registered for a named location.
After they are discovered, the parent view places those views on the screen by adding them to the placeholder control.

After the application is loaded, the composite view is notified to handle the placement of new views that have been added to the registry.

The following illustration shows the view discovery approach.

The view detection approach allows a module to register a view (view model or presentation model) of a particular named location.
When the location is displayed at run time, all views registered at that location are automatically created and displayed in it.

The module registers the view in the registry.
The parent view queries this registry to find the view registered in the specified location.
After they are detected, the parent view places them on the screen by adding them to the placeholder control.

After the application is loaded, the composite view is notified to handle the placement of the new view added to the registry.

The following figure shows the view detection approach.
image.png

image.png

The Prism Library defines a standard registry, RegionViewRegistry, to register views for these named locations.

To show a view in a region, register the view with the region manager, as shown in the following code example.
You can directly register a view type with the region, in which case the view will be constructed by the dependency injection container and added to the region when the control hosting the region is loaded.

The Prism Library defines a standard registry called RegionViewRegistry to register views for these named locations.

To view the views in a region, register the views with the Region Manager, as shown in the following code example.
You can register the view type directly in the area. In that case, the view is built by the dependency injection container and added to the region when the control that hosts the region is loaded.

// View discovery
this.regionManager.RegisterViewWithRegion("MainRegion", typeof(EmployeeView));

image.png

Optionally, you can provide a delegate that returns the view to be shown, as shown in the next example.
The region manager will display the view when the region is created.

You can optionally provide a delegate that returns the view you want to display, as shown in the following example.
Once the region is created, the Region Manager displays the view.

// View discovery
this.regionManager.RegisterViewWithRegion("MainRegion", () => this.container.Resolve<EmployeeView>());

Displaying Views in a Region Programmatically
image.png

In the view injection approach, views are programmatically added or removed from a named location by the modules that manage them.
To enable this, the application contains a registry of named locations in the UI.
A module can use the registry to look up one of the locations and then programmatically inject views into it.
To make sure that locations in the registry can be accessed similarly, each of the named locations adheres to a common interface used to inject the view.
The following illustration shows the view injection approach.

538/5000
In the view injection approach, views are programmatically added or removed from the named location by the module that manages the view.
To make this possible, the application includes a registry of named locations within the UI.
The module can use the registry to find one of the locations and programmatically insert the view.
To ensure that you can access the locations in the registry as well, each named location conforms to the common interface used to insert views.
The following figure shows the view injection approach.
image.png

image.png

The Prism Library defines a standard registry, RegionManager, and a standard interface, IRegion, for access these locations.

To use view injection to add a view to a region, get the region from the region manager, and then call the Add method, as shown in the following code.
With view injection, the view is displayed only after the view is added to a region, which can happen when the module is loaded or when a user action completes a predefined action.

The Prism Library defines a standard registry, RegionManager, and standard interface IRegion to access these locations.

To add a view to a region using view injection, get the region from the region manager and call the Add method, as shown in the following code.
Inserting a view only displays the view after it has been added to the area. This can happen when a module is loaded or when a user action completes a predefined action.

// View injection
IRegion region = regionManager.Regions["MainRegion"];

var ordersView = container.Resolve<OrdersView>();
region.Add(ordersView, "OrdersView");
region.Activate(ordersView);

Ordering Views in a Region
image.png

Whether it uses view discovery or view Injection, an application might need to order how views appear in a TabControl, ItemsControl, or any other control that displays multiple active views.
By default, views appear in the order that they were registered and added into the region.

When a composite application is built, views are often registered from different modules.
Declaring dependencies between modules can help alleviate the problem, but when modules and views do not have any real interdependencies, declaring an artificial dependency couples modules unnecessarily.

To allow views to participate in ordering themselves, the Prism Library provides the ViewSortHint attribute.
This attribute contains a string Hint property that allows a view to declare a hint of how it should be ordered in the region.

Whether you use view detection or view injection, your application may need to order how the view is displayed in TabControl, ItemsControl, or other controls that display multiple active views.
By default, the views are displayed in the order they are registered and added to the region.

When a composite application is built, views are often registered from different modules.
Declaring dependencies between modules can alleviate the problem, but if there are no actual interdependencies between modules and views, declaring artificial dependencies will unnecessarily combine modules.

The Prism Library provides a ViewSortHint attribute to allow views to participate in their own order.
This attribute contains a string Hint property that allows the view to declare a hint of ordering within the region.

image.png

When displaying views, the Region class uses a default view sorting routine that uses the hint to order the views.
This is a simple case-sensitive ordinal sort.
Views that have the sort hint attribute are ordered ahead of those without.
Also, those without the attribute appear in the order they were added to the region.

If you want to change how views are ordered, the Region class provides a SortComparison property that you can set with your own Comparison<_object_> delegate method.
It is important to note that the ordering of the region's Views and ActiveViews properties are reflected in the UI because adapters such as the ItemsControlRegionAdapter bind directly to these properties.
A custom region adapter could implement its own sorting and filter that will override how the region orders views.

When displaying a view, the Region class uses the default view sort routine to sort the views using hints.
This is a simple case-sensitive ordinal sort.
Views with the sort hint attribute take precedence over views without views.
Also, those without attributes are displayed in the order they were added to the region.

If you want to change the way the views are ordered, the Region class has a SortComparison property that you can set with your own Comparison <_object_> delegate method. It is important to note that the UI reflects the order of the Region's Views and ActiveViews properties, as adapters such as the ItemsControlRegionAdapter bind directly to these properties. Custom region adapters can implement their own sorts and filters that override the way regions order views.

Sharing Data Between Multiple Regions
image.png

The Prism Library provides multiple approaches to communicating between views, depending on your scenario.
The region manager provides the RegionContext property as one of these approaches.

RegionContext is useful when you want to share context between a parent view and child views that are hosted in a region.
RegionContext is an attached property.
You set the value of the context on the region control so that it can be made available to all child views that are displayed in that region control.
The region context can be any simple or complex object and can be a data-bound value.
The RegionContext can be used with either view discovery or view injection.

The Prism Library offers multiple approaches to communication between views, depending on the scenario.
Region Manager provides the RegionContext property as one of these approaches.

RegionContext is useful for sharing a context between a parent view and a child view hosted in a region.
RegionContext is an attached property.
Set the context value for the region control so that it is available in all child views displayed in that region control.
The region context can be any simple or complex object and can be a data bound value.
RegionContext can be used in view discovery or view injection.

image.png

Note: The DataContext property in WPF is used to set the local data context for the view. It allows the view to use data binding to communicate with a view model, local presenter, or model. RegionContext is used to share context between multiple views and is not local to a single view. It provides a simple mechanism for sharing context between multiple views.

The following code shows how the RegionContext attached property is used in XAML.

Note: The WPF DataContext property is used to set the view’s local data context. This allows the view to use data binding to communicate with the view model, local presenter, or model. RegionContext is used to share context between multiple views and is not local to a single view. It provides a simple mechanism for sharing context between multiple views.

The following code shows how the RegionContext attached property is used in XAML.

<TabControl AutomationProperties.AutomationId="DetailsTabControl" 
    prism:RegionManager.RegionName="{x:Static local:RegionNames.TabRegion}"
    prism:RegionManager.RegionContext="{Binding Path=SelectedEmployee.EmployeeId}"
...>

image.png

You can also set the RegionContext in code, as shown in the following example.

You can also set the RegionContext in code, as shown in the following example.

RegionManager.Regions["Region1"].Context = employeeId;

image.png

To retrieve the RegionContext in a view, the GetObservableContext static method of the RegionContext class is used.
It passes the view as a parameter and then accesses its Value property, as shown in the following code example.

To get the RegionContext in the view, use the GetObservableContext static method of the RegionContext class.
Pass the view as a parameter and access its Value property, as shown in the following code example.

private void GetRegionContext()
{
    this.Model.EmployeeId = (int)RegionContext.GetObservableContext(this).Value;
}

image.png

The value of the RegionContext can be changed from within a view by simply assigning a new value to its Value property.
Views can opt to be notified of changes to the RegionContext by subscribing to the PropertyChanged event on the ObservableObject that is returned by the GetObservableContext method.
This allows multiple views to be kept in synchronization when their RegionContext is changed.
The following code example demonstrates subscribing to the PropertyChanged event.

You can change the value of RegionContext from within the view by simply assigning a new value to the Value property.
The view can choose to be notified of changes to the RegionContext by subscribing to the ObservableObject’s PropertyChanged event returned by the GetObservableContext method.
This allows you to keep multiple views in sync when the RegionContext changes.
The following code example shows a subscription to the PropertyChanged event.

ObservableObject<object> viewRegionContext = 
                RegionContext.GetObservableContext(this);
viewRegionContext.PropertyChanged += this.ViewRegionContext_OnPropertyChangedEvent;

private void ViewRegionContext_OnPropertyChangedEvent(object sender, 
                    PropertyChangedEventArgs args)

{
    if (args.PropertyName == "Value")
    {
        var context = (ObservableObject<object>) sender;
        int newValue = (int)context.Value;
    }
}

image.png

Note: The RegionContext is set as an attached property on the content object hosted in the region. This means that the content object has to derive from DependencyObject. In the preceding example, the view is a visual control, which ultimately derives from DependencyObject.

If you choose to use WPF data templates to define your view, the content object will represent the ViewModel or PresentationModel. If your view model or presentation model needs to retrieve the RegionContext, it will need to derive from the DependencyObject base class.

Note: RegionContext is set as an attached property of content objects hosted in the region. That is, the content object must derive from the DependencyObject. In the example above, the view is a visual control and ultimately derives from the DependencyObject.

If you choose to define a view using a WPF data template, the content object represents a ViewModel or PresentationModel. If the view model or presentation model needs to get a RegionContext, it must be derived from the DependencyObject base class.

Creating Multiple Instances of a Region
image.png

Scoped regions are available only with view injection. You should use them if you need a view to have its own instance of a region. Views that define regions with attached properties automatically inherit their parent's RegionManager. Usually, this is the global RegionManager that is registered in the shell window. If the application creates more than one instance of that view, each instance would attempt to register its region with the parent RegionManager. RegionManager allows only uniquely named regions; therefore, the second registration would produce an error.

Instead, use scoped regions so that each view will have its own RegionManager and its regions will be registered with that RegionManager rather than the parent RegionManager, as shown in the following illustration.

Scope regions are only available for view injection. If your view requires its own instance of the region, you should use these. The view that defines the area to which the property is attached automatically inherits from the parent RegionManager. This is usually a global RegionManager registered in the shell window. If your application creates multiple instances of that view, each instance will try to register that region with its parent RegionManager. RegionManager only allows uniquely named regions. Therefore, the second registration will result in an error.

Instead, use a scoped area to ensure that each view has its own RegionManager and that area is registered with that RegionManager instead of its parent RegionManager, as shown in the following figure.
image.png

image.png

To create a local RegionManager for a view, specify that a new RegionManager should be created when you add your view to a region, as illustrated in the following code example.

To create a local RegionManager for a view, specify that a new RegionManager should be created when you add the view to a region, as shown in the following code example.

IRegion detailsRegion = this.regionManager.Regions["DetailsRegion"];
View view = new View();
bool createRegionManagerScope = true;
IRegionManager detailsRegionManager = detailsRegion.Add(view, null, createRegionManagerScope);

image.png

The Add method will return the new RegionManager that the view can retain for further access to the local scope.

The Add method returns a new RegionManager that the view can hold for further access to the local scope.