Export (0) Print
Expand All

Communication

When building large complex applications, a common approach is to divide the functionality into discrete module assemblies. It is also desirable to minimize the use of static references between these modules. This allows the modules to be independently developed, tested, deployed, and updated and it forces loosely coupled communication.

When communicating between modules, it is important that you know the differences between the approaches, to decide which the best to use in a particular scenario is. The Composite Application Library provides the following communication approaches:

  • Commanding. Use when there is an expectation of immediate action from the user interaction.
  • Event aggregation. For communication across ViewModels, Presenters, or Controllers when there is not a direct action-reaction expectation.
  • Region context. Use this to provide contextual information between the host and views in the host's region. This approach is somewhat similar to the DataContext, but it does not rely on it.
  • Shared services. Callers can call a method on the service which raises an event to the receiver of the message. Use this if none of the preceding is applicable.

Commanding

If you need to respond to a user gesture, such as clicking on a command invoker (for example, a button or menu item), and if you want the invoker to be enabled based on business logic, use commanding.

Windows Presentation Foundation (WPF) provides RoutedCommand, which is good at connecting command invokers, such as menu items and buttons, with command handlers that are associated with the current item in the visual tree that has keyboard focus.

However, in a composite scenario, the command handler is often a controller that does not have any associated elements in the visual tree or is not the focused element. To support this scenario, the Composite Application Library provides DelegateCommand, which allows you to call a delegate method when the command is executed and CompositeCommand, which allows you to combine multiple commands. These commands are different from the built-in RoutedCommand, which will route command execution and handling up and down the visual tree. This allows you to trigger a command at a point in the visual tree and handle it at a higher level.

The CompositeCommand is an implementation of ICommand so that it can be bound to invokers. CompositeCommands can be connected to several child commands; when the CompositeCommand is invoked, the child commands are also invoked.

CompositeCommands support enablement. CompositeCommands listen to the CanExecuteChanged event of each one of its connected commands. It then raises this event notifying its invoker(s). The invoker(s) reacts to this event by calling CanExecute on the CompositeCommand. The CompositeCommand then again polls all its child commands by calling CanExecute on each child command. If any call to CanExecute returns false, the CompositeCommand will return false, thus disabling the invoker(s).

How does this help you with cross module communication? Applications based on the Composite Application Library may have global CompositeCommands that are defined in the shell that have meaning across modules, such as Save, Save All, Cancel. Modules can then register their local commands with these global commands and participate in their execution.

For more information about using composite commands, see the following:

Ff647735.note(en-us,PandP.10).gifNote:
About WPF Routed Events and Routed Commands
A routed event is a type of event that can invoke handlers on multiple listeners in an element tree, instead of just on the object that raised the event. WPF-routed commands deliver command messages through UI elements in the visual tree, but the elements outside the tree will not receive these messages because they only bubble up or down from the focused element or an explicitly stated target element. Routed events can be used to communicate through the element tree, because the event data for the event is perpetuated to each element in the route. One element could change something in the event data, and that change would be available to the next element in the route.
Therefore, you should use WPF routed events in the following scenarios: defining common handlers at a common root or defining your own custom control class.

Event Aggregation

If you need to publish an event across modules and do not need a response, use EventAggregator.

Consider using EventAggregator when sending a message between business logic code, such as controllers and presenters. An example is when the Process Order button has been clicked and the order successfully processed; in this case, other modules need to know the order is successfully processed so they can update their views.

EventAggregator provides multicast publish/subscribe functionality. This means there can be multiple publishers that raise the same event and there can be multiple subscribers listening to the same event.

This helps with cross module communication because events can be defined in a shared assembly in a way that publishers and subscribes can reside in entirely separate modules.

For more information about using EventAggregator, see the following:

