WinForm VB.NET Hosting WPF Ribbon - Part 2 - Using ICommand with MVVM Pattern
My idea was to host a WPF user control with Ribbon within a WinForm VB.NET project and try to use MVVM pattern.
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
' Property variables
Private _RibbonSizeIsMinimized As Boolean
' Internal variables
Private sAppPath As String
#Region " constructors"
Public Sub New()
InitializeComponent()
sAppPath = _textData.AppPath
End Sub
#End Region
#Region " ApplicationCommands"
' now using iCommand
#End Region
#Region " RibbonControls"
' now using viewmodel and iCommand
#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"
' moved to viewmodel
#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