There are Ribbon frameworks which allow using it on Winforms, but it does not seem 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 CodeProject article, Using ICommand with MVVM Pattern, which creates and uses ICommand
s according to MVVM pattern.
I used code from there as a template for my own classes / interface.
Background
My first article, WinForm VB.NET Hosting WPF Ribbon was focused on some basic work to get the demo running.
But I wasn‘t too happy about the interface / communication between the WinForm and the WPF usercontrol.
Here, we will only discuss things that were missing in the first article and/or are new or have been changed.
Winform Concept and Code
Needed Project References to Host a WPF Usercontrol
PresentationCore
PresentationFramework
WindowsBase
WindowsFormsIntegration
The code to detect ActiveTextBox
and ActiveRichTextBox
is as given below:
Private Sub TextBox_Enter(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles PlainTextBox.MouseClick,
PlainTextBox.Enter, PlainTextBox.MouseEnter, PlainTextBox.TextChanged
_textData.ActiveTextBox = CType(sender, TextBox)
_textData.ActiveRichTextBox = Nothing
MyRibbonUserControl.StackpanelRT1.Visibility = Windows.Visibility.Hidden
MyRibbonUserControl.StackpanelRT2.Visibility = Windows.Visibility.Hidden
MyRibbonUserControl.ribbonComboBoxColor.Visibility = Windows.Visibility.Hidden
MyRibbonUserControl.FontsDlgRibbonButton.Visibility = Windows.Visibility.Hidden
MyRibbonUserControl.GraphicAlignRibbonButton.Visibility = Windows.Visibility.Hidden
End Sub
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
_textData.ActiveRichTextBox = CType(sender, RichTextBox)
_textData.ActiveTextBox = Nothing
MyRibbonUserControl.StackpanelRT1.Visibility = Windows.Visibility.Visible
MyRibbonUserControl.StackpanelRT2.Visibility = Windows.Visibility.Visible
MyRibbonUserControl.ribbonComboBoxColor.Visibility = Windows.Visibility.Visible
MyRibbonUserControl.FontsDlgRibbonButton.Visibility = Windows.Visibility.Visible
MyRibbonUserControl.GraphicAlignRibbonButton.Visibility = Windows.Visibility.Visible
End Sub
ICommand with MVVM Pattern
From the WPF Ribbon
’s point of view, the data structure / model is simple:
The Usercontrol
has menu items / ribbon buttons which work together with the ActiveRichTextBox
or the ActiveTextBox
. That is what we see in model class TextData
.
Some menu items / commands have been moved to the TextDataViewModel
, except all of the Richtext
formatting Ribbon Buttons.
There are two new classes, TextDataViewModel
and TextData
.
Imports System.Windows.Controls.Ribbon
Public Class TextData
Private _ActiveTextBox As TextBox
Private WithEvents _ActiveRichTextBox As RichTextBox
Private _AppPath As String
Dim _MyRibbonWPF As Ribbon
Dim _MyRibbonUserCtl As UserControlRibbonWPF
Public Sub New()
End Sub
Public Property MyRibbonUserCtl() As UserControlRibbonWPF
Get
Return _MyRibbonUserCtl
End Get
Set(value As UserControlRibbonWPF)
_MyRibbonUserCtl = value
End Set
End Property
Public Property MyRibbonWPF() As Ribbon
Get
Return _MyRibbonWPF
End Get
Set(value As Ribbon)
_MyRibbonWPF = value
End Set
End Property
Public Property AppPath() As String
Get
Return _AppPath
End Get
Set(value As String)
_AppPath = value
End Set
End Property
Public Property ActiveTextBox() As TextBox
Get
Return _ActiveTextBox
End Get
Set(value As TextBox)
_ActiveTextBox = value
End Set
End Property
Public Property ActiveRichTextBox() As RichTextBox
Get
Return _ActiveRichTextBox
End Get
Set(value As RichTextBox)
_ActiveRichTextBox = value
End Set
End Property
End Class
First part of class TextDataViewModel
, which contains properties, ICommand
s and methods.
Imports System.ComponentModel
Imports System.Windows.Controls.Ribbon
Imports System.Windows.Input
Imports System.Windows.Media
Public Class TextDataViewModel
Implements INotifyPropertyChanged
#Region " fields"
Private WithEvents _ActiveTextBox As TextBox
Private WithEvents _ActiveRichTextBox As RichTextBox
Dim _MyRibbonWPF As Ribbon
Dim _MyRibbonUserCtl As UserControlRibbonWPF
Private _AppPath As String = Application.StartupPath
#End Region
#Region " constructors"
Public Sub New()
End Sub
#End Region
#Region " properties"
Public Property MyRibbonUserControl() As UserControlRibbonWPF
Get
Return _textData.MyRibbonUserCtl
End Get
Set(value As UserControlRibbonWPF)
_textData.MyRibbonUserCtl = value
End Set
End Property
Public Property MyRibbonWPF() As Ribbon
Get
Return _textData.MyRibbonWPF
End Get
Set(value As Ribbon)
_textData.MyRibbonWPF = value
End Set
End Property
Public Property ActiveRichTextBox() As RichTextBox
Get
Return _textData.ActiveRichTextBox
End Get
Set(value As RichTextBox)
_textData.ActiveRichTextBox = value
NotifyPropertyChanged()
End Set
End Property
Public Property ActiveTextBox() As TextBox
Get
Return _textData.ActiveTextBox
End Get
Set(value As TextBox)
_textData.ActiveTextBox = value
NotifyPropertyChanged()
End Set
End Property
Public Property AppPath() As String
Get
Return _textData.AppPath
End Get
Set(value As String)
_textData.AppPath = value
NotifyPropertyChanged()
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(Optional propertyName As String = "")
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Dim _cmdSA As New Command(AddressOf SaveAsFileDlg)
Dim _cmdO As New Command(AddressOf OpenFileDlg)
Dim _cmdN As New Command(AddressOf New_Click)
Dim _cmdE As New Command(AddressOf Exit_Click)
Dim _cmdD As New Command(AddressOf Print_Click)
Dim _cmdI As New Command(AddressOf Info_Click)
Dim _cmdCut As New Command(AddressOf Cut_Click)
Dim _cmdC As New Command(AddressOf Copy_Click)
Dim _cmdP As New Command(AddressOf Paste_Click)
Dim _cmdBG As New Command(AddressOf BackgroundGreenRibbonButton_Click)
Dim _cmdBW As New Command(AddressOf BackgroundWhiteRibbonButton_Click)
Dim _cmdQAT As New Command(AddressOf RestoreQAT_Click)
Example - For one iCommand
, we need:
Public ReadOnly Property ExitApp As ICommand
Get
Return _cmdE
End Get
End Property
Dim _cmdE As New Command(AddressOf Exit_Click)
Private Sub Exit_Click()
AppForm.Close()
End Sub
Another new class called RichDataViewModel
contains properties, ICommand
s and methods for the Richtext
menu commands / Ribbon Buttons.
WPF Usercontrol Concept and Code
For UserCtlRibbonWPF
, we need the following to get ICommand
available:
<UserControl.DataContext>
<local:TextDataViewModel/>
</UserControl.DataContext>
When we add a new property
to the viewmodel
, we have to create a new build (F5 debug is not enough) before we see it in the designer / the interface.
In the designer, we can now connect menu commands and interface.
The code behind the usercontrol:
Imports System.Windows
Public Class UserControlRibbonWPF
Private _RibbonSizeIsMinimized As Boolean
Private sAppPath As String
#Region " constructors"
Public Sub New()
InitializeComponent()
sAppPath = _textData.AppPath
End Sub
#End Region
#Region " ApplicationCommands"
#End Region
#Region " RibbonControls"
#End Region
#Region " Properties"
Public Property RibbonSizeIsMinimized() As Boolean
Get
Return _RibbonSizeIsMinimized
End Get
Set(ByVal value As Boolean)
_RibbonSizeIsMinimized = value
End Set
End Property
#End Region
#Region " RibbonEvents"
Private Sub RibbonWPF_MouseDoubleClick_
(sender As Object, e As Windows.RoutedEventArgs) Handles RibbonWPF.MouseDoubleClick
If RibbonWPF.IsMinimized = True Then
_RibbonSizeIsMinimized = True
AppForm.UserControlWPFisMinimized = True
Else
_RibbonSizeIsMinimized = False
AppForm.UserControlWPFisMinimized = False
End If
End Sub
Private Sub UserControlRibbonWPF_ContextMenuClosing_
(sender As Object, e As EventArgs) Handles Me.ContextMenuClosing
AppForm.UserControlWPFisMinimized = RibbonWPF.IsMinimized
RibbonSizeIsMinimized = RibbonWPF.IsMinimized
End Sub
#End Region
#Region " Form Events"
Private Sub UserControlRibbonWPF_Loaded(sender As Object, e As RoutedEventArgs) _
Handles Me.Loaded
Try
_textData.MyRibbonUserCtl = Me
_textData.MyRibbonWPF = RibbonWPF
Catch ex As Exception
IO.File.AppendAllText(sAppPath & "\Log.txt", _
String.Format("{0}{1}", Environment.NewLine, _
Now.ToString & "; " & ex.ToString()))
End Try
End Sub
#End Region
#Region " RibbonSettings"
#End Region
End Class
Module Mod_Public
:
Public _textData As New TextData
Public cTextDataVM As New TextDataViewModel
The Modified XAML Code for the ApplicationMenu
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu CanAddToQuickAccessToolBarDirectly="True">
<RibbonApplicationMenuItem x:Name="AppCmdNew" Header="New"
Command="{Binding NewFile, Mode=OneWay}" IsCheckable="False"
ImageSource="Images/newdocument32.png"
QuickAccessToolBarImageSource="Images/newdocument32.png"
CanAddToQuickAccessToolBarDirectly="False" />
<RibbonApplicationMenuItem x:Name="AppCmdOpen" Header="Open"
Command="{Binding OpenFile, Mode=OneWay}" IsCheckable="False"
ImageSource="Images/open16.png"
QuickAccessToolBarImageSource="Images/open16.png"
CanAddToQuickAccessToolBarDirectly="False" />
<RibbonApplicationMenuItem x:Name="AppCmdSaveAs"
Header="Save As" Command="{Binding SaveFileAs, Mode=OneWay}"
IsCheckable="False" ImageSource="Images/save16.png"
QuickAccessToolBarImageSource="Images/save16.png"
CanAddToQuickAccessToolBarDirectly="False" />
<RibbonApplicationMenuItem x:Name="AppCmdPrint" Header="Print"
Command="{Binding Print, Mode=OneWay}" IsCheckable="False"
ImageSource="Images/print.png"
QuickAccessToolBarImageSource="Images/print.png"
CanAddToQuickAccessToolBarDirectly="False" />
<RibbonApplicationMenuItem x:Name="AppCmdClose"
Header="Close App" Command="{Binding ExitApp, Mode=OneWay}"
ImageSource="Images/close.png"
QuickAccessToolBarImageSource="Images/close.png"
CanAddToQuickAccessToolBarDirectly="False" />
</RibbonApplicationMenu>
</Ribbon.ApplicationMenu>
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 on the WinForm.
And I think with Command Binding and the above discussed Command interface, this should work really well.
Final Note
I am interested in feedback of any kind - problems, suggestions and other.
History
- 11th January, 2022 - Initial submission