Thursday, December 23, 2010

Custom RadOutlookBar region adapter – Silverlight, Prism, MEF

The Prism Library provides default control adapters for enabling a control as a region. Extensions around regions may involve providing custom region adapters, custom regions, or replacing the region manager. If you are planning to use a custom Silverlight control or a third party control that does not work with the provided region adapters, you may need to create a custom adapter for the control. In this post I'll show how to use the RadOutlookBar control as a region manager and create a custom adapter for it in Prism. I have used MEF container instead of unity in this sample.
To create a custom region adapter you need to inherit the RegionAdapterBase class or create an implementation for IRegionAdapter. The sample used in this post inherits the RegionAdapterBase class
public class TelerikOutlookBarRegionAdapter : RegionAdapterBase<RadOutlookBar>
{
    public TelerikOutlookBarRegionAdapter(IRegionBehaviorFactory regionBehaviourFactory) : base(regionBehaviourFactory) { }

    protected override void Adapt(IRegion region, RadOutlookBar regionTarget)
    {
        _outlookBar = regionTarget;
        _outlookBar.Items.Clear();
        region.ActiveViews.CollectionChanged +=
        new System.Collections.Specialized.NotifyCollectionChangedEventHandler((x, y) =>
        {
            switch (y.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (RadOutlookBarItem outlookBarButton in y.NewItems)
                        _outlookBar.Items.Add(outlookBarButton);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (RadOutlookBarItem outlookBarButton in y.NewItems)
                        _outlookBar.Items.Remove(outlookBarButton);
                    break;
            }
        });
        region.ActiveViews.ToList().ForEach(x => _outlookBar.Items.Add(x as RadOutlookBarItem));
    }

    protected override IRegion CreateRegion()
    {
        return new AllActiveRegion();
    }

    RadOutlookBar _outlookBar;
}

Next we need to register the region adapter in the Bootstrappers ConfigureRegionAdapterMappings method.

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
    var regionAdapterMappings = base.ConfigureRegionAdapterMappings();
    var regionBehaviourFactory = Container.GetExportedValueIRegionBehaviorFactory>();
    regionAdapterMappings.RegisterMapping(typeof(Telerik.Windows.Controls.RadOutlookBar), new TelerikOutlookBarRegionAdapter(regionBehaviourFactory));
    return regionAdapterMappings;
}

Adding a new Tab from a module can be done like
[ModuleExport("BlogsPrajeesh.PrismSamples.Configuration", typeof(ConfiguraitonModule))]
public class ConfiguraitonModule : IModule
{
    [ImportingConstructor]
    public ConfiguraitonModule(IRegionManager regionManager)
    {
        _regionManager = regionManager;
    }

    public void Initialize()
    {
        CreateNavigationTabAndRegisterModule();
    }

    private void CreateNavigationTabAndRegisterModule()
    {
        RadOutlookBarItem configurationModuleTab = new RadOutlookBarItem();
        configurationModuleTab.Header = "Configuration";       
        configurationModuleTab.IsSelected = true;

        configurationModuleTab.Content = CreateModulePanel();
        _regionManager.Regions["NavigationRegion"].Add(configurationModuleTab);
    }

    StackPanel CreateModulePanel()
    {
        StackPanel configurationPanel = new StackPanel();
        configurationPanel.Orientation = Orientation.Vertical;
        configurationPanel.Height = 450;
        configurationPanel.Children.Add(CreateModuleLinkItems("User Profile", () => OnUserProfileClicked()));
 //Add more links
        return configurationPanel;
    }

    HyperlinkButton CreateModuleLinkItems(string content, Action command)
    {
        HyperlinkButton configurationLinkItem = new HyperlinkButton();
        configurationLinkItem.HorizontalAlignment = HorizontalAlignment.Left;
        configurationLinkItem.Margin = new Thickness(5D, 5D, 0D, 5D);
        configurationLinkItem.Content = content;
        configurationLinkItem.Click += new RoutedEventHandler((x, y) => command.Invoke());
        return configurationLinkItem;
    }
    //Other methods…

    IRegionManager _regionManager;
}


Output

4 comments:

Michael said...

im a little new at this...where is onUserProfile defined? In the ViewModel?

Prajeesh said...

User Profile is the link button on the navigation panel and specific to the module. So I define this in the controller file or the module.cs file. When you click the link, a view is loaded which displays ui components for the user profile and you can have a view model for that view.

Michael said...

I'm still getting a region doesn't exist error. which I'm guessing means the adapter isnt working?. I'm confident I've followed your code, though I'm using MEF to set the dependencies on creating the adapters. (But I've tried it both ways)

I've followed it and it runs through the code registering the mappings which leaves me with perhaps my XAML?? :

Prajeesh said...

in the shell.xaml file you should register the region

telerik:RadOutlookBar Name="NavigationRegion" Regions:RegionManager.RegionName="NavigationRegion"
Width="200" toolkit:DockPanel.Dock="Left"