There are some Ribbon frameworks which allow using it on Winforms, but it seems not so easy to customize them. 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.
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 Richtextbox
es.
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")
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
) Ribbon
Tabs Start, Ribbon
Settings 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