Ff647735.note(en-us,PandP.10).gifNote:
About .NET Framework Events
Using .NET Framework events is the most simple and straightforward approach for communication between components if loose coupling is not a requirement. Events in the .NET Framework implement the Publish-Subscribe pattern, but to subscribe to an object, you need a direct reference to that object, which, in composite applications, typically resides in another module. This results in a tightly coupled design. Therefore, .NET Framework events are used for communication within modules instead of between modules.
If you use .NET Framework events, you have to be very careful of memory leaks, especially if you have a non-static or short-lived component that subscribes to an event on a static or longer-lived one. If you do not unsubscribe the subscriber, it will be kept alive by the publisher, and this will prevent the first one from being garbage-collected.

Region Context

There are a lot of scenarios where you might want to share contextual information between the view that is hosting a region and a view that is inside a region. For example, a master detail—like view shows a business entity and exposes a region to show additional detail information for that business entity. The Composite Application Library uses a concept named RegionContext to share an object between the host of the region and any views that are loaded inside the region.

Ff647735.b9306dce-8a15-4b63-8d1e-1323417e73f1(en-us,PandP.10).png

Figure 1

Using RegionContext

Depending on the scenario, you can choose to share a single piece of information (such as an identifier) or a shared model. The view can retrieve the RegionContext, and then sign up for change notifications. The view can also change the RegionContext's value. There are several ways of exposing and consuming the RegionContext:

  • You can expose RegionContext to a region in XAML.
  • You can expose RegionContext to a region in code.
  • You can consume RegionContext from a view inside a region.
Ff647735.note(en-us,PandP.10).gifNote:
The Composite Application Library currently only supports consuming the RegionContext from a view inside a region if that view is a DependencyObject. If your view is not a DependencyObject (for example, you are using WPF automatic data templates and adding your ViewModel directly in the region), consider creating a custom RegionBehavior to forward the RegionContext to your view objects. For more information, see the section "Region Behaviors" in the UI Composition technical concept. For more information about RegionContext, see the UI Composition technical concept and View Discovery Composition QuickStart.


Ff647735.note(en-us,PandP.10).gifNote:
About the Data Context Property
Data context is a concept that allows elements to inherit information from their parent elements about the data source that is used for binding. Child elements automatically inherit the DataContext of their parent element. The data flows down the visual tree.
The best method for binding a ViewModel to a view in Silverlight is by using the DataContext property; that is why, in most cases, the DataContext is used to store the view's model.
Because of this, unless you have very simple views, it is not recommended that you use the DataContext property as a communication mechanism between different loosely coupled views.

Shared Services

Another method of cross-module communication is through shared services. When the modules are loaded, modules add their services to the service locator. Typically, services are registered and retrieved from a service locator by common interface types. This allows modules to use services provided by other modules without requiring a static reference to the module. Service instances are shared across modules, so you can share data and pass messages between modules.

In the Stock Trader Reference Implementation, the Market module provides an implementation of IMarketFeedService. The Position module consumes these services by using the shell application's dependency injection container, which provides service location and resolution. The IMarketFeedService is meant to be consumed by other modules, so it can be found in the StockTraderRI.Infrastructure common assembly, but the concrete implementation of this interface does not need to be shared, so it is defined directly in the Market module and can be updated independently of other modules.

To see how these modules register their services into the Unity dependency injection container, see the MarketModule.cs file, as shown in the following code. The Position module's ObservablePosition receives the IMarketFeedService service through constructor dependency injection. For more information about the Dependency Injection pattern, see Dependency Injection.

protected void RegisterViewsAndServices()
{
_container.RegisterType<IMarketFeedService, MarketFeedService>(new ContainerControlledLifetimeManager());
   //...
}

This helps with cross-module communication because service consumers do not need a static reference to modules providing the service. This service can be used to send or receive data between modules.

For more information about services and containers, see the following:

More Information

For more information about other Composite Application Guidance technical concepts, see the following topics:



Home page on MSDN | Community site

Show:
© 2014 Microsoft