6

I want to start development of new application using PrismV4, MEF, Ribbon. But now, I have a problem. How to create tabs for Ribbon dynamically? Each module in application could create own tab in Ribbon. And each tab may have many groups.

How can it be done? Where do I need to place each group's definitions (what controls to use (buttons, textboxes, comboboxes, etc) and command bindings and how?

Do I need to write XAML somewhere in Module, or all it can be done by code? And last question, how to notify Ribbon (in Shell) to add these tabs to Ribbon? Shall I use EventAggregator to communicate from Module to Shell? Or?

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Lari13
  • 1,850
  • 10
  • 28
  • 55

3 Answers3

3

I think you'll have to use Region Adapter (for treating non-standard (not supported out-of-the-box controls as regions)

http://msdn.microsoft.com/en-us/library/dd458901.aspx

Above link can be a good starting point. Then you can register your tabs as views perhaps (I'm not sure because I haven't used ribbon).

Otherwise you can expose a service IRibbonService that different modules take in their constructor and you can then call methods like AddTab / AddGroup.

Muhammad Hasan Khan
  • 34,648
  • 16
  • 88
  • 131
  • Thanks. It might be answer to the last question about commincation between Module and "MainModule". I'll check it. What is about to create this RibbonTab in each Module? How is the right way to implement it? – Lari13 Dec 29 '10 at 12:02
  • The solution with 'IRibbonService' seems to be very nice. Thanks! – Lari13 Dec 29 '10 at 12:09
3

For non-contextual tabs, my favorite approach for this problem is to dynamically load components (via reflection for instance) that include XAMLs with binding to commands and VMs (or controllers) that include the command implementations and perform that command bindings.

For contextual tabs, my favorite approach is to include a dictionary of Model to ViewModel mappings, then activate/deactivate contextual tabs by name, that were loaded using the above approach (and pass the correct data context to them - the view model).

Pseudo code should be more or less like this (depending on what your frameworks implement and what you have to implement yourself):

// Deserialize UI controllers from configuration files
// Each controller should act as view-model for its UI elements

// Register controllers with UI Manager
foreach controller in config.UiControllers uiManager.AddController(controller);


void UiManager.AddController(UiController controller)
{
    // Load controller's tool windows
    foreach toolWindow in contoller.toolWindows
    {
         toolWindow.LoadResources();
         toolWindow.DataContext = controller;
         mainWindow.AddToolWindow(toolWindow, contoller.PreferedUiRegion);
    }

    // Load controller's toolbars
    foreach toolBar in controller.ToolBars
    {
         toolBar.DataContext = controller;
         mainWindow.AddToolBar(toolBar);
    }

    // Load controller's contextual toolbar groups
    foreach group in controller.ContextualToolBarGroups
    {
         group.DataContext = controller;
         mainWindow.AddContextualToolBarGroupr(group);
    }

    // Load view models for specific model types
    foreach item in controller.ViewModelsDictionary
    {
         this.RegisterViewModelType(item.ModelType, item.ViewModelType, item.ViewType);
    }
}


void UiManager.OnItemSelected(Object selectedItem)
{
    var viewModelType = GetViewModelTypeFromDictionary(selectedItem);
    var viewType = GetViewTypeFromDictionary(selectedItem) ?? typeof(ContentPresentor);

    var viewModel = Reflect.CreateObject(viewModelType);
    var view = Reflection.CreateObject(viewType);

    viewModel.Model = selectItem;
    view.DataContext = viewModel;

    // Enable activation of contextual tab group on activate of view (if user clicks on it)
    view.OnActivatedCommandParameter = viewModel.ContextualTabGroupName;

    // This command should ask mainWindow to find contextual tab group, by name, and activate it
    view.OnActivatedCommand = ModelActivatedCommand;

    mainWindow.DocumentArea.Content = view;
}
Danny Varod
  • 17,324
  • 5
  • 69
  • 111
1

Create a view for ribbon tab: view:

<!-- See code-behind for implementation of IRegionMemberLifetime interface. This interface
causes the RibbonTab to be unloaded from the Ribbon when we switch views. -->

<!--<ribbon:RibbonGroup Header="Group B1">
    <ribbon:RibbonButton LargeImageSource="Images\LargeIcon.png" Label="Button B1" />
    <ribbon:RibbonButton SmallImageSource="Images\SmallIcon.png" Label="Button B2" />
    <ribbon:RibbonButton SmallImageSource="Images\SmallIcon.png" Label="Button B3" />
    <ribbon:RibbonButton SmallImageSource="Images\SmallIcon.png" Label="Button B4" />
</ribbon:RibbonGroup>-->

cs:

using Microsoft.Practices.Prism.Regions;
//using Microsoft.Windows.Controls.Ribbon;

namespace Prism4Demo.ModuleB.Views
{
    /// <summary>
    /// Interaction logic for ModuleBRibbonTab.xaml
    /// </summary>
    public partial class ModuleBRibbonTab :  IRegionMemberLifetime
    {
        #region Constructor

        public ModuleBRibbonTab()
        {
            InitializeComponent();
        }

        #endregion

        #region IRegionMemberLifetime Members

        public bool KeepAlive
        {
            get { return false; }
        }

        #endregion
    }
}

Module class:

public class ModuleC : IModule
    {
        #region IModule Members

        /// <summary>
        /// Initializes the module.
        /// </summary>
        public void Initialize()
        {
            /* We register always-available controls with the Prism Region Manager, and on-demand 
             * controls with the DI container. On-demand controls will be loaded when we invoke
             * IRegionManager.RequestNavigate() to load the controls. */

            // Register task button with Prism Region
            var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
            regionManager.RegisterViewWithRegion("TaskButtonRegion", typeof(ModuleBTaskButton));

            /* View objects have to be registered with Unity using the overload shown below. By
             * default, Unity resolves view objects as type System.Object, which this overload 
             * maps to the correct view type. See "Developer's Guide to Microsoft Prism" (Ver 4), 
             * p. 120. */

            // Register other view objects with DI Container (Unity)
            var container = ServiceLocator.Current.GetInstance<IUnityContainer>();
            container.RegisterType<Object, ModuleBRibbonTab>("ModuleBRibbonTab");
            container.RegisterType<Object, ModuleBNavigator>("ModuleBNavigator");
            container.RegisterType<Object, ModuleBWorkspace>("ModuleBWorkspace");
        }
Yuriy Vikulov
  • 2,469
  • 5
  • 25
  • 32