PRISM v2 – dROP 6 (Composite Application Guidance for WPF and Silverlight

Today, we have released the 6th drop of Prism V2 (affectionately known as Compostite Application Guidance for WPF and Silverlight)

You can download it from:

https://www.codeplex.com/CompositeWPF/Release/ProjectReleases.aspx?ReleaseId=19731 

 

What’s new?

We’ve had a busy iteration, where we mainly focused on improving some of the design around regions, bugfixes and some code cleanup. This is the list of new items:

    • RegionContext usable in XAML.
    • Introduced a façade on IRegionManager for adding views to regions (push based) and registering view types (pull based)
    • Expanded the usage of pull based composition in Top Down Composition quickstart
    • Silverlight RI is now using the Silverlight Toolkit Controls for line and pie charts
    • Regions and RegionContext using an expanded behavior mechanisms
    • Have established package load key for project linker
        • known issue: this still requires the SDK to be installed, however.
    • Changed to Add Project Link... dialog
    • Resolved a number of code-analysis rules
    • Stock Trader RI is now using pull-view (or top-down) composition for trend line graph
    • ProjectLinker installer does not have to be started with elevated permissions on Vista

So like always, please check it out and tell us what you think.

There are a couple of topics I’d like to go into a bit deeper:

 

Façade on IRegionManager

We’ve introduced a façade on the IRegionManager to make it a bit easier to work with Regions, either from a push or pull based perspective. You can use the following calls:

    1: public class MyModule : IModule
    2:  
    3:     public void Initialize()
    4:     {
    5:     
    6:         // Push based composition
    7:         // This code will find "Region1" (it must already be created) and add a new 
    8:         // instance of MyView to it. 
    9:         this.regionManager.AddViewToRegion("Region1", new MyView());
   10:     
   11:         // Pull based composition
   12:         // This code will register a viewtype with a region. Whenever Region2 is 
   13:         // displayed, it will automatically be populated by a new instance of MyView()
   14:         // created by the ServiceLocator. 
   15:         this.regionManager.RegisterTypeWithRegion("Region2", typeof(MyView());
   16:     
   17:         // Variation on pull based composition:
   18:         // This code uses a delegate that creates the view to be pulled. This is great
   19:         // if your presenters are responsible for creating the views. 
   20:         this.regionManager.RegisterTypeWithRegion("Region2", 
   21:                () => new MyViewPresenter().View());
   22:         
   23:     }   
   24:  
   25:     private IRegionManager regionManager;
   26:     
   27:     public MyModule(IRegionManager regionManager)
   28:     {
   29:         this.regionManager = regionManager;
   30:     }
   31:  
   32: }

In the previous drop, we have introduced the IRegionViewRegistry, where you could register views to be pulled (Previously called RegionContentRegistry). This allows you to plug in your own registration module. However, we figured that for simple scenario’s, you don’t need to know about this implementation detail.

RegionContext

An other thing we’ve improved is the RegionContext. Like I mentioned in my previous blogpost, the RegionContext is a way that a view that hosts a region can share some of it’s information with any childviews that are loaded into it’s region. The RegionContext now supports 2 way databinding and is very configurable using RegionBehaviors.

The following codesnippet shows how to use the RegionContext in XAML:

 <ContentControl x:Name="SelectionPanel"
                cal:RegionManager.RegionName="{x:Static local:RegionNames.SelectionRegion}"
                cal:RegionManager.RegionContext="{Binding Path=SelectedEmployee, Mode=TwoWay}"
                /> 

And in the views, you can get access to the regioncontext like this:

 // To get the value from the regioncontext, from inside the view in a region:
var regionContextValue = RegionExtensions.GetRegionContext(this).Value

// To change the RegionContext:
RegionExtensions.GetRegionContext(this).Value = newValue

// And to subscribe to change events:
RegionExtensions.GetRegionContext(this).PropertyChanged += RegionContext_PropertyChanged

 

 

RegionBehaviors

We’ve received a number of questions from people who liked the functionality where a region would automatically pull in instances of all registered viewtypes. However, they really wanted the ability to influence which views were pulled in. This was one of the reasons why we moved to implement a list of behaviors on a region.

We had the following requirements:

  • You have to be able to define regions in XAML and Code
  • You should be able to bind the RegionContext in XAML or set it through code.
  • The RegionContext should be available for views inside a region. But a view can either be an UserControl (thus a dependencyobject) or a ViewModel (poco)
  • A region should be able to pull in registered views (but in a way you can influence it)

This lead us to the conclusion a region has a couple of behaviors. We will provide a number of behaviors by default, which you can change globally, or for a specific Region instance.

image

Default behaviors

We have built a number of behaviors:

  • For synchronizing the RegionContext, we’ve built two behaviors: SyncRegionContextBehavior, which will synchronize the regioncontext dependency property, defined in XAML with the Context property on the region. To synchronize the Regions Context property with childviews (we only support usercontrols now) we have the BindRegionContextToDependencyObjectBehavior.
  • For the TopDown (pull based) composition, we’ve created the AutoPopulateBehavior. This will query the RegionViewRegistry for all viewtypes defined on the view and autopopulate it.
  • To register the region with the regionmanager, we’ve created the RegionManagerRegistrationBehavior.
  • Some views are aware of the fact that they are ‘active’ or not, by implementing the IActiveAware. The active property is set on them by the RegionActiveAwareBehavior.

This diagram shows the behaviors we have built now:

image

 

Customizing the behaviors for all regions

The default behaviortypes are registered in the UnityBootstrapper. By overriding the ConfigureDefaultRegionBehaviors method you can add new behaviortypes, replace existing types or remove behaviors. You can also choose to register a different IRegionBehaviorFactory, if you require custom initialization for your behaviors.

The default behaviors are added by the RegionAdapterBase. In the RegionAdapter you can decide to add behaviors for specific control types. For example, we had to build a behavior to get the TabControl in silverlight to work properly. (It doesn’t derive from selector like in WPF).

Customizing the behaviors for a single region instance

Sometimes, you want to add a behavior to a specific instance of a region. For example, when you want to change the way views are pulled into a region. To do this, you have to detect that the region has been created, but before the default region behaviors are attached. You can do that by hooking up an eventhandler in the constructor of the view that’s hosting a region.

    1: // From the TopDownUIComposition Quickstart
    2: public EmployeesView()
    3: {
    4:     InitializeComponent();
    5:  
    6:     RegionExtensions.GetObservableRegion(this.SelectionPanel).PropertyChanged += delegate(object sender, PropertyChangedEventArgs args)
    7:     {
    8:         if (args.PropertyName == "Value")
    9:         {
   10:             // The region has just been created, but (Very Important), the default behaviors have not been added yet. 
   11:             // This gives you a chance to add your own custom behaviors. If you use the same key as a default behavior
   12:             // it will replace a default behavior. 
   13:             IRegion region = RegionExtensions.GetObservableRegion(this.SelectionPanel).Value;
   14:             region.Behaviors.Add(AutoPopulateRegionBehavior.BehaviorKey, new CustomAutoPopulateBehavior());
   15:         }
   16:     };

The ‘GetObservableRegion’ method will return an object that you can monitor to when the region is created. The .Value property will hold the new region.