Potential of AI (Generative AI) in Business: Learnings and Insights
Porting the Legacy Application to Composite Application Guidance
1. Porting the Legacy
Application to
MVVM/Composite
Application Guidance
By Don Burnett
2. Definitions
• Model: The underlying business entities that represent the data in your
system.
• View: The user interface representation of the underlying Model).
• Presenter: When the Model can provide the data that the View requires
without any complex manipulation or transformation, then the View binds
directly to the Model and the Presenter handles the state of the View in
the cases where complex user interface logic is required, such as changing
colours of controls and showing/hiding controls
• PresentationModel: When the View cannot bind directly to the Model, a
PresentationModel class is used to provide the Model in a format more
easily consumed by the View acting as a façade. The PresentationModel
class also manages UI-specific state and behaviour.
• Controller: Controller classes to manage the interaction between multiple
views.
3. Advantages
• CAL (Composite Application Library) allows modules to be individually developed, tested, and
deployed by different individuals or sub-teams; they also allow them to be modified or extended
with new functionality more easily, thereby allowing the application to be more easily extended
and maintained. Note that even single-person projects experience benefits in creating more
testable and maintainable applications using the composite approach.
• Provide a common shell composed of UI components contributed from various modules that
interact in a loosely coupled way. This reduces the contention that arises from multiple
developers adding new functionality to the UI, and it promotes a common appearance.
• Promote re-use and a clean separation of concerns between the application's horizontal
capabilities, such as logging and authentication, and the vertical capabilities, such as business
functionality that is specific to your application.
• Help maintain a separation of roles by allowing different individuals or sub-teams to focus on a
specific task or piece of functionality according to their focus or expertise. Provide a cleaner
separation between the user interface and the business logic of the application
• Doing all of this this means the UI designer can focus on creating a richer user experience.
• Composite applications are highly suited to a range of client application scenarios. For example,
a composite application is ideal for creating a rich end-user experience over a number of
disparate back-end systems.
4. Ficticious Legacy Application Scenario
• We are working at a life insurance office and
the application lets us look at our client’s life
insurance policies.
• It was done in Winforms and has since been
moved to WPF, but still uses code behinds.
• It is inefficient to scale as they have a growing
number of customers and branch offices.
5. First Step
• Create the Bootstrapper
• Added references to the following Prism assemblies in
my main (shell) project:
– Microsoft.Practices.Composite.dll
– Microsoft.Practices.Composite.Wpf.dll
– Microsoft.Practices.UnityExceptions.dll
– Microsoft.Practices.Unit.dll
– Microsoft.Practices.ObjectBuilder.dll
• Added a new class called MyApplicationBootstrapper,
which inherits from UnityBootstrapper as shown in the
following code example.
6. My Bootstrapper
public class MyApplicationBootstrapper :
UnityBootstrapper { protected override
DependencyObject CreateShell()
{ MainWindow window = new MainWindow();
window.Show(); return window;
}
protected override IModuleEnumerator
GetModuleEnumerator()
{
return new StaticModuleEnumerator(); }
}
7. First Step Continued..
• Removed the StartupUri property from App.xaml. Note
if you design in Expression Blend this will automatically
get added to App.xaml, you have to keep removing it if
you notice it showing up again.
• Created an instance of in the Application.Startup
event handler as shown in the following code example.
private void Application_Startup(object sender, StartupEventArgs
e)
{ MyApplicationBootstrapper bootstrapper = new
MyApplicationBootstrapper();
bootstrapper.Run();
}
8. Next Steps
• The application runs as it did before but now
it uses a Bootstrapper. No benefits yet, but it
works.
• Next step, start writing some tests and a some
modules to contain the existing UI controls as
views in a more Prism-like approach.
9. Application Structure Overview
• Our pretend legacy application consists primarily of a WPF user interface,
a WCF service that provides access to a SQL Server database and a few
support libraries.
• One of the support libraries provides some classes that deal with the
interaction between the user interface and the WCF service and is my
prime candidate for making some improvements using the Prism
guidance.
• The plan: create Prism Modules for the project and to create the relevant
Views, ViewModels, Services, and Controllers that are required to
implement the functionality. Rather than having to fully re-write the
underlying code that handles UI/WCF Service interaction.
• Write a Prism “façade” that will simply call out to the existing
infrastructure, which I can eventually replace as time progresses. This
gives me the testability advantages of Prism without having to rewrite the
application, which would be a costly and time consuming task.
10. Moving Forward
• Let’s focus on the Policy aspects of the
application.
• next step create a Policy module containing
the PolicyModule, a PolicyService,
PolicyTreeView, PolicyTreeViewModel and a
PolicyController, which will handle the
interactions between the different views of a
policy (when I get that far).
11. Creating a Policy Module
• Each of these classes implements an interface,
which enables me to easily mock these
implementations to test specific aspects of the
application.
• At this stage implementations are very simple in
that they just initialize the relevant Views,
ViewModels, etc.
• The View itself is simply a UserControl that
contains a root Grid element with a background.
The important thing is to get it loaded and
displayed first.
12. Adding Definition
• In the application's shell, the existing user control
was removed and replaced with the following
region definition.
– <ContentControl x:Name="_navigation"
cal:RegionManager.RegionName="{x:Static
inf:RegionNames.Navigation}" DockPanel.Dock="Left" />
• The "cal" XML namespace resolves to
"http://www.codeplex.com/CompositeWPF" and
the "inf" XML namespace resolves to an
infrastructure assembly that contains constants
for the region names.
13. Policy Controller Details
• The PolicyController class retrieves the relevant region that is
the destination of the View and adds the View for the
associated ViewModel to that region and then activates it as
shown below.
public void Run()
{
IRegion region =
this._regionManager.Regions[RegionNames.Navigation];
region.Add(this._policyTreeViewModel.View);
region.Activate(this._policyTreeViewModel.View);
}
• The call to the Activate method is essential if you want to be able to see your
View. Until the Activate method is called, you will not be able to see your View.
14. Policy Controller Continued..
• The Run method of the PolicyController is called in the Initialize method of the PolicyModule after
registering the required views and services, as shown in the following code example.
{
// NOTE: The ContainerControlledLifetimeManager ensures that the same
// instance is returned by any call to Resolve or ResolveAll.
this._container.RegisterType<IPolicyService, PolicyService>(new
ContainerControlledLifetimeManager());
this._container.RegisterType<IPolicyController, PolicyController>(new
ContainerControlledLifetimeManager());
this._container.RegisterType<IProjectTreeView, PolicyTreeView>();
this._container.RegisterType<IPolicyTreeViewModel, PolicyTreeViewModel>();
}
#endregion
Private Methods
#region
IModule Members public void Initialize()
{ RegisterViewsAndServices();
IPolicyController controller = this._container.Resolve<IPolicyController>();
controller.Run(); }
#endregion
16. Modules and Services
• in a composite application built using the CAL, the bulk of the application logic lives in the modules.
• MessagesModule provides related messsages for each policy that is selected.
• PolicyModule provides real-time information on the accounts, for the selected accounts.
• AccountsModule provides a Watch Module that displays a list of accounts you are working with.
• ListModule displays the list of policies in which you an account has and allows you to perform appropriate transactions.
• Using a region removes the need for the module to have any specific reference to the UI or have any knowledge of how the views
injected will be laid out and displayed.
Special thanks to MSDN magazine for letting me re-use this cool diagram.. From their article:
http://msdn.microsoft.com/en-us/magazine/cc785479.aspx
17. Updates
• CAL includes a Region class that is basically a handle that wraps these locations
together.
• The Region class contains a Views property that is a read-only collection of the views
to be displayed within the region.
• Views are added to the region by calling the region's add method.
• The Views property actually contains a generic collection of objects; it is not limited to
containing only UIElements.
• This collection implements INotifyPropertyCollectionChanged so that the UIElement
associated with the region can bind to it and observe changes.
18. Regions and Registration
• Thanks to the rich template support in WPF, you can actually add models directly to the
region. That model can then have an associated Data-Template defined for it that will define
the rendering for the model. If the item added is a UIElement or a user control, then WPF will
just render it as is. This means that if you have a region that is a tab of open policies, you can
sadd the PolicyModel or PolicyPresentation-Model to the region and then define a custom
DataTemplate to control the display rather than having to create a custom PolicyView user
control.
• Regions can be registered in two ways. The first way is defined in XAML by annotating a
UIElement with a RegionName attached property.
• Once a region has been defined via XAML, it will automatically be registered at run time with
RegionManager, one of the composition services registered by the bootstrapper.
RegionManager is essentially a Dictionary where the key is the name of the region and the
value is an instance of the IRegion interface. The RegionManager attached property uses a
RegionAdapter to create this instance.
• CAL ships with three region adapters that are registered by the bootstrapper—
ContentControlRegionAdapter, ItemsControlRegionAdapter, and SelectorRegionAdapter.
Adapters are registered with a RegionAdapterMappings class. Adapters all inherit from
RegionAdapterBase, which implements the I-RegionAdapter interface.
19. Region Adapters
• ItemsControlRegionAdapter- How the adapter itself is implemented depends
completely on the type of UIElement to which it is being adapted. In
ItemsControlRegionAdapter, the bulk of its implementation is in the Adapt method.
• The Adapt method accepts two parameters. The first parameter is an instance of the
Region class itself that Region-Manager creates.
• The second parameter is the UIElement that represents the region. The Adapt method
performs the relevant plumbing to ensure that the region works with the element.
• In the case of an ItemsControl, the adapter automatically removes any child items from
the ItemControl itself and then adds them to the region. The region's Views collection
is then bound to the ItemsSource of the control.
• The second method overridden is CreateRegion, which returns a new AllActiveRegion
instance.
• Regions can contain views that are active or inactive. In the case of the ItemsControl,
all of its items are active all the time because it does not have a notion of selection.
However, in the case of other types of regions, such as Selector, only one item is
selected at a time.
• A view can implement the IActiveAware interface so that it is notified by its region
that it is selected. Whenever the view is selected, it will have its IsSelected property set
to true.
20. More Region Adapters
• Once the region has been defined, it can be accessed from any class within the application by
grabbing hold of the Region-Manager service.
• The common way to do this in a CAL application is to have the dependency injection
container inject the RegionManager into the constructor of the class that needs it. To add a
view or model to a region, simply call the region's Add method. When you add a view, you
can pass an optional name:
_regionManager.Regions["MainRegion"].Add( somePresentationModel,
"SomeView");
• You can later use that name to retrieve the view from the region by using the region's
GetView method.
21. Local Regions
• There is only one RegionManager instance in your application,
thereby making every region globally scoped.
• This addresses a wide set of scenarios, but there are situations
where you may want to define a region that exists only at a
particular scope.
• An example of where you might want to do this is when your
application has a view for a account/policy where multiple
instances of the view can be displayed at the same time.
• If these views are fairly complex, they behave like mini-shells or
CompositeViews. In these cases, you may want each view to have
its own regions, as the shell does.
• The CAL allows you to define a local RegionManager for a view so
that any regions defined within it or its child views are
automatically registered in that local region.
22. Injection
• The UI Composition quickstart included with the guidance illustrates this employee scenario.
– http://msdn.microsoft.com/en-us/library/dd458809.aspx
• In the quickstart there is an employee list. As you click on each employee, you see the
associated employee detail.
• For each employee selection, a new EmployeeDetailsView is created for that employee and
added to the DetailsRegion .
• This view contains a local TabRegion, into which the EmployeesController injects a
ProjectListView in its OnEmployeeSelected method.
23. More Injection
• The region is rendered as a TabControl and contains both static and dynamic content. The General and Location tabs are
statically defined within the XAML. The Current Projects tab, however, has its views injected.
• You can see in the code that a new RegionManager instance is returned from the detailsRegion.Add method. Also notice
that I am using the overload of Add that passes in a name for the view and sets the createRegionManagerScope parameter
to true. Doing this creates a local RegionManager instance that will be used for any regions defined in the children. The
TabRegion itself is defined in the XAML of the EmployeeDetailsView:
<TabControl AutomationProperties.AutomationId="DetailsTabControl"
cal:RegionManager.RegionName="{x:Static local:RegionNames.TabRegion}" .../>
24. Benefits
• Using local regions provides an additional benefit even if you are not using instance regions.
• You can use them for defining a top-level boundary so that a module does not automatically
expose its regions to the rest of the world.
• To do this, add the top-level view for that module into a region and specify for it to have its own
scope.
• Without views you would have no need for a composite. Views are the single most important
element that you will build within your composite apps, as they are the gateway for your users to
the world of functionality that your application provides.
• Views are typically the screens of your application. Views can contain other views, thereby
becoming composite views. Another use of views is for menus and toolbars.
• WPF supports a much richer notion of a View than what was the convention in the Windows Forms
world. In Windows Forms you were basically restricted to using controls as your visual
representation.
• In WPF, this model is still supported and you can create custom user controls for representing your
different screens.
• Views are typically the screens of your application. Views can contain other views, thereby
becoming composite views. Another use of views is for menus and toolbars. In our application, for
example, the ContainerBottom.xaml Toolbar is a view that contains buttons for operations that
work like normal system ones such as Submit, Cancel, Submit All, and Cancel All.
25. Using Models (Code) Instead
• Another approach is to use models. WPF will allow you to bind any model to the UI and then use a
DataTemplate to render it.
• Templates are rendered recursively, meaning that if a template renders an element that binds to a
property of the model, that property will be rendered using a template if it is available.
• WPF supports a much richer notion of a View than what was the convention in the Windows Forms
world. In Windows Forms you were basically restricted to using controls as your visual
representation. In WPF, this model is still supported and you can create custom user controls for
representing your different screens. If you look throughout the CAL sample application, this is the
primary mechanism employed for defining views.
• Another approach is to use models. WPF will allow you to bind any model to the UI and then use a
DataTemplate to render it. Templates are rendered recursively, meaning that if a template renders
an element that binds to a property of the model, that property will be rendered using a template
if it is available.
• Everything starts with the Employee-DetailsPresentationModel. Its template declares that it should
be rendered as a TabControl. As part of the template, it binds the TabControl's ItemsSource to the
EmployeeDetailsPresentationModel's EmployeeDetails collection property. This collection is
populated with two pieces of information when the Employee details are constructed:
public EmployeesDetailsPresentationModel()
{ EmployeeDetails = new ObservableCollection<object>();
EmployeeDetails.Insert(0, new HeaderedEmployeeData());
EmployeeDetails.Insert(1, new EmployeeAddressMapUrl()); ...
27. A Details View Detailed
• Did you notice the details view we just looked at is one big template inside a
resource dictionary??
• In the sample, A separate tab will be rendered for each item in the collection. As
the first item gets rendered, WPF will use the DataTemplate specified for
HeaderedEmployeeData.
• The HeaderedEmployeeData model contains the employee name and contact info.
Its associated template renders the model as a series of labels for displaying the
information.
• The second item will get rendered using the template specified for
EmployeeAddressMapUrl, which in this case will render a frame that contains a
Web page with a map of where the employee lives.
• What’s new here is that a view as you previously knew it only existed at run time
through the combination of the model and its associated template. You can also
implement a hybrid of both approaches, where you have user controls that have
controls within them that are then bound to models that are rendered through
templates.
28. UI: Presentation Model and
Supervising Controller
• The Presentation Model pattern assumes a model that
contains both the behavior and the data for the UI. The
view then projects the presentation model's state "onto
the glass."
• Behind the scenes the model interacts with business and
domain models. The model also includes additional state
information such as the selected item or whether an
element is checked. The view then binds to the
Presentation Model directly and renders it.
• The rich support in WPF for data binding, templates, and
commands makes the Presentation Model pattern almost a
nuclear option for development with anything else.
29. Communication Challenges
• The model is the data; it is more often than not a business object. The view is a UIElement to which the
model directly binds. And lastly, the presenter is a class that contains the UI logic. In this pattern, the view
contains very little logic other than delegating to the presenter and responding to callbacks from the
presenter to perform simple actions including either displaying or hiding a control.
• One of the challenges that arise when implementing separated presentation is the communication
between the view and the presentation model or presenter. There are several approaches to handling
this.
• One often implemented is having event handlers in the view that either directly call or raise events to the
presentation model or presenter. The same UIElements that initiate calls to the presenter often have to be
enabled or disabled in the UI based on state changes or permissions. This requires the view to have
methods that can be used to call it back in order to disable those elements.
30. And More Challenges..
• One of the challenges that arise when implementing separated presentation is the
communication between the view and the presentation model or presenter. There are
several approaches to handling this.
• One that is often implemented is having event handlers in the view that either directly call or
raise events to the presentation model or presenter. The same UIElements that initiate calls
to the presenter often have to be enabled or disabled in the UI based on state changes or
permissions. This requires the view to have methods that can be used to call it back in order
to disable those elements.
• Another approach is to use WPF commands. Commands provide a clean way of addressing
these situations without requiring all the back-and-forth delegation logic. Elements in WPF
can bind to commands to handle both execution logic and enabling or disabling elements.
When a UIElement is bound to a command, it will automatically be disabled whenever the
command's Can-Execute property is false. Commands can be bound declaratively in XAML.
• WPF provides RoutedUICommands. Using these commands requires a handler for the
Execute and Can-Execute methods within the view's codebehind—which means that code
modification is still required for the back-and-forth communication. RoutedUICommands
also have other constraints, such as requiring the receiver to be in WPF's logical tree, a
constraint that is problematic for building composite applications.
31. Icommand and Delgate-Command to
the Rescue
• WPF provides the ICommand interface. This will bind to any
command that implements it. This means you can create custom
commands fullfill all of your needs, and you don't need to touch the
codebehind.
• The downside is that you have to implement custom commands,
such as SaveCommand, Submit-Command, and CancelCommand
yourself in the application.
• The CAL includes new commands such as the Delegate-
Command<T>,which allows you to specify the two delegates for
the Execute and CanExecute methods in the constructor.
• Using this command allows you to wire up views without having to
delegate through methods defined in the view itself and without
having to create custom commands for each action.
32. Using a Delegate Command in the
Accounts Watchlist
• WatchListService uses this command for
adding items to the watch list
public WatchListService(IMarketFeedService
marketFeedService)
{this.marketFeedService = marketFeedService;
WatchItems = new ObservableCollection<string>();
AddWatchCommand = new DelegateCommand<string>(AddWatch);
}
33. Publish and Subscribe
• In addition to routing commands between the view
and a presenter or presentation model, there are other
types of communication, such as event publication,
that need to be handled.
• In these cases, the publisher is completely decoupled
from the subscriber.
• For example, our “legacy” application needs to have a
module that exposes a Web service endpoint that
receives notifications from the server. Once that
notification is received, it needs to fire an event to
which components within the same or other modules
can subscribe.
34. Events
• To support this functionality, the CAL has an EventAggregator service that is registered with the
container. Using this service, which is an implementation of the Event Aggregator pattern,
publishers and subscribers can communicate in a loosely coupled fashion.
• The EventAggregator service contains a repository of events that are instances of the abstract
EventBase class. The service has one GetEvent<TEventType> method for retrieving event
instances.
• The CAL includes the CompositeWPFEvent<TPayload> class that inherits EventBase and
provides specific support for WPF. This class uses delegates rather than full .NET events for
publishing. Under the hood it uses a DelegateReference class that, by default, functions as a weak
delegate (see msdn.microsoft.com/library/ms404247). This allows subscribers to be garbage
collected even when they do not explicitly unsubscribe.
• The CompositeWPFEvent class contains Publish, Subscribe, and Unsubscribe methods. Each uses
the generic type information of the event to ensure that the publisher passes the correct
parameters (TPayload) and the Subscriber property receives them (Action<TPayload>).
• The Subscribe method allows passing in a ThreadOption that can be set to PublisherThread,
UIThread, or BackgroundThread. This option determines on which thread the subscribing delegate
will be invoked. Also, the Subscribe method is overloaded to allow passing in a Predicate<T>
filter so that the subscriber only gets notified of the event if the filter is satisfied.
35. Event Aggregation
• In our app, the EventAggregator should be
used for broadcasting whenever a policy item
or an account is selected in the positions
screen. The messages module subscribes to
this event and displays messages for the
account or policy.
• public class AccountSelectedEvent : CompositeWpfEvent<string>
{ }
36. Defining an Event
• The event is defined in our application’s infrastructure assembly, to which all modules in our
application reference.
public void Run()
{
this.regionManager.Regions[“MessagesRegion"].Add( articlePresentationModel.View);
eventAggregator.GetEvent<AccountSelectedEvent>().Subscribe( ShowNews,
ThreadOption.UIThread);
}
public void ShowNews(string companySymbol)
{
articlePresentationModel.SetAccount(companyAccount);
}
Another module's Controller then subscribes to this event in its Run method:
private void View_CompanySymbolSelected(object sender, DataEventArgs<string> e)
{ _trendLinePresenter.OnCompanySymbolSelected(e.Value);
EventAggregator.GetEvent<TickerSymbolSelectedEvent>().Publish( e.Value); }
The presentation model then fires the event whenever an account is selected.
37. Composite Extensions to support
Non-UI Elements and processes
• Brian Noyes’ Composite Extensions: If you like the modularity and publish
-subcribe events capabilities in Prism and would like to be able to do those same
things to adapt your legacy .NET application here are some extensions and add-
ons that are must haves.
• These extensions allow you to use the modular loading patterns and capabilities of
Prism as well as the pub/sub events in a Windows Forms application, or any other
kind of application (even console apps, WCF services, etc.).
• KEY ELEMENTS (staying the same):
– A CompositeEvent class that has all the same capability as the CompositePresentationEvent
class in Prism2, but is not tied to the WPF libraries at all.
– For the UI thread dispatching capability, it uses the SynchronizationContext class (which is
used under the covers by both WPF and Windows Forms, so this class will also work with
WPF).
– A SimpleUnityBootstrapper class that removes the tie to WPF in the bootstrapper by
removing the creation of the shell and the region adapter stuff.
• Check out Brian Noyes’ Downloads and Videos and Demos at DNR.TV
• http://www.dnrtv.com/default.aspx?showNum=132
39. More Tools- Calcium
• Introduction
• Calcium is a WPF composite application toolset that
leverages the Composite Application Library. It
provides much of what one needs to rapidly build a
multifaceted and sophisticated modular application.
• Calcium consists of a client application and server
based WCF services, which allow interaction and
communication between clients. Out of the box,
Calcium comes with a host of modules and services,
and an infrastructure that is ready to use in your next
application.
41. Calcium Features
• Duplex messaging services for interacting with the user from the client or
server using the same API. Interact with the user using Message Boxes
from the server!
• Module Manager for enabling or disabling of modules at runtime.
• A User Affinity module that assists collaboration with other users of the
application.
• A Command Service to associate WPF ICommands with content interfaces
that only become active when an active view or viewmodel implements
the interface.
• Region Adapters for ToolBars and Menus.
• Client-server logging ready to work out-of-the-box.
• Includes modules, such as a Web Browser, Text Editor, Output Window,
and many more.
• Tabbed interface with dirty file indication (reusable across modules).
42. Calcium
• Calcium is a WPF composite application toolset that leverages the Composite
Application Library. It provides much of what one needs to rapidly build a
multifaceted and sophisticated modular application.
Calcium consists of a client application and server based WCF services, which allow
interaction and communication between clients. Out of the box, Calcium comes
with a host of modules and services, and an infrastructure that is ready to use in
your next application.
• Get it today at: http://calcium.codeplex.com/
43. Porting the Legacy
Application to
MVVM/Composite
Application Guidance
By Don Burnett