Telerik blogs

With the our Q3 2013 SP1 release of RadDocking we introduced some new functionalities in the RadDocking control which helps using the control in MVVM scenarios. You can find more information about this topic in my previous blog post here. Before proceeding with this blog post it’s good to be familiar with PRISM and its functionalities because it will be an accent of the blog post.

The Bootstrapper

First we will need to create a bootstrapper which inherits the MefBootstrapper. There is nothing special here except that we need to register a custom RegionAdapter. The docking control is either ContentControl or ItemsControl and that’s why the default PRISM functionality doesn’t work when implementing it with the control. Fortunately, extending the framework is an easy task. Let’s map the custom RegionAdapter to the docking control: 

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
    // Registering the DockingRegionAdapter. This will allow the dock to be marked as region and accomudate views.
    RegionAdapterMappings mappings = base.ConfigureRegionAdapterMappings();
    var regionBehaviorFactory = Container.GetExportedValue<IRegionBehaviorFactory>();
    mappings.RegisterMapping(typeof(RadDocking), Container.GetExportedValue<DockingRegionAdapter>());
    return mappings;
}

The RegionAdapter

We will need to create a custom RegionAdapter which inherits from RegionAdapterBase<RadDocking> and override its Adapt method. Its job is to initialize the relationship between the targeted control (RadDocking in this case) and the region:

protected override void Adapt(IRegion region, RadDocking regionTarget)
{
    regionTarget.PanesSource = region.Views;
}

None of the predefined regions in PRISM can fully cover the docking layout. We will need to create a region and apply a custom RegionBehavior which will help when activating views:

protected override IRegion CreateRegion()
{
    var region = new Region();
    region.Behaviors.Add("DockActivationRegionBehavior", serviceLocator.GetInstance<DockActivationRegionBehavior>());
    return region;
}

The RegionBehavior

In the custom RegionBehavior we have the proper information to listen up for IActiveAware.IsActiveChanged event (more details about the activation can be find on the next paragraph). When a view is registered, we check whether it implements IActiveAware interface and listen for the event in order to activate it via the Region: 

public void OnViewIsActiveChanged(object sender, EventArgs e)
{
    var activeAware = (IActiveAware)sender;
    if (activeAware.IsActive)
    {
        this.Region.Activate(sender);
    }
    else
    {
        this.Region.Deactivate(sender);
    }
}

The Activation

RadDocking control has its own build-in activation functionality which is separated from the PRISM’s one, but they can be combined with some custom code. First, we need to implement the IActiveAware interface of each view (the views are actually the panes). Note that RadPane has IsActive property which matches the interface IsActive property. The key moment here is to override the RadPane’s OnIsActiveChanged method. The method is executed each time when the RadPane’s IsActive property changes its value and we can fire the IActiveAware.IsActiveChanged event in order to notify that the activation has changed. The further logic is already implemented in the custom DockActivationRegionBehavior. 

using PRISMIActiveAware = Microsoft.Practices.Prism.IActiveAware;
 
namespace ShellPrism
{
    /// <summary>
    /// Interaction logic for ErrorList.xaml
    /// </summary>
    [Export]
    public partial class ErrorList : RadPane, IPaneModel, PRISMIActiveAware
    {
        public ErrorList()
        {
            InitializeComponent();
        }
 
 
        public event EventHandler IsActiveChanged;
 
        protected override void OnIsActiveChanged()
        {
            base.OnIsActiveChanged();
            this.OnIsActiveChanged(EventArgs.Empty);
        }
 
        private void OnIsActiveChanged(EventArgs e)
        {
            if (this.IsActiveChanged != null)
            {
                this.IsActiveChanged(this, e);
            }
        }
    }
}

With such implementation we can activate panes in RadDocking using the PRISM framework with ease:

public void OnActivateView(string documentHeader)
{
    var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
    if (regionManager != null)
    {
        var dockRegion = regionManager.Regions["DocumentsRegion"];
        var paneView = dockRegion.Views.FirstOrDefault(p => ((RadPane)p).Header.ToString() == documentHeader);
        if (paneView != null)
        {
            dockRegion.Activate(paneView);
        }
    }
}

Add new document at runtime

All we need to do is add a new view to the Docking region called “DocumentsRegion”. Please, note that the new added document object must be of type RadDocumentPane:

private void AddDocument(RadDocumentPane document)
{
    var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
    if (regionManager != null)
    {
        regionManager.AddToRegion("DocumentsRegion", document);
    }
}

Save/Load layout

How to save and load the layout in RadDocking control is covered in our online documentation here - http://www.telerik.com/help/wpf/raddocking-features-save-load-layout.html. The tricky question here is how to restore the content of each added document at runtime? The SaveLoadLayoutHelper gives the answer for that. All that you will need to do is:
-          Create a class that inherits DefaultSaveLoadLayoutHelper
-          Override the ElementLoadingOverride method
-          Create a new document for each loaded xml element

public class PRISMSaveLoadLayoutHelper : DefaultSaveLoadLayoutHelper
{
    protected override DependencyObject ElementLoadingOverride(string serializationTag)
    {
        var element = base.ElementLoadingOverride(serializationTag);
        if (serializationTag == "Document" && element == null)
        {
            element = FileServicesModule.CreateDocument("New Document") as DependencyObject;
        }
 
        return element;
    }
}

You can find a runnable project and its source code for both WPF and Silverlight here. The described scenario just points out some of the main functionalities when building RadDocking with PRISM. If you have any suggestions and ideas feel free to write a comment about it to this blog post.


About the Author

Georgi Dangalov

Senior Developer
Telerik XAML Team

Comments

Comments are disabled in preview mode.