Avalonia ribbon toolkit for desktop applications, with XAML-first composition, MVVM-friendly state flow, and optional JSON persistence for user customization state.
Full project documentation is published with Lunet at wieslawsoltes.github.io/RibbonControl.
- XAML-first, MVVM-first, and hybrid composition models.
- Rich ribbon primitives including tabs, groups, split buttons, combo boxes, toggle groups, galleries, backstage, and quick access toolbar.
- Data-driven composition with
RibbonDefinition,RibbonTab,RibbonGroup,RibbonItem, and matching definition/view-model types. - Merge-aware runtime composition through
TabsSource,GroupsSource,ItemsSource, andIRibbonMergePolicy. - Built-in key tip routing, adaptive layout, stable ribbon height support, and command-height synchronization.
- Runtime customization export/reset/load flows through
IRibbonCustomizationServiceandIRibbonStateStore. - JSON persistence package with schema versioning support and pluggable migrations.
- Automation peers, headless tests, visual regression coverage, and performance tests in the repository.
dotnet add package RibbonControl.Core
dotnet add package RibbonControl.Persistence.JsonAdd the ribbon theme resources to your Avalonia app:
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp.App">
<Application.Styles>
<FluentTheme />
<StyleInclude Source="avares://RibbonControl.Core/Themes/Generic.axaml" />
</Application.Styles>
</Application>Use the RibbonControl XML namespace in views:
xmlns:ribbon="https://github.com/wieslawsoltes/ribboncontrol"<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ribbon="https://github.com/wieslawsoltes/ribboncontrol">
<DockPanel>
<ribbon:Ribbon DockPanel.Dock="Top">
<ribbon:RibbonTab Id="home" Header="Home" Order="0">
<ribbon:RibbonGroup Id="clipboard" Header="Clipboard" Order="0">
<ribbon:RibbonItem Id="paste"
Label="Paste"
Primitive="PasteSplitButton"
IconPathData="{x:Static ribbon:FluentIconData.ClipboardPaste20Regular}"
ScreenTip="Paste from clipboard" />
<ribbon:RibbonItem Id="copy"
Label="Copy"
Primitive="Button"
IconPathData="{x:Static ribbon:FluentIconData.Copy20Regular}" />
</ribbon:RibbonGroup>
</ribbon:RibbonTab>
</ribbon:Ribbon>
</DockPanel>
</Window>Bind the control to view-model state and command services:
<ribbon:Ribbon TabsSource="{CompiledBinding Ribbon.Tabs}"
CommandCatalog="{CompiledBinding CommandCatalog}"
StateStore="{CompiledBinding StateStore}"
SelectedTabId="{CompiledBinding Ribbon.SelectedTabId, Mode=TwoWay}"
IsMinimized="{CompiledBinding Ribbon.IsMinimized, Mode=TwoWay}"
IsKeyTipMode="{CompiledBinding Ribbon.IsKeyTipMode, Mode=TwoWay}"
QuickAccessItems="{CompiledBinding QuickAccessItems}"
QuickAccessPlacement="{CompiledBinding QuickAccessPlacement, Mode=TwoWay}" />Typical service setup:
using RibbonControl.Core.Models;
using RibbonControl.Core.Services;
using RibbonControl.Persistence.Json.Storage;
using RibbonControl.Core.ViewModels;
var commandCatalog = new DictionaryRibbonCommandCatalog();
var customizationService = new RibbonCustomizationService();
var stateStore = new JsonRibbonStateStore(
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"RibbonControl",
"ribbon-state.json"));
var ribbon = new RibbonViewModel();
var homeTab = new RibbonTabViewModel
{
Id = "home",
Header = "Home"
};
var clipboardGroup = new RibbonGroupViewModel
{
Id = "clipboard",
Header = "Clipboard"
};
clipboardGroup.ItemsViewModel.Add(new RibbonItemViewModel
{
Id = "paste",
Label = "Paste",
CommandId = "paste"
});
homeTab.GroupsViewModel.Add(clipboardGroup);
ribbon.Tabs.Add(homeTab);RibbonControl.Persistence.Json ships a file-based IRibbonStateStore implementation for saving and restoring RibbonRuntimeState.
using RibbonControl.Persistence.Json.Storage;
using RibbonControl.Persistence.Json.Storage.SampleMigrations;
var stateStore = new JsonRibbonStateStore(
"ribbon-state.json",
new JsonRibbonStateStoreOptions
{
CurrentSchemaVersion = 2,
Migrations =
{
new Schema1To2QuickAccessPlacementMigration()
}
});Bind the store to Ribbon.StateStore, then use the built-in LoadStateCommand, SaveStateCommand, ResetStateCommand, and ResetCustomizationCommand commands exposed by the control.
| Project | Focus |
|---|---|
RibbonControl.Samples.XamlOnly |
Entire ribbon declared in XAML, including backstage and command presentation |
RibbonControl.Samples.MvvmOnly |
Ribbon generated from view-model state with two-way runtime synchronization |
RibbonControl.Samples.Hybrid |
Static shell in XAML with dynamic tabs, groups, and backstage content |
The package split is intentionally small:
RibbonControl.Core: controls, themes, automation peers, runtime models, MVVM view models, merge logic, key tips, and state/customization services.RibbonControl.Persistence.Json: a file-basedIRibbonStateStoreimplementation with schema versioning and migration support.
Application code references RibbonControl.Core directly and adds RibbonControl.Persistence.Json when persistent ribbon state is required.
dotnet restore RibbonControl.sln
dotnet build RibbonControl.sln -c Release
dotnet test RibbonControl.sln -c Release
dotnet pack src/RibbonControl.Core/RibbonControl.Core.csproj -c Release
dotnet pack src/RibbonControl.Persistence.Json/RibbonControl.Persistence.Json.csproj -c ReleaseThe GitHub Actions setup includes:
CI: matrix build and test on macOS, Linux, and Windows, plus NuGet package artifact generation.Docs: Lunet build and GitHub Pages deployment from the default branch.Release: tag-driven package build, NuGet.org publish, symbol package upload, and GitHub release creation.
Create a tag such as v1.0.0 to publish a versioned release through the release workflow.