There are many CodeProject articles about other MVVM frameworks, but almost nothing with WPF and the MVVM Toolkit. So I started to create this document.

Introduction
This article and the demo are about getting started using the MVVM Toolkit and some self-created interfaces / services for MessageBox
and some dialogs.
Background
There are many CodeProject articles about other MVVM frameworks, but almost nothing with WPF and the MVVM Toolkit. So I started to create this document.
The Model, View and ViewModel (the MVVM pattern) is a good way to organize or structure your code and helps you to simplify, develop and test (e.g. unit testing) your application.
The Model holds the data and has nothing to do with the application logic.
The ViewModel acts as the connection between Model and View.
The View is the User Interface.
I will not describe and explain every detail of the complete demo project. The focus is how to test some of the features.
Using the Code
MVVM Structure / Features
The MVVM Toolkit is from Microsoft and also some of the other used features are not my own: Sources as listed in the Credits / Reference section.
Quick Overview of the Content
- MVVM Toolkit and .NET 4.7.2
RelayCommand
OnPropertyChanged
ObservableRecipient
(Messenger
and ViewModelBase
) DependencyInjection
(to run MsgBox
and Dialog
s as a service) ObservableCollection
(for Credits Listbox
)
- Ribbon Menu
- Services / dialogs
- Using
ICommand
to bind buttons to the ViewModel
Installation of the MVVM Toolkit
With the installation of the NuGet package of the MVVM Toolkit, it installs 6 or 7 other packages.
For DependencyInjection
, we need to install another NuGet package:

