Click here to Skip to main content
Click here to Skip to main content
Go to top

UIExtensionSites for PRISM

, 4 Aug 2010
Rate this:
Please Sign up or sign in to vote.
Classes to add a UIExtensionSites-like mechanism to PRISM.

Introduction

PRISM is a great library to develop WPF and Silverlight applications with. Yet, coming from the CAB (Microsoft Composite UI Application Blocks) library, I was missing the UIExtensionSites functionality. As it wasn't provided by PRISM and I needed it, I decided to write it myself (and subsequently share it).

The source code

The solution is a VS2010 solution. Note: to compile the code, you'll have to install the PRISM library from CodePlex first.

The code consists of four classes:

  • UIExtensionSiteManager: this is the main class, and contains the code to add the attached properties, and keeps track of the UIExtensionSites.
  • IUIExtensionSiteAdapter: classes that implement this interface 'adapt' a control to a UIExtensionSite.
  • UIExtensionSiteMappings: contains the mappings between the control types and the IUIExtensionSiteAdapter.
  • IUIExtensionSite: classes that implement this interface allow controls to be added to the UIExtensionSite.

UIExtensionSiteManager

The UIExtensionSiteManager defines the 'UIExtensionSite' attached property:

public static readonly DependencyProperty UIExtensionSiteProperty = 
  DependencyProperty.RegisterAttached(
    "UIExtensionSite",
    typeof(string),
    typeof(UIExtensionSiteManager),
    new PropertyMetadata(OnSetUIExtensionSiteCallback));

public static void SetUIExtensionSite(DependencyObject siteTarget, string siteName)
{
    siteTarget.SetValue(UIExtensionSiteProperty, siteName);
}

public static string GetUIExtensionSite(DependencyObject siteTarget)
{
    return siteTarget.GetValue(UIExtensionSiteProperty) as string;
}

private static void OnSetUIExtensionSiteCallback(DependencyObject element, 
               DependencyPropertyChangedEventArgs args)
{
    if (!IsInDesignMode(element))
    {
        IServiceLocator locator = ServiceLocator.Current;
        UIExtensionSiteManager manager = 
          locator.GetInstance<UIExtensionSiteManager>();

        UIExtensionSiteMappings mappings = 
          locator.GetInstance<UIExtensionSiteMappings>();
        IUIExtensionSiteAdapter adapter = mappings.GetMapping(element.GetType());
        IUIExtensionSite site = adapter.Adapt(element);
        manager.AddUIExtensionSite(
          (string)element.GetValue(UIExtensionSiteProperty), site);
    }
}

All controls in WPF/Silverlight can use the UIExtensionSiteManager.UIExtensionSite property to define an extension site.

The second role of the manager is to keep track of the extension sites. The method 'GetUIExtensionSite' allows you to retrieve an extension site.

The UIExtensionSiteManager must be registered in the Unity container at the start of the application. A good place is the ConfigureContainer method in the Unity bootstrapper:

protected override void ConfigureContainer()
{
    Container.RegisterType<UIExtensionSiteManager>(
        new Microsoft.Practices.Unity.ContainerControlledLifetimeManager(), 
        new Microsoft.Practices.Unity.InjectionMember[] {});            
    base.ConfigureContainer();
}

IUIExtensionSiteAdapter

The IUIExtensionSiteAdapter is an interface that defines what operations the adapter must implement. There's only one method: Adapt. This method accepts a DependencyObject and returns an object that implements IUIExtensionSite. The Adapt method must check if the type of the given DependencyObject matches the type that can be adapted.

A matching IUIExtensionSiteAdapter must be defined for each control that must be declared as a UIExtensionSite.

E.g., if you want to define a ComboBox as a UIExtensionSite, you'll have to provide a IUIExtensionSiteAdapter that 'adapts' a ComboBox to a fitting IUIExtensionSite object.

