WinForm VB.NET Hosting WPF Ribbon
Host WPF usercontrol with Ribbon within winforms VB.NET project
Introduction
This article and the demo were inspired by the C# WPF WYSIWYG HTML Editor - CodeProject article, which shows a beautiful Ribbon UI and gave me some ideas, code snippets and images.
So my idea was to host a WPF usercontrol with Ribbon within a winforms VB.NET project.
This is no richtext editor but only a demo with example menu items and demo code.
Background
I did not want to „clone“ the HTML editor from the mentioned article and decided to use a richtextcontrol on the winform and make a WPF Ribbon with menuitems and buttons to control it.
The structure on the winform is easy.
There is a splitcontainer, panel1 (on top) with the ElementHost control.
Panel2 (on bottom) with the tabcontrol and Richtextboxes.

Winform Concept and Code
As soon as the WPF Usercontrol is available, it appears on the toolbox.

When you drag it from the toolbox to the Splitcontainer Panel1, a ElementHost control is placed there, which hosts the usercontrol.

The code shows that we use a variable MyRibbonUserControl on the winform to get easy access to the WPF usercontrol.
Public Class AppForm
Inherits Form
Private WithEvents MyRibbonUserControl As New UserControlRibbonWPF
Private blnIsMinimized As Boolean
Private Sub AppForm_Load(sender As Object, e As EventArgs) Handles Me.Load
Try
ElementHost1.Dock = DockStyle.Fill
MyRibbonUserControl = ElementHost1.Child
Some of the errors which may happen are written to a log file, which we use as data source for the RTBox.
In Module Mod_Public, we declare:
Public WithEvents activeRichTextBox As RichTextBox
It is used in the winform for:
Private Sub RichTextBox_Enter(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles WinformRichTextBox.MouseClick,
WinformRichTextBox.Enter, WinformRichTextBox.MouseEnter,
WinformRichTextBox.TextChanged,
LogRichTextBox.MouseClick, LogRichTextBox.Enter,
LogRichTextBox.MouseEnter, LogRichTextBox.TextChanged
activeRichTextBox = CType(sender, RichTextBox)
End Sub
When the app is shown, we could do some Configuration | Settings for WPF Ribbon as well.
Private Sub AppForm_Shown(sender As Object, e As EventArgs) Handles Me.Shown
Try
activeRichTextBox = WinformRichTextBox
activeRichTextBox.Text = ReadTextLines("Log.txt")
' Configuration | Settings for WPF Ribbon
With MyRibbonUserControl
blnIsMinimized = .RibbonSizeIsMinimized
.TabHelp.IsEnabled = True
.RibbonWPF.Visibility = Windows.Visibility.Visible
The remaining code on the winform is mostly related to ribbon settings which will be presented below.
WPF usercontrol Concept and Code
The Ribbon includes
- Application Menu
- QAT (
QuickAccessToolbar) RibbonTabs Start,RibbonSettings and Help
Application Menu

QAT (QuickAccessToolbar)

You can remove buttons from the QAT (on right click, a context menu appears for that).

And you can show QAT below the Ribbon:

You can Restore QAT from Settings Tab as well.
And you can change backcolor of the ribbon.
Ribbon Tabs Start, Insert, Ribbon Settings and Help

The Ribbon can be minimized via Context menu or doubleclick on a tab header.

VB.NET Code in UserControlRibbonWPF.xaml.vb
Public Sub New() shows that commandBinding is used which I found here.
Public Sub New()
Dim binding As CommandBinding
binding = New CommandBinding(ApplicationCommands.[New])
AddHandler binding.Executed, AddressOf New_Click
Me.CommandBindings.Add(binding)
binding = New CommandBinding(ApplicationCommands.Open)
AddHandler binding.Executed, AddressOf Open_Click
Me.CommandBindings.Add(binding)
binding = New CommandBinding(ApplicationCommands.Close)
AddHandler binding.Executed, AddressOf CloseCommand
Me.CommandBindings.Add(binding)
binding = New CommandBinding(ApplicationCommands.Print)
AddHandler binding.Executed, AddressOf PrintCommand
Me.CommandBindings.Add(binding)
binding = New CommandBinding(ApplicationCommands.SaveAs)
AddHandler binding.Executed, AddressOf SaveAs_Click
Me.CommandBindings.Add(binding)
End Sub
The code for the other button events on the WPF side is almost the same as it would be on the winform side.
For handling the RTBox content, the above noted variable activeRichTextBox can be used.
Example:
Private Sub LeftAlignRibbonButton_Click(sender As Object, e As Windows.RoutedEventArgs) _
Handles LeftAlignRibbonButton.Click
If activeRichTextBox IsNot Nothing Then If activeRichTextBox.SelectedText.Length = 0 _
Then Exit Sub
If activeRichTextBox IsNot Nothing Then activeRichTextBox.SelectionAlignment = _
System.Windows.Forms.HorizontalAlignment.Left
End Sub
XAML File UserControlRibbonWPF.xaml
The part with the application menu:
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu CanAddToQuickAccessToolBarDirectly="True">
<RibbonApplicationMenuItem x:Name="AppCmdNew" Header="New"
Command="ApplicationCommands.New" IsCheckable="False"
ImageSource="Images/newdocument32.png"
QuickAccessToolBarImageSource="Images/newdocument32.png"
CanAddToQuickAccessToolBarDirectly="False" />
<RibbonApplicationMenuItem x:Name="AppCmdOpen" Header="Open"
Command="ApplicationCommands.Open" IsCheckable="False"
ImageSource="Images/open16.png"
QuickAccessToolBarImageSource="Images/open16.png"
CanAddToQuickAccessToolBarDirectly="False" />
<RibbonApplicationMenuItem x:Name="AppCmdSaveAs"
Header="Save As" Command="ApplicationCommands.SaveAs"
IsCheckable="False" ImageSource="Images/save16.png"
QuickAccessToolBarImageSource="Images/save16.png"
CanAddToQuickAccessToolBarDirectly="False" />
<RibbonApplicationMenuItem x:Name="AppCmdPrint" Header="Print"
Command="ApplicationCommands.Print" IsCheckable="False"
ImageSource="Images/print.png"
QuickAccessToolBarImageSource="Images/print.png"
CanAddToQuickAccessToolBarDirectly="False" />
<RibbonApplicationMenuItem x:Name="AppCmdClose"
Header="Close App" Command="ApplicationCommands.Close"
ImageSource="Images/close.png"
QuickAccessToolBarImageSource="Images/close.png"
CanAddToQuickAccessToolBarDirectly="False" />
</RibbonApplicationMenu>
</Ribbon.ApplicationMenu>
The part with the QAT:
<Ribbon.QuickAccessToolBar>
<RibbonQuickAccessToolBar HorizontalAlignment="Right" Width="120">
<RibbonButton x:Name="AppCmdNewQAT" Command="ApplicationCommands.New"
SmallImageSource="Images\newdocument32.png" KeyTip="N" />
<RibbonButton x:Name="AppCmdOpenQAT" Command="ApplicationCommands.Open"
SmallImageSource="Images\open16.png" KeyTip="O"/>
<RibbonButton x:Name="AppCmdSaveAsQAT" Command="ApplicationCommands.SaveAs"
SmallImageSource="Images\save16.png" KeyTip="S"/>
<RibbonButton x:Name="AppCmdCloseQAT" Command="ApplicationCommands.Close"
SmallImageSource="Images\close.png"/>
</RibbonQuickAccessToolBar>
</Ribbon.QuickAccessToolBar>
And the first part of the EditorTab" ("Start"):
<RibbonTab x:Name="EditorTab" Header="Start" Height="97" Margin="0,0,-2,-9"
RenderTransformOrigin="0.5,0.682" KeyTip="E">
<RibbonGroup Header="File" Height="92" Margin="0"
VerticalAlignment="Top" Width="199" FontFamily="Arial"
CanAddToQuickAccessToolBarDirectly="False">
<RibbonButton x:Name="New" Content="RibbonButton"
HorizontalAlignment="Left" Height="53" Margin="10,2,-86,-53"
VerticalAlignment="Top" Width="80" Label="New"
LargeImageSource="Images/newdocument32.png" ToolTipTitle=""
ToolTip="New" KeyTip="N" CanAddToQuickAccessToolBarDirectly="False" />
<RibbonButton x:Name="Open" Content="RibbonButton"
HorizontalAlignment="Left" Height="22" Margin="95,0,-176,-26"
VerticalAlignment="Top" Width="85" Label=" Open"
SmallImageSource="Images/open16.png" ToolTipTitle=""
ToolTip="Open" KeyTip="O" CanAddToQuickAccessToolBarDirectly="False" />
<RibbonButton x:Name="SaveAs" Content="RibbonButton"
HorizontalAlignment="Left" Height="Auto" Margin="94,24,-162,-70"
VerticalAlignment="Top" Width="80" Label=" Save As" KeyTip="S"
AutomationProperties.AccessKey="S" AutomationProperties.AcceleratorKey="S"
SmallImageSource="Images/save16.png"
CanAddToQuickAccessToolBarDirectly="False"/>
<RibbonButton x:Name="Save" Content="RibbonButton"
HorizontalAlignment="Left" Height="Auto" Margin="94,46,-162,-70"
VerticalAlignment="Top" Width="80" Label=" Save" KeyTip="S"
AutomationProperties.AccessKey="S" AutomationProperties.AcceleratorKey="S"
SmallImageSource="Images/save16.png" Visibility="Hidden"
CanAddToQuickAccessToolBarDirectly="False"/>
</RibbonGroup>
<RibbonGroup Header="Clipboard" Margin="0" Width="100" FontFamily="Arial"
VerticalAlignment="Top" Height="92" CanAddToQuickAccessToolBarDirectly="False">
<RibbonButton x:Name="ClipboardCmdCut" Label=" Cut"
SmallImageSource="Images/cut16.png" Margin="0,2,0,0"
CanAddToQuickAccessToolBarDirectly="False"/>
<RibbonButton x:Name="ClipboardCmdCopy" Label=" Copy"
SmallImageSource="Images/copy16.png" KeyTip="C" Margin="0,2,-1.708,0"
Height="Auto" CanAddToQuickAccessToolBarDirectly="False"/>
<RibbonButton x:Name="ClipboardCmdPaste" Label=" Paste"
SmallImageSource="Images/paste16.png" Margin="0,2,0,0"
CanAddToQuickAccessToolBarDirectly="False"/>
</RibbonGroup>
Saving QAT State when Closing the App
VB.NET code in AppForm.vb:
Private Sub AppForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
Try
If MyRibbonUserControl.AppCmdNewQAT.IsLoaded = False Then
My.Settings.AppCmdNewQAT_Visible = False
ElseIf MyRibbonUserControl.AppCmdNewQAT.IsLoaded = True Then
My.Settings.AppCmdNewQAT_Visible = True
End If
If MyRibbonUserControl.AppCmdOpenQAT.IsLoaded = False Then
My.Settings.AppCmdOpenQAT_Visible = False
ElseIf MyRibbonUserControl.AppCmdopenQAT.IsLoaded = True Then
My.Settings.AppCmdOpenQAT_Visible = True
End If
If MyRibbonUserControl.AppCmdSaveAsQAT.IsLoaded = False Then
My.Settings.AppCmdSaveAsQAT_Visible = False
ElseIf MyRibbonUserControl.AppCmdSaveAsQAT.IsLoaded = True Then
My.Settings.AppCmdSaveAsQAT_Visible = True
End If
If MyRibbonUserControl.AppCmdCloseQAT.IsLoaded = False Then
My.Settings.AppCmdCloseQAT_Visible = False
ElseIf MyRibbonUserControl.AppCmdcloseQAT.IsLoaded = True Then
My.Settings.AppCmdCloseQAT_Visible = True
End If
Project Settings

Restore QAT State when Winform Loads
Private Sub AppForm_Load(sender As Object, e As EventArgs) Handles Me.Load
If My.Settings.AppCmdNewQAT_Visible = False Then
MyRibbonUserControl.RibbonWPF.QuickAccessToolBar.Items.Remove(MyRibbonUserControl.AppCmdNewQAT)
End If
If My.Settings.AppCmdOpenQAT_Visible = False Then
MyRibbonUserControl.RibbonWPF.QuickAccessToolBar.Items.Remove(MyRibbonUserControl.AppCmdOpenQAT)
End If
Restore QAT During User Session (from Tab ribbon Settings):
VB.NET code in UserControlRibbonWPF.xaml.vb
Private Sub RestoreQAT_Click(sender As Object, e As RoutedEventArgs) _
Handles RestoreQATRibbonButton.Click
If AppCmdNewQAT.IsLoaded = False Then
RibbonWPF.QuickAccessToolBar.Items.Add(AppCmdNewQAT)
End If
If AppCmdOpenQAT.IsLoaded = False Then
RibbonWPF.QuickAccessToolBar.Items.Add(AppCmdOpenQAT)
End If
If AppCmdSaveAsQAT.IsLoaded = False Then
RibbonWPF.QuickAccessToolBar.Items.Add(AppCmdSaveAsQAT)
End If
If AppCmdCloseQAT.IsLoaded = False Then
RibbonWPF.QuickAccessToolBar.Items.Add(AppCmdCloseQAT)
End If
End Sub
Known Bugs
After minimizing the ribbon, it may not appear complete when you use the context menu to maximize it.
As a workaround, doubleclick on a tab header can be used to minimize/maximize the ribbon.
Conclusion
I hope this demo shows that it would be possible to upgrade a Winforms app with a WPF Ribbon without too many changes regarding the data structure. I also think that with Command Bindings, a helpful Command interface should be possible.
History
- 1st January, 2022 - Initial submission
- 1st January, 2022 - Added download link