And I made interfaces / services for MsgBox
and some dialogs.
DependencyInjection
manages to start the following dialogs independent from the viewmodel
s:
FontDlgVM
MsgBoxService
DialogVM
OpenFileDlgVM
RibbonStatusService
SaveAsFileDlgVM
SearchDlgVM
This allows the usage of custom Messagebox
es / dialogs as well as Unit Testing.
MainWindow Concept and Code
MainWindow
shows the Ribbon
.
Below that there is a Tabcontrol
, with tabs for MVVM Toolkit Testing and RichText
.
The code behind Class MainWindow
is that:
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new TestingViewModel();
}
}
}
Application
Registering the services / viewmodels is in the App code behind.
public partial class App : Application
{
private bool blnReady;
public App()
{
InitializeComponent();
Exit += (_, __) => OnClosing();
Startup += Application_Startup;
try
{
Mod_Public.sAppPath = Directory.GetCurrentDirectory();
Ioc.Default.ConfigureServices(
new ServiceCollection()
.AddSingleton<IMsgBoxService, MsgBoxService>()
.AddSingleton((IDialog)new DialogVM())
.AddSingleton((IOpenFileDlgVM)new OpenFileDlgVM())
.AddSingleton((ISaveAsFileDlgVM)new SaveAsFileDlgVM())
.AddSingleton((IRichTextDlgVM)new RichTextDlgVM())
.BuildServiceProvider());
}
catch (Exception ex)
{
File.AppendAllText(Mod_Public.sAppPath + @"\Log.txt",
string.Format("{0}{1}", Environment.NewLine,
DateAndTime.Now.ToString() + "; " + ex.ToString()));
var msgBoxService = Ioc.Default.GetService<IMsgBoxService>();
msgBoxService.Show("Unexpected error:" + Constants.vbNewLine +
Constants.vbNewLine + ex.ToString(), img: MessageBoxImage.Error);
}
}
private void OnClosing()
{
}
private void Application_Startup(object sender, EventArgs e)
{
blnReady = true;
}
}
Mod_Public
Mod_Public
includes:
public static void ErrHandler(string sErr)
and:
public static string ReadTextLines(string FileName)
MVVM Pattern - Details
From the WPF Ribbon
’s point of view, the data structure / model is simple:
The Ribbon
has menu items / ribbon buttons which work together with the ActiveRichTextBox
or the ActiveTextBox
.
That is what we see in model class TextData
.
Model class called TextData
:
{
public class TextData : ObservableRecipient, INotifyPropertyChanged
{
private string _text;
private string _richText;
private RibbonTextBox _NotifyTest;
private string _readText;
private TextBox _ActiveTextBox;
private RichTextBox __ActiveRichTextBox;
private RichTextBox _ActiveRichTextBox
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
return __ActiveRichTextBox;
}
[MethodImpl(MethodImplOptions.Synchronized)]
set
{
__ActiveRichTextBox = value;
}
}
private Ribbon _MyRibbonWPF;
private MainWindow _MyMainWindow;
public TextData()
{
}
public MainWindow MyMainWindow
{
get
{
return _MyMainWindow;
}
set
{
_MyMainWindow = (MainWindow)value;
}
}
public Ribbon MyRibbonWPF
{
get
{
return _MyRibbonWPF;
}
set
{
_MyRibbonWPF = value;
}
}
public RibbonTextBox NotifyTestbox
{
get
{
return _NotifyTest;
}
set
{
_NotifyTest = value;
}
}
public string RichText
{
get
{
return _richText;
}
set
{
_richText = value;
OnPropertyChanged("RichText");
}
}
public string GetText
{
get
{
return _text;
}
set
{
_text = value;
OnPropertyChanged("GetText");
}
}
public string ReadText
{
get
{
return _readText;
}
set
{
_readText = value;
GetText = _readText;
OnPropertyChanged("ReadText");
}
}
public TextBox ActiveTextBox
{
get
{
return _ActiveTextBox;
}
set
{
_ActiveTextBox = value;
OnPropertyChanged("ActiveTextBox");
}
}
public RichTextBox ActiveRichTextBox
{
get
{
return _ActiveRichTextBox;
}
set
{
_ActiveRichTextBox = value;
}
}
}
}
ViewModel class
called TestingViewModel
The class called TestingViewModel
contains properties, ICommands
and methods for the testing of some MVVM features. It contains also code for the ObservableCollection
(Of Credits), which is used for the Listview
with the References / Credits for this article.
Putting Things Together - WPF Concept and Code
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
.
DependencyInjection or ServiceInjection
As already mentioned, there is some code for this in code behind of the App.
Save File Dialog Example with ISaveAsFileDlgVM
It uses interface ISaveAsFileDlgVM
and service
/ viewmodel SaveAsFileDlgVM
.
public class TestingViewModel
: ObservableRecipient
, INotifyPropertyChanged
...
public ICommand SaveAsFileDlgCommand { get; set; }
...
RelayCommand cmdSAFD = new RelayCommand(SaveAsFileDialog);
SaveAsFileDlgCommand = cmdSAFD;
...
private void SaveAsFileDialog()
{
var dialog = Ioc.Default.GetService<ISaveAsFileDlgVM>();
if (ActiveRichTextBox is object)
{
dialog.SaveAsFileDlg(_textData.RichText, ActiveRichTextBox);
}
if (ActiveTextBox is object)
{
dialog.SaveAsFileDlg(_textData.GetText, ActiveTextBox);
}
}
...
And, very important, Command="{Binding SaveAsFileDlgCommand}"/>
in the XAML file.
<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"
="Save As" Command="{Binding SaveAsFileDlgCommand}"/>
From the Ribbon
, you can start and test other dialogs or the messagebox
with:
- Open Dialog
- Search (Source as listed in Credits/References)
OpenFileDialog
- Tab Help > Info
FontDialog
Messenger Test
It is important to add Inherits ObservableRecipient
, this and other details are described in ObservableObject - Windows Community Toolkit | Microsoft Docs.
"View specific messages should be registered In the Loaded Event Of a view And deregistered In the Unloaded Event To prevent memory leaks And problems multiple callback registrations."
We can send a Msg
from Class
TestingViewModel
:
Imports Microsoft.Toolkit.Mvvm.Messaging
public class TestingViewModel : ObservableRecipient, INotifyPropertyChanged
private string msg;
…
_cmdMsg = new Command(SendMsgRibbonButton_Click);
…
public ICommand SendMsg
{
get
{
return _cmdMsg;
}
}
…
private void SendMsgRibbonButton_Click()
{
try
{
string msg = "Test Msg...";
SetStatus("TestingViewModel", msg);
}
catch (Exception ex)
{
SetStatus("TestingViewModel", ex.ToString());
Mod_Public.ErrHandler(ex.ToString());
}
}
...
public void SetStatus(string r, string m)
{
try
{
Messenger.Send(new DialogMessage(m));
}
catch (Exception ex)
{
SetStatus("TestingViewModel", ex.ToString());
Mod_Public.ErrHandler(ex.ToString());
}
}
...
public class StatusMessage
{
public StatusMessage(string status)
{
NewStatus = status;
}
public string NewStatus { get; set; }
}
Send Msg
is only possible if the message is registered:
using Microsoft.Toolkit.Mvvm.Messaging;
...
Messenger.Register<DialogMessage>(this, (r, m) => DialogMessage = m.NewStatus);
Messenger.Register<StatusMessage>(this, (r, m) => StatusBarMessage = m.NewStatus);
...
Messenger.Unregister<StatusMessage>(this);
Messenger.Unregister<DialogMessage>(this);
On closing the viewmodel
, we have to unregister the message.
The message appears on StatusBar
and the Ribbon
.
PropertyChanged Test
<RibbonTextBox x:Name="ribbonTextBox"
Text="{Binding OnPropertyChangedTest, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Right" Margin="0,0,-90,-30"
TextWrapping="Wrap" VerticalAlignment="Bottom"
Width="120" UndoLimit="10" FontSize="12"/>
<RibbonTextBox x:Name="NotifyTextBox" Text="{Binding OnPropertyChangedTest,
UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Right" Margin="0,0,-90,-53"
TextWrapping="Wrap" VerticalAlignment="Bottom" Width="120"
UndoLimit="10" FontSize="12"/>
Both textbox
es normally show only if the activeTextbox
is related to "RichText
" or "PlainText
". But if you edit the upper one manually, you can see that the lower one's content is changed immediately.
This is caused by UpdateSourceTrigger=PropertyChanged
in the XAML file.
EventTrigger
Requirements: Microsoft.Xaml.Behaviors.Wpf
(NuGet package)
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
...
<b:Interaction.Triggers>
<b:EventTrigger EventName= "MouseWheel">
<b:InvokeCommandAction Command="{Binding ParamCommandTBx}"
CommandParameter="{Binding ElementName=myTextBox, Mode=OneWay}"/>
</b:EventTrigger>
<b:EventTrigger EventName= "MouseDoubleClick">
<b:InvokeCommandAction Command="{Binding ParamCommandTBx}"
CommandParameter="{Binding ElementName=myTextBox, Mode=OneWay}"/>
</b:EventTrigger>
<b:EventTrigger EventName= "TextChanged">
<b:InvokeCommandAction Command="{Binding ParamCommandTBx}"
CommandParameter="{Binding ElementName=myTextBox, Mode=OneWay}"/>
</b:EventTrigger>
<b:EventTrigger EventName= "MouseEnter">
<b:InvokeCommandAction Command="{Binding ParamCommandTBx}"
CommandParameter="{Binding ElementName=myTextBox, Mode=OneWay}"/>
</b:EventTrigger>
</b:Interaction.Triggers>
...
This is used when the Ribbon
is minimized via ContextMenu
and for other stuff.
ObservableCollection
It is part of viewmodel TestingViewModel
and used for the listbox
with Credits / References.
public class TestingViewModel : ObservableRecipient, INotifyPropertyChanged
#Region " fields"
...
private ObservableCollection<Credits> _credit = new ObservableCollection<Credits>();
...
#End Region
...
_credit = new ObservableCollection<Credits>()
{
new Credits()
{
Item = "MVVM Toolkit",
Note = "Microsoft",
Link = "https://docs.microsoft.com/en-us/windows/
communitytoolkit/mvvm/introduction"
},
new Credits()
{
Item = "MVVMLight",
Note = "GalaSoft",
Link = "https://www.codeproject.com/Articles/768427/
The-big-MVVM-Template"
},
new Credits()
{
Item = "ICommand with MVVM pattern",
Note = "CPOL",
Link = "https://www.codeproject.com/Articles/863671/
Using-ICommand-with-MVVM-pattern"
},
new Credits()
{
Item = "C# WPF WYSIWYG HTML Editor - CodeProject",
Note = "CPOL",
Link = "https://www.codeproject.com/Tips/870549/
Csharp-WPF-WYSIWYG-HTML-Editor"
},
new Credits()
{
Item = "SearchDialog",
Note = "Forum Msg",
Link = "https://social.msdn.microsoft.com/forums/vstudio/en-US/
fc46affc-9dc9-4a8f-b845-89a024b263bc/
how-to-find-and-replace-words-in-wpf-richtextbox"
}
};
...
public class Credits
{
public string Item { get; set; }
public string Note { get; set; }
public string Link { get; set; }
}
Test with the ObservableCollection
Click on Clear Listbox the delete the credits.
Read XML to Listbox
restores the references.
The advantage of the ObservableCollection
is that we need no UpdateTrigger
for the Listbox
.
RichText
From tab 'RichText
', you can select some text within the RichTextBox
and use the RibbonButton
s to format it. Many of these are EditingCommand
s and appear only in the UserCtlRibbonWPF.xaml file.
Conclusion
This is only a demo – it is not production ready.
But I think the MVVM Toolkit will allow you a variety of extensions.
Final note: I am very interested in feedback of any kind - problems, suggestions and other.
Credits / Reference
History
- 23rd Feb, 2023 - Version 1.1 - Because
Microsoft.ToolKit.Mvvm
has been deprecated, we now must use this alternate package: CommunityToolkit.Mvvm
- 8th June, 2022 - Added the Model, View and ViewModel (the MVVM pattern) explanation
- 19th May, 2022 - Initial submission