public class ComboBoxUIExtensionSiteAdapter : IUIExtensionSiteAdapter
{
    public IUIExtensionSite Adapt(DependencyObject element)
    {
        if (element is ComboBox)
            return new ComboBoxUIExtensionSite(element as ComboBox);
        else
            throw new ArgumentException("ComboBoxUIExtensionSiteAdapter " + 
                                        "can only adapt ComboBox objects !");
    }
}

UIExtensionSiteMappings

This class is a collection of types and their matching IUIExtensionSiteAdapter. A mapping should be provided for each IUIExtensionSiteAdapter.

E.g., if you want to define a ComboBox as a UIExtensionSite, you'll have to add a mapping for the ComboBox and its matching IUIExtensionSiteAdapter. This is done in the UnityBootstrapper of your PRISM application.

protected override void ConfigureContainer()
{
    Container.RegisterType<UIExtensionSiteManager>(
        new Microsoft.Practices.Unity.ContainerControlledLifetimeManager(), 
        new Microsoft.Practices.Unity.InjectionMember[] {});
    ConfigureUIExtensionSiteAdapterMappings();
            
    base.ConfigureContainer();
}

protected void ConfigureUIExtensionSiteAdapterMappings()
{
    UIExtensionSiteMappings mappings = new UIExtensionSiteMappings();
    Container.RegisterInstance(mappings, 
      new Microsoft.Practices.Unity.ContainerControlledLifetimeManager());
    mappings.RegisterMapping(typeof(System.Windows.Controls.ComboBox), 
      this.Container.Resolve<ComboBoxUIExtensionSiteAdapter>());
}

IUIExtensionSite

An extension site must implement this interface. It defines a 'wrapper' around the DependencyObject that is the extension site, and defines one method: Add. The code in this method should add the given FrameworkElement to the extension site.

E.g., if you want to define a ComboBox as a UIExtensionSite, you'll have to create a class that implements the IUIExtensionSite interface and provides a wrapper for the Items.Add() method of the ComboBox.

public class ComboBoxUIExtensionSite : IUIExtensionSite
{
    private ComboBox _combo;

    public ComboBoxUIExtensionSite(ComboBox combo)
    {
        this._combo = combo;
    }

    public void Add(FrameworkElement element)
    {
        _combo.Items.Add(element);
    }

    public DependencyObject Target
    {
        get { return _combo; }
    }
}

Using the UIExtensionSites

You can use the UIExtensionSite by adding an attached property to you controls. E.g., if you want to define a ComboBox as a UIExtensionSite:

<ComboBox uiext:UIExtensionSiteManager.UIExtensionSite="ProjectCombo"></ComboBox>

In your modules, all you have to do is to retrieve a reference to the UIExtensionSite and add your items to it. E.g., to add items to the previously defined ComboBox:

UIExtensionSiteManager manager = container.Resolve<UIExtensionSiteManager>();
manager.GetUIExtensionSite("ProjectCombo").Add(new ComboBoxItem() { Content = "Project A" });
manager.GetUIExtensionSite("ProjectCombo").Add(new ComboBoxItem() { Content = "Project B" });
manager.GetUIExtensionSite("ProjectCombo").Add(new ComboBoxItem() { Content = "Project C" });

A sample using the DevExpress Silverlight toolbar

As I'm using the DevExpress components, I've created a UIExtensionSite for the DevExpress toolbar. But I think the following code might quite easily be adapted to fit other third-party components.

I started by creating a DXBarUIExtensionSite and a DXBarUIExtensionSiteAdapter:

public class DXBarUIExtensionSite : IUIExtensionSite
{
    private DevExpress.Xpf.Bars.Bar _bar;

    public DXBarUIExtensionSite(DevExpress.Xpf.Bars.Bar bar)
    {
        if (bar == null)
            throw new ArgumentException("DXBarUIExtensionSite must " + 
                              "be initialized with a valid Bar object !");
        _bar = bar;
    }

