Pregunta

I am looking for a way to provide a pluggable way to add items to the .NET 4.5 ribbon, and have made a few strides in doing so, but not quite the way I'd like it:

[InheritedExport]
public interface IRibbonItem
{
    void Compose(DesignerRibbon ribbon);
}

public class TestItem : IRibbonItem
{
    public void Compose(CustomRibbon ribbon)
    {
        ribbon.ApplicationMenu.Items.Add((new RibbonApplicationMenuItem() { Header = "_Hello, World!" }));
    }
}

public class MEFRibbon : System.Windows.Controls.Ribbon.Ribbon
{
    [ImportMany]
    private IEnumerable<IRibbonItem> _oRibbons = null;

    public MEFRibbon() : base()
    {
         this.ApplicationMenu = new RibbonApplicationMenu();
         MEFHelper.Instance.ComposeParts(this);
         this._oRibbons.ToList().ForEach(x => x.Compose(this));
    }
}

While this works, it seems messy. I'd prefer to write all of the plugins' content in XAML. Is there a way I can achieve this goal?

Everything I've looked at references the old Ribbon or some other library.

Thanks.

¿Fue útil?

Solución

I had the same issues so far. The goals I've planned to achieve:

  • The application should be extensible via plugins.
  • Application plugins should be able to add their own UI content into predefined UI regions: ribbon tabs, ribbon application menu, ribbon quick access toolbar and status bar.
  • The application should be built within MVVM architecture as close, as it possible.
  • Plugin developer should be able to describe UI content in XAML. There shouldn't be any view model types, which reproduce hierarchy of ribbon controls (such as RibbonTabViewModel, RibbonButtonViewModel, etc), because this leads to parallel hierarchy of classes, and limits the abilities of XAML.
  • Plugins should use MEF to be loaded into host application.

Here's the approach I've used.
All my plugins are decorated with [ApplicationExtension(...)] and implement IApplicationExtension:

public interface IApplicationExtension
{
    void Startup();
    bool CanShutdown();
    void Shutdown();
}

ApplicationExtension is declared as:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class ApplicationExtensionAttribute : ExportAttribute, IApplicationExtensionMetadata
{
    public ApplicationExtensionAttribute(/* ... */)
        : base(typeof(IApplicationExtension))
    {
        this.Name = name;
        this.UICompositionOrder = uiCompositionOrder;
    }

   // ...
}

IApplicationExtensionMetadata - some metadata, such as display name, etc.

Host application, in turn, implements this interface:

public interface IApplicationInfrastructureProvider : IInfrastructureProvider
{
    ICollection<ViewModel> RibbonTabs { get; }
    ICollection<ViewModel> ApplicationMenuItems { get; }
    ICollection<ViewModel> QuickAccessToolbarItems { get; }
    ICollection<ViewModel> StatusBarItems { get; }
}

where ViewModel - some base view model class, and loads plugins via MEF:

    [ImportMany]
    private IEnumerable<Lazy<IApplicationExtension, IApplicationExtensionMetadata>> Extensions { get; set; }

When application initializes, it calls Startup method for each plugin.
Here plugin implementation is able to add tabs, menu items, etc. via IApplicationInfrastructureProvider as view models to extend application's UI.

Note, that these view models are not any RibbontTabViewModel or RibbonButtonViewModel. They are just some pieces of functionality.

How it works at the UI side.
When, for example, RibbonTabs collection is changed, application calls custom service, which performs this job:

  • First, it lookups a resource dictionary for the view model. For simplicity, I'm using naming conventions: if the type of tab view model is MyCustomVm, the service looks for MyCustom.xaml in the same assembly.
  • Second, the service explores resource dictionary and looks for resource, whose key ends with _RibbonTabKey. View model becomes the data context of found resource. This assumes, that resource is at least FrameworkElement.
  • Third, the found resource is being added to RibbonTabsUI collection of host application.

The ribbon is bound to the RibbonTabsUI in XAML for host application:

<r:Ribbon x:Name="ribbon" Grid.Row="0" ItemsSource="{Binding RibbonTabsUI}">
    <!-- other content -->
</r:Ribbon>

The sample of ribbon tab looks like this:

<r:RibbonTab x:Key="MyCustom_RibbonTabKey" x:Shared="False">
    <r:RibbonGroup Header="Some group">
        <!-- other content -->
    </r:RibbonGroup>
    <!-- other content -->
</r:RibbonTab>

ApplicationMenuItems and QuickAccessToolbarItems are being processed the same way, the difference is the resource key suffix.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top