如何动态创建Ribbon选项卡?

6
我想使用PrismV4、MEF和Ribbon开始开发新应用程序。但是,我现在遇到了一个问题。如何动态创建Ribbon选项卡?应用程序中的每个模块都可以创建自己的Ribbon选项卡。每个选项卡可能有许多组。
如何做到这一点? 我需要将每个组的定义放在哪里(要使用什么控件(按钮、文本框、组合框等)和命令绑定,以及如何实现)?
我需要在模块中某个地方编写XAML代码吗,还是全部都可以通过代码完成? 最后一个问题,如何通知Ribbon(在Shell中)添加这些选项卡到Ribbon中?我应该使用EventAggregator从模块向Shell通信吗?还是其他方法?
3个回答

3
对于非上下文选项卡,我最喜欢的方法是通过动态加载组件(例如通过反射)来解决此问题,这些组件包括绑定到命令和VM(或控制器)的XAML,并执行该命令绑定。
对于上下文选项卡,我最喜欢的方法是包含一个模型到视图模型映射的字典,然后按名称激活/停用上下文选项卡,使用上述方法加载它们(并将正确的数据上下文传递给它们-视图模型)。
伪代码应该是这样的(取决于您的框架实现和您需要自己实现的内容):
// 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;
}

谢谢您的回答。您能提供一个小的代码示例吗? - Lari13
代码的变化取决于您使用的框架,我可以添加一个伪代码示例。 - Danny Varod
我使用带有MEF的PrismV4。我觉得伪代码也可以 :) 谢谢 - Lari13

3
我认为您需要使用区域适配器(用于将非标准(默认不支持的控件)视为区域)。

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

上面的链接可以作为一个很好的起点。 然后,您可以将标签注册为视图(我不确定,因为我没有使用过功能区)。
否则,您可以公开一个服务IRibbonService,不同的模块在它们的构造函数中使用它,然后您可以调用AddTab / AddGroup等方法。

谢谢。这可能是关于模块和“MainModule”之间通信的最后一个问题的答案。我会检查一下。每个模块中创建这个RibbonTab怎么样?实现它的正确方式是什么? - Lari13
使用“IRibbonService”的解决方案似乎非常好。谢谢! - Lari13

1
创建一个用于Ribbon标签的视图: 视图:
<!-- 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
    }
}

模块类:

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");
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接