    public void Add(FrameworkElement element)
    {
        if (!(element is DevExpress.Xpf.Bars.BarItem))
            throw new ArgumentException("element should be of type BarItem !");

        DevExpress.Xpf.Bars.BarItem item = element as DevExpress.Xpf.Bars.BarItem;

        DevExpress.Xpf.Bars.BarManager barmanager = _bar.Manager;
        if (!barmanager.Items.Contains(item))
            barmanager.Items.Add(item);
        _bar.ItemLinks.Add(item);
    }

    public DependencyObject Target
    {
        get { return _bar; }
    }
}

public class DXBarUIExtensionSiteAdapter : IUIExtensionSiteAdapter
{
    public IUIExtensionSite Adapt(DependencyObject element)
    {
        return new DXBarUIExtensionSite(element as DevExpress.Xpf.Bars.Bar);
    }
}

The next step was to define a toolbar in my Shell as a UIExtensionSite (in XAML):

<dxb:BarManager Name="barManager" CreateStandardLayout="True">
    <dxb:BarManager.Items>
        </dxb:BarManager.Items>
        <dxb:BarManager.Bars>
                <dxb:Bar Caption="MainMenu" x:Name="MainMenu" 
                        IsMainMenu="True" UseWholeRow="True"
                        uiext:UIExtensionSiteManager.UIExtensionSite="MainMenu">
                    <dxb:Bar.DockInfo>
                        <dxb:BarDockInfo ContainerType="Top"/>
                    </dxb:Bar.DockInfo>
                </dxb:Bar>
        </dxb:BarManager.Bars>
</dxb:BarManager>

The final step was to add buttons to my UIExtensionSite. Note that this sample also includes a binding to a Command:

DelegateCommand<string> openrepositorycmd = new DelegateCommand<string>(
    s => regionManager.RegisterViewWithRegion("MainTab", () => 
    container.Resolve<RepositoryListPresenter>().View));

Binding binding = new Binding();
binding.Source = openrepositorycmd;

System.Windows.Media.Imaging.BitmapImage image =
    new System.Windows.Media.Imaging.BitmapImage(
    new Uri("/RepositoryModule;component/Img/anchor16.png", UriKind.Relative));
DevExpress.Xpf.Bars.BarButtonItem button = new DevExpress.Xpf.Bars.BarButtonItem()
    {
        Content="Go !",
        Hint="This is a testbutton",
        Glyph=image   
    };
button.SetBinding(DevExpress.Xpf.Bars.BarButtonItem.CommandProperty, binding);

DevExpress.Xpf.Bars.BarSubItem repMenu = new DevExpress.Xpf.Bars.BarSubItem()
    {
        Content = "Repository"
    };
repMenu.ItemLinks.Add(button);

UIExtensionSiteManager manager = container.Resolve<UIExtensionSiteManager>();
manager.GetUIExtensionSite("MainMenu").Add(repMenu);

History

  • 2010-08-04: First version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Xavier Spileers
CEO TRI-S bvba, Cogenius bvba
Belgium Belgium
I'm working since 1999 in an IT environment: started developing in PROGRESS 4GL, then VB6 and am working since 2003 with C#. I'm currently transitioning to HTML5, CSS3 and JavaScript for the front-end development.
I started my own company (TRI-S) in 2007 and co-founded another one (Cogenius) in 2012.
Besides being a Microsoft Certified Professional Developer (MCPD) I'm also a Microsoft Certified Trainer (MCT) and am teaching .NET and JavaScript courses.
Follow on   Google+

Comments and Discussions

 
GeneralIt's working! PinmemberChop_CZ4-Sep-10 5:48 
QuestionNeeded? PinmemberMike Kenyon9-Aug-10 11:33 
AnswerRe: Needed? PinmemberXavier Spileers9-Aug-10 20:57 
GeneralRe: Needed? PinmemberMike Kenyon10-Aug-10 5:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140926.1 | Last Updated 4 Aug 2010
Article Copyright 2010 by Xavier Spileers
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid