Introduction
I construct all my WPF applications with multiple layers (assemblies). Typically, these consist of:
Application
: the GUI implementation ViewModel
: the application logic DataModel
: the (data) model definition
The reason for this architecture is to abstract and isolate different kinds of components. The layers have a top-down dependency to each other, and cyclic library references are not permitted.
This means that objects in the top layer can use the objects in the layers below, and not the other way round. If an object wants to notify a parent object, it has to implement an event with a delegate definition to which the parent object has to register itself so that it can be notified by the child object. This is not a new topic, and many articles can be found that explain this pattern.
In this article, I describe a technique that builds on delegate and event design paradigms to provide user interaction from the application logic layer. In this case, I have code in the view-model layer that needs to interact with the user.
Example
For example, a command implementation that requests the path to some file:
public class OpenFileCommand : ICommand
{
--- Code omitted ---
public void Execute(object parameter)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == true)
{
this.filePath = openFileDialog.FileName;
}
}
--- Code omitted ---
}
This example uses a GUI component, the OpenFileDialog
, to interact with the user to request the path to some file. This implementation breaches the architectural concept of layering! Instead, I want to raise an event, so that the implementation that uses the OpenFileDialog
is residing in the application layer.
Solution Benefits
- Sound Architecture: clean layering, no blending of logic and GUI components
- Clean References: no GUI references in sub layers
- Testability: no modal dialogs, this kills any automated test
- Environment Independency: the GUI layer can be substituted
Background
This solution is inspired by the Microsoft Prism library. This library is a part of the Microsoft Patterns for Building Composite Applications, a strong, but also large library that contains many classes and patterns for building a desktop application that can be composed of multiple units (see Domain Driven Design).
Using the Code
The sample solution contains the simplest Notepad implementation. The Interaction Request pattern is used to request the file path for loading or saving the text file when a "Load" or "Save As..." button is clicked.

Simplified Component and Class Diagram
The next diagram shows the basic structure of the sample program. Only the load command is shown to simplify the drawing. In the next steps, I will add the Interaction Request classes as well.
The application has 2 assemblies:
- Application: contains the GUI elements
- ViewModel: contains the business logic and data container

Application
- MainWindow: the GUI definition, written in XAML
- ShowLoadFileDialogAction: this class this will create the
OpenFileDialog
instance and show it to the user
ViewModel
MainViewModel
: contains the view's commands LoadCommand
: is executed when the user presses the "Load..." button TextContainer
: singleton instance that contains the text in the view's text box
This article explains a method to programmatically get from the LoadCommand
, residing in the ViewModel
layer, to the ShowLoadFileDialogAction
, which is part of the Application layer.
The implementation of this method can be reused, and therefore I will create a library for the implementation. I will call this library the "Small Application Framework".
Small Application Framework
The "Small Application Framework" is a library in which I will put the reusable components. I will use this library for this and future articles, and extend and modify its contents.
The "Small Application Framework" consists of two libraries:
SmallApplicationFramework
: contains GUI independent core application logic components SmallApplicaitonFramework.Wpf
: contains core application logic components that use WPF

SmallApplicationFramework
IInteractionRequest
: interface of the interaction request, containing the Raised
event INotification
: interface of the interaction request notification InteractionRequest
: generic implementation of the interaction request that takes a notification implementation as generic type argument InteractionRequestEventArgs
: the interaction request event arguments that are passed into the Raised
event CommandBase
: an ICommand
implementation, handling the CanExecuteChanged
event handler. This class is used as the base class for the commands
SmallApplicationFramework.Wpf
InteractionRequestTrigger
: the specialized event trigger for the interaction request TriggerActionBase
: base class for the interaction request actions
Complete Package Diagram
The complete package diagram looks as follows:

The Implementation
So far, the base classes are introduced. This section explains how they work together. The goal of this exercise is to create code that can be called from a command (in this example, the LoadCommand
) that will show a OpenFileDialog
in the GUI layer. This means that an event is raised, which contains a notification. The notification contains data specific for the task at hand. The first step is to define the notification.
The Notification
The notification is derived from the INotification
interface. It is instantiated at the time that the LoadCommand
is executed. At this time, the title and the filter that will be shown in the open file dialog are set. The file path and name are result properties.
public class ShowLoadFileDialogNotification : INotification
{
public string Title { get; set; }
public string FileName { get; set; }
public string FilePath { get; set; }
public string Filter { get; set; }
}
The InteractionRequest Definition
The InteractionRequestContainer
is a public singleton instance, containing the interaction requests. The ShowLoadFileDialogInteractionRequest
is a property of the container.
public class InteractionRequestContainer
{
private static InteractionRequestContainer instance;
private InteractionRequestContainer()
{
this.ShowLoadFileDialogInteractionRequest =
new InteractionRequest<ShowLoadFileDialogNotification>();
}
public static InteractionRequestContainer Instance
{
get
{
return instance ?? (instance = new InteractionRequestContainer());
}
}
public InteractionRequest<ShowLoadFileDialogNotification>
ShowLoadFileDialogInteractionRequest { get; private set; }
}
Using the InteractionRequest
The ShowLoadFileDialogInteractionRequest
is used in the Execute
method of the LoadCommand
. The interaction request is raised with the notification instance. The resulting value is tested and if it succeeds, the file is opened and its contents are written in the TextContainer
. This is also a singleton instance and contains the text string and the file path of the text file.
Please note that in this case the execution of the interaction request is a synchronous action. The control flow returns after the OpenFileDialog
is closed and its result value is stored in the notification object.
public class LoadCommand : CommandBase
{
public override void Execute(object parameter)
{
var notification = new ShowLoadFileDialogNotification
{
Title = "Select Text File",
Filter = "Text Files|*.txt|All Files|*.*"
};
InteractionRequestContainer.Instance.ShowLoadFileDialogInteractionRequest.Raise(notification);
if (string.IsNullOrEmpty(notification.FilePath) == false
&& File.Exists(notification.FilePath))
{
using (var streamReader = new StreamReader(notification.FilePath))
{
TextContainer.Instance.Text = streamReader.ReadToEnd();
}
TextContainer.Instance.FilePath = notification.FilePath;
}
}
}
The MainViewModel
The MainViewModel
does not contain any references to the interaction request. The instance is already declared in the InteractionRequestContainer
which is a public singleton instance. Therefore the MainViewModel
class contains only the LoadCommand
as a property.
public class MainViewModel
{
public MainViewModel()
{
this.LoadCommand = new LoadCommand();
}
public ICommand LoadCommand { get; private set; }
}
The ShowLoadFileDialogAction Definition
This class contains the logic for the interaction with the user. It is derived from the TriggerActionBase
class and it is instantiated with the ShowLoadFileDialogNotification
generic type parameter, so that it can receive this type of notifications. The base class handles the parameter checking and calls the abstract ExecuteAction()
method. This method has to be overridden and implemented as shown below. Here, the OpenFileDialog
is instantiated and its result is put into the notification after the dialog is closed.
public class ShowLoadFileDialogAction : TriggerActionBase<ShowLoadFileDialogNotification>
{
protected override void ExecuteAction()
{
var openFileDialog = new OpenFileDialog();
openFileDialog.Title = this.Notification.Title;
openFileDialog.Filter = this.Notification.Filter;
if (string.IsNullOrEmpty(this.Notification.FileName) == false)
{
openFileDialog.InitialDirectory = this.Notification.FileName;
}
this.Notification.FileName = string.Empty;
if (openFileDialog.ShowDialog() == true)
{
this.Notification.FileName = openFileDialog.SafeFileName;
this.Notification.FilePath = openFileDialog.FileName;
var mainWindow = this.AssociatedObject as MainWindow;
if (mainWindow != null)
{
mainWindow.Title = string.Format("{0} - {1}",
"Simplest Notepad",
Notification.FileName);
}
}
}
}
Receiving the InteractionRequest
The interaction request mechanism relies on the "Windows.System.Interactivity.dll" assembly. This assembly is packed with the Expression Blend SDK and it contains many useful features. The Interactivity assembly is referenced as a Nuget package.
The next XAML definition shows the application's GUI definition. The section Interaction.Triggers
holds the definition of the an InteractionRequestTrigger
that is bound to the ShowLoadFileDialogInteractionRequest
, defined in the InteractionRequestContainer
. The ShowLoadFileDialogAction
is defined as the event instance that is triggered when the InteractionReuqestContainer
's ShowLoadFileDialogInteractionRequest
event is raised.
<Window x:Class="InteractionRequestNotepad.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:ViewModel;assembly=ViewModel"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:interactionRequest="clr-namespace:SmallApplicationFramework.Wpf.InteractionRequest;
assembly=SmallApplicationFramework.Wpf"
xmlns:actions="clr-namespace:Application.Actions"
Title="Simplest Notepad" Height="350" Width="525">
<Window.DataContext>
<viewModel:MainViewModel/>
</Window.DataContext>
<i:Interaction.Triggers>
<interactionRequest:InteractionRequestTrigger
x:Name="ShowLoadFileDialog"
SourceObject="{Binding Path=ShowLoadFileDialogInteractionRequest,
Source={x:Static viewModel:InteractionRequestContainer.Instance}}">
<actions:ShowLoadFileDialogAction/>
</interactionRequest:InteractionRequestTrigger>
</i:Interaction.Triggers>
<DockPanel LastChildFill="True">
<ToolBar x:Name="MainToolBar" DockPanel.Dock="Top">
<Button x:Name="LoadButton"
Content="Load..." Command="{Binding LoadCommand}"/>
</ToolBar>
<Grid>
<TextBox x:Name="TextBox"
Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextWrapping="Wrap"
AcceptsReturn="True"
DataContext="{x:Static viewModel:TextContainer.Instance}"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"/>
</Grid>
</DockPanel>
</Window>
At this point, all actors are set up, and the system works. But how does it work?
Sequence Diagram
The next sequence diagram shows all the classes and the respective calls made to show the OpenFileDialog
.

Here is the link to the sequence diagram in the original size.
The Implementation Behind the Scenes
InteractionRequest
The InteractionRequest
class takes an INotification
object as its generic type parameter. It consists of an event called Raised
, to which the trigger will subscribe, and the Raise(T context)
method that will receive the notification instance as a parameter and packs the notification instance into the InteractionRequestEventArgs
where it will be passed to the Raised
event. In case of asynchronous usage, the Raise
method can be called with a callback Action
delegate, that can be called when the action has completed.
public class InteractionRequest<T> : IInteractionRequest
where T : INotification
{
public event EventHandler<InteractionRequestedEventArgs> Raised;
public void Raise(T context)
{
this.Raise(context, c => { });
}
public void Raise(T context, Action<T> callback)
{
EventHandler<InteractionRequestedEventArgs> handler = this.Raised;
if (handler != null)
{
handler(this, new InteractionRequestedEventArgs(context, () => callback(context)));
}
}
}
InteractionRequestEventArgs
The event argument with the notification and the optional callback action delegate.
public class InteractionRequestedEventArgs : EventArgs
{
public InteractionRequestedEventArgs(INotification context, Action callback)
{
this.Context = context;
this.Callback = callback;
}
public INotification Context { get; private set; }
public Action Callback { get; private set; }
}
InteractionRequestTrigger
The specialized interaction request trigger. The GetEventName()
method returns the name of the event the trigger is listening for. In this case, it is the "Raised
" event.
public class InteractionRequestTrigger : EventTrigger
{
protected override string GetEventName()
{
return "Raised";
}
}
TriggerActionBase
A base class for all interaction request actions. It implements the abstract Invoke(object)
method, defined in the TriggerAction
class which is called when the event is triggered. It retrieves the notification object and the callback action from the event arguments and calls the ExecuteAction()
method if a valid notification object is received.
public abstract class TriggerActionBase<T> : TriggerAction<FrameworkElement>
where T : class, INotification
{
private T notification;
private Action callback;
protected T Notification
{
get
{
return this.notification;
}
}
protected Action Callback
{
get
{
return this.callback;
}
}
protected override void Invoke(object parameter)
{
var interactionRequestedEventArgs = parameter as InteractionRequestedEventArgs;
if (interactionRequestedEventArgs != null)
{
this.notification = interactionRequestedEventArgs.Context as T;
this.callback = interactionRequestedEventArgs.Callback;
if (this.notification != null)
{
this.ExecuteAction();
}
}
}
protected abstract void ExecuteAction();
}
Conclusion
As stated in the Background section: I did not invent this pattern. The basis is a part of the Microsoft Prism Library. But I found the complete library too large to use for my "simple" applications. Still, I liked the concept and therefore I use it as the code, in the way shown here.
The main reason I am writing this article is to raise the awareness that calling GUI components from the ViewModel
layer must be dealt with carefully. I urge software engineers to refrain from implementing code like I presented in the introductory example. At the latest when automated testing becomes an issue, the presented dialog window will kill the test case.
I understand that the same functionality can be achieved using depencency injection (DI) and inversion of control. Meaning that an interface is defined in the ViewModel
layer, which is implemented in the View layer. The interface and the implementation are both registered to the DI mechanism, and this will instantiate the interface implementation, when it is used in the ViewModel
layer (i.e., called by the command).
I don't use the DI approach because of the automated application tests. Typically, I use the SpecFlow library to write the acceptance tests, and my scenarios act on the ViewModel layer classes, mimicking the Application layer. In this case, I can subscribe the interaction requests and keep the "called" state in the test context. With the DI approach, I have to implement and inject the test implementation of the interaction requests, which might influence the actual code execution, if it doesn't comply to the "real-life" implementation.