Integrating WPF Ribbon With Prism and MVVM (with VB.NET example)

The WPF Ribbon control is an exciting UI element that provides instant Wow factor to applications and is fun to use, but the current CTP version for Framework v3.5 which is a parallel release for the WPF June 2009 toolkit, does not natively integrate with Prism or MVVM.

Firstly, the RibbonCommand is based on WPF’s standard RoutedUICommand class which does not have the same capacities as Prism’s DelegateCommand and secondly the RibbonCommand requires its CanExecute and Execute Event Handlers in Code Behind which violates the MVVM pattern. In addition, RibbonCommands must be declared as Static Resources which is an unusual restriction on WPF development.

Fortunately there are some smart programmers out there who have grappled with these issues. It turns out you need to:

  1. Sub-class RibbonCommand in order to delegate (that’s dele-GATE as a verb) the RibbonCommand to a Prism DelegateCommand via ICommand;
  2. Provide a Bridging Class which allows Static Resources to connect to a ViewModel Property instead of requiring EventHandlers in Code Behind.

The above is the discovery of sergioesp on the Prism Discussion Forum. I translated his code into VB.NET and added support for InputGestures (Menu Short Cuts).

SergioEsp, you are a legend!

So here’s the beef…

In the XAML for the RibbonWindow you need the following markup:

NB, inf is the xmlns for my Infrastructure project in which I have defined the classes CommandReference and DelegatedRibbonCommand

Window.Resources
inf:CommandReference x:Key="SaveAllCommandReference"
Command="{Binding SaveAllCommand}"/

inf:DelegatedRibbonCommand
x:Key="SaveAllCommand"
LabelTitle="Save All"
LabelDescription="Save all edits (Ctrl+S)"
ToolTipDescription="Save All edits"
ToolTipTitle="Save All (Ctrl+S)"
SmallImageSource="/Images/Ribbon/disk.png"
LargeImageSource="/Images/Ribbon/disk.png"
InputGestureKey="Ctrl+S"
DelegatedCommand="{StaticResource SaveAllCommandReference}" /

/Window.Resources

CommandReference is the Bridging Class that allows StaticResources to connect to a ViewModel Property by exposing a Dependency Property. SergioEsp got this class by using the WPF Futures MVVM Visual Studio Template, version 0.1. I didn’t want to use the MVVM Template, so I just downloaded the code and stole the CommandReference class. Here it is, translated to VB.NET

Imports System
Imports System.Windows
Imports System.Windows.Input

'''
''' This class facilitates associating a key binding in XAML markup to a command
''' defined in a View Model by exposing a Command dependency property.
''' The class derives from Freezable to work around a limitation in WPF when data-binding from XAML.
'''
'''
''' Taken from MSDN WPF Futures MVVM Toolkit v0.1 source. Converted from C# to VB
'''
Public Class CommandReference
Inherits Freezable
Implements ICommand

#Region "Constructor..."

Public Sub CommandReference()
'// Blank
End Sub

#End Region

#Region "Properties..."

Public Shared ReadOnly CommandProperty As DependencyProperty = _
DependencyProperty.Register("Command", GetType(ICommand), GetType(CommandReference), _
New PropertyMetadata(New PropertyChangedCallback(AddressOf OnCommandChanged)))

Public Property Command() As ICommand
Get
Return CType(GetValue(CommandProperty), ICommand)
End Get
Set(ByVal value As ICommand)
SetValue(CommandProperty, value)
End Set
End Property

#End Region

#Region "ICommand Members"

Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute

If Not Command Is Nothing Then
Return Command.CanExecute(parameter)
End If

Return True
End Function

Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
Command.Execute(parameter)
End Sub

#End Region

Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

Private Shared Sub OnCommandChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)

Dim commandReference As CommandReference = CType(d, CommandReference)
Dim oldCommand As ICommand = TryCast(e.OldValue, ICommand)
Dim newCommand As ICommand = TryCast(e.NewValue, ICommand)

'If oldCommand IsNot Nothing Then
' 'RemoveHandler oldCommand.CanExecuteChanged, commandReference.CanExecuteChanged
'End If
'If newCommand IsNot Nothing Then
' newCommand.CanExecuteChanged += commandReference.CanExecuteChanged.Canxecute

'End If

commandReference.Command = newCommand
CommandManager.InvalidateRequerySuggested()

End Sub

#Region "Freezable"

Protected Overrides Function CreateInstanceCore() As Freezable

Throw New NotImplementedException()
End Function

#End Region

End Class

The DelegatedRibbonCommandClass is a translatation of SergioEsp’s RibbonCommandEx class with added support for InputGestures. As you can see, the RibbonCommand is made to use the CanExecute and Execute handlers of the ICommand in the DelegatedCommand Property of DelegatedRibbonCommand.

Since we are using Prism, that DelegatedCommand Property is a Prism DelegateCommand. The ViewModel code is shown further below, but first, here’s the DelegatedRibbonCommandClass:

Imports System.Windows.Input
Imports Microsoft.Windows.Controls.Ribbon

'''
''' Class to delegate a RibbonCommand to an ICommand.
''' Allows RibbonCommands to be used within WPF Prism in a MVVM design pattern
'''
'''
''' Code taken from MSDN Prism Discussion Forum
''' http://wpf.codeplex.com/Thread/View.aspx?ThreadId=52089
'''
Public Class DelegatedRibbonCommand
Inherits RibbonCommand

'''
''' KeyGestureConverter for all DelegatedRibbonCommand instances
'''
'''
Private Shared _myKeyGestureConverter As KeyGestureConverter
Public Shared ReadOnly Property MyKeyGestureConverter() As KeyGestureConverter
Get
If _myKeyGestureConverter Is Nothing Then
_myKeyGestureConverter = New KeyGestureConverter
End If
Return _myKeyGestureConverter
End Get
End Property

'''
''' ICommand that this RibbonCommand will delegate to
'''
'''
Private _delegatedCommand As ICommand
Public Property DelegatedCommand() As ICommand
Get
Return _delegatedCommand
End Get
Set(ByVal value As ICommand)

Me.SetKeyGesture()

If Not _delegatedCommand Is value Then
_delegatedCommand = value
If Not _delegatedCommand Is Nothing Then
AddHandler Me.CanExecute, AddressOf DelegatedRibbonCommand_CanExecute
AddHandler Me.Executed, AddressOf DelegatedRibbonCommand_Executed
Else
RemoveHandler Me.CanExecute, AddressOf DelegatedRibbonCommand_CanExecute
RemoveHandler Me.Executed, AddressOf DelegatedRibbonCommand_Executed
End If
End If
End Set
End Property

'''
''' Property which contains String for InputGesture
'''
'''
Private _inputGestureKey As String
Public Property InputGestureKey() As String
Get
Return _inputGestureKey
End Get
Set(ByVal value As String)
_inputGestureKey = value
End Set
End Property

#Region "Event Handlers..."
'''
''' Event handler for Execute event.
''' Calls Execute method on DelegateCommand
'''
'''
'''
'''
Private Sub DelegatedRibbonCommand_Executed(ByVal sender As Object, _
ByVal e As ExecutedRoutedEventArgs)
DelegatedCommand.Execute(e.Parameter)
End Sub

'''
''' EventHandler for CanExecute event
''' Calls CanExecute function on DelegatedCommand
'''
'''
'''
'''
Private Sub DelegatedRibbonCommand_CanExecute(ByVal sender As Object, _
ByVal e As CanExecuteRoutedEventArgs)
e.CanExecute = DelegatedCommand.CanExecute(e.Parameter)
End Sub

#End Region

'''
''' Set the KeyGesture for this DelegatedRibbonCommand
'''
'''
Private Sub SetKeyGesture()
If Not String.IsNullOrEmpty(Me.InputGestureKey) Then
MyBase.InputGestures.Add( _
MyKeyGestureConverter.ConvertFromString(Me.InputGestureKey))
End If
End Sub

End Class

In the ViewModel, we define the Prism DelegateCommand that we want the Ribbon to use. This is standard Prism and MVVM code.

Private _saveAllCommand as ICommand
Public Property SaveAllCommand() As ICommand
Get
return _saveAllCommand
EndGet
Set(value As ICommand)
_saveAllCommand = value
EndSet

In ViewModel Constructor or some arbitrary InitialiseCommands method:
SaveAllCommand = new DelegateCommand(Of Object)(Address Of Me.SaveAll)

The StaticResource to the ViewModel Property by the markup further up.
The WPFRibbon then uses the Prism DelegateCommand in the ViewModel by referencing the StaticResource.
Note below that the SaveAllCommand is the x:Key for the DelegatedRibbonCommand in the Window Resource, NOT the ICommand ViewModel Property.

r:Ribbon.ApplicationMenu
r:RibbonApplicationMenu Command="{StaticResource MainMenuRootCommand}"
!-- Hack to make sure sub-menu items appear.
Add a RecentItems List

http://www.chakkaradeep.com/post/Proper-way-to-create-Menus-2b-Submenus-in-WPF-Ribbon.aspx--

r:RibbonApplicationMenu.RecentItemList
Rectangle Height="200"/Rectangle
/r:RibbonApplicationMenu.RecentItemList
r:RibbonApplicationMenuItem Command="{StaticResource SaveAllCommand}" /
r:RibbonApplicationMenuItem Command="{StaticResource CloseAllCommand}" /
/r:RibbonApplicationMenu
/r:Ribbon.ApplicationMenu

Happy to answer any questions if any of the above is not clear.

The word in the forums is that Microsoft are rewriting the WPF Ribbon to make it natively compatible with MVVM so hopefully before long we won’t need these additional classes to make it possible.

About these ads

Tags:

8 Responses to “Integrating WPF Ribbon With Prism and MVVM (with VB.NET example)”

  1. Yet another blog about... Says:

    MVVM – How to integrate the Office Ribbon respecting the pattern (especially the commands)…

    Synopsis The ribbon controls – introduced by office 2007 -are available for free on the Microsoft Office web site (French users should set the language to "english" to access the ressources). They can leverage the user’s experience of your…

  2. Jmix90 Says:

    Thx for this article.

    Here is an article describing it in C# : http://blog.lexique-du-net.com/index.php?post/2010/02/28/MVVM-How-to-integrate-the-Office-Ribbon

    • baraholka1 Says:

      JMix90,

      Thanks for dropping by. Your article has some additional comments which are very useful.

      All The Best,

      Barra

  3. Jmix90 Says:

    Thx !

  4. jRojas Says:

    Do you have the source code for this sample?

    • baraholka1 Says:

      Senor Rojas,

      You mean in the form of a downloadable Visual Studio Project ?
      Unfortunately, no.
      You will have to endure my scribbles above.

      Many commisserologies,

      Barra

  5. John Says:

    The latest version of RibbonControl makes this work-around obsolete:

    http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/b60520b5-7050-4cc4-af98-974c26fe424c

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: