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.
I had the same issues so far. The goals I've planned to achieve:
RibbonTabViewModel
, RibbonButtonViewModel
, etc), because this leads to parallel hierarchy of classes, and limits the abilities of XAML.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:
MyCustomVm
, the service looks for MyCustom.xaml
in the same assembly._RibbonTabKey
. View model becomes the data context of found resource. This assumes, that resource is at least FrameworkElement
.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.