A common question asked by WPF developers is “How do I display modal content in a windows presentation foundation application?”.
Usually this question comes with requirements such as:
- The ability to display arbitrary content.
- Making the modal content modal to a particular view rather than the entire application.
- Stopping the user interacting with the content that is behind the modal content.
- making this work in an MVVM compliant way.
There are a number of questions on stackoverflow which are a variation on this theme. The answer usually involves interfaces and a lot of architecture to decouple the view from the viewModel. All of this seemed overly complicated to me and something just didn’t seem right with controlling this explicitly in the viewModel (after all, modality is a feature of the view not the viewModel; can anyone explain to me what modal data is exactly?)
The solution
One solution I found was to create a custom UserControl that hosts two pieces of content and uses a property to show and hide the modal content. This stackoverflow post provides an example of this solution. I managed to hack together a similar solution that derived from the ContentControl class based on the code provided but as I developed it I noticed a couple of issues.
First of all, the content behind the modal popup was disabled. This isn’t a major problem but I have noticed that whenever this question is asked people do not want this to happen. I can also see this being a problem if an application is using custom themes as it may not have disabled states defined for all controls (a control may not need to be disabled under any other circumstances except when used by this control).
Second of all, the solution derived from UserControl which is not right. This problem isn’t about visual representation; it is about application behaviour. I also noticed that the control was very dependant on the items that were contained in the user controls xaml; If the controls or the grid contained in the user control were changed or moved about then the control would break. I had the same problem even after I updated my version of the control to derive from ContentControl because the user could, in theory, re-template the control causing it to break. The control was very brittle.
The fact that this solution derived from ContentControl just didn’t seem right to me so I decided to develop this into a custom FrameworkElement and in doing so hopefully fix some of these flaws.
How it works
The custom type will derive from the FrameworkElement class and will work by:
- Providing two properties that accept arbitrary content. Each piece of content will be hosted in a
ContentPresenter. - The modal content will always be positioned in front of the primary content but will initially be hidden.
- The visibility of the modal content will be controlled by a property so that it can support data binding.
- When the modal content is displayed, the primary content will be dimmed and made inaccessible.
The content properties
The control will store the content using a pair of dependency properties called PrimaryContent and ModalContent, respectively. These properties will also be accessible via standard clr property wrappers.
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content",typeof(object),
typeof(ModalContentPresenter),
new UIPropertyMetadata(null, OnContentChanged));
public static readonly DependencyProperty ModalContentProperty =
DependencyProperty.Register("ModalContent", typeof(object),
typeof(ModalContentPresenter),
new UIPropertyMetadata(null, OnModalContentChanged));
public object Content
{
get { return (object)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public object ModalContent
{
get { return (object)GetValue(ModalContentProperty); }
set { SetValue(ModalContentProperty, value); }
}
When these properties are set, the object will be passed to the respective ContentPresenter.Content property and the display of the ‘content’ will be managed automatically (this is what allows the control to host arbitrary content).
Positioning the content
What I wanted was to have the modal content placed in front of the primary content but also take up the same area (remember the modal content is initially hidden). A Grid with no columns or rows actually behaves like this so this is the layout panel of choice; each ContentPresenter can be hosted in the grid and the ‘content’ will be placed in the correct position.
Note: technically, the modal content is contained in a Border which is used as the overlay and it is this that is placed above the primary content. This is an implementation detail that is required so the overlay, when shown, only obscures the primary content (the reason which will be explained later).
Hiding and showing the content
Displaying and hiding the modal content is controlled in two ways; by a boolean property and by methods. Again, the property is simply a DependencyProperty, defined like this:
public static readonly DependencyProperty IsModalProperty =
DependencyProperty.Register("IsModal", typeof(bool),
typeof(ModalContentPresenter),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnIsModalChanged));
And the method simply wraps a call to the clr property wrapper:
public void ShowModalContent()
{
if(!IsModal)
IsModal= true;
}
public void HideModalContent()
{
if (IsModal)
IsModal = false;
}
All of the logic that controls the display of the modal content happens inside the property changed callback for the IsModal property.
private static void OnIsModalChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
ModalContentPresenter control = (ModalContentPresenter)d;
if ((bool)e.NewValue == true)
{
// Show modal content.
}
else
{
// Hide modal content.
}
}
Overlaying the primary content
Remember earlier when I said that the ContentPresenter that is used to display the modal content is actually hosted inside a Border? Well the reason for this is that when the modal content is displayed I want the primary content to be ‘dimmed’. This is achieved by simply setting the borders background property to DarkGray with 80% opacity.
This actually had the side effect of stopping the user from selecting the primary content with the mouse which means it no longer has to be disabled when the modal content is displayed.
The final step was to stop the user from navigating to the primary content using the keyboard whilst the modal content was being displayed. This is how I achieved this:
- When the modal content is displayed, cache the keyboard navigation mode of the primary content.
- Set the primary contents keyboard navigation mode to ‘None’.
- When the modal content is hidden set the primary contents keyboard navigation mode back to the cached value.
// Called when showing the modal content
control.cachedKeyboardNavigationMode =
KeyboardNavigation.GetTabNavigation(control.primaryContent);
KeyboardNavigation.SetTabNavigation(
control.primaryContent, KeyboardNavigationMode.None);
// Called when hiding the modal content
KeyboardNavigation.SetTabNavigation(
control.primaryContent, control.cachedKeyboardNavigationMode);
control.primaryContent.MoveFocus(traversalDirection);
Finishing touches
Because I have derived this control from FrameworkElement, there are a number of additional steps that need to be taken in order to get this to work properly. Explanation of this is outside the scope of this post so I plan on explaining these details in a follow up post. For now, see the source code for the additional details.
Below is an example of how you would use this new element in a WPF application.
<Window x:Class="ModalContentTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="ModalContentTest.Controls"
Title="Modal content test"
Height="350"
Width="525">
<c:ModalContentPresenter Name="modalPresenter">
<TabControl Margin="5">
<TabItem Header="Tab one">
<Button Margin="55"
Padding="10"
Click="ShowModalContent">
_Show modal content
</Button>
</TabItem>
</TabControl>
<c:ModalContentPresenter.ModalContent>
<StackPanel Orientation="Vertical"
VerticalAlignment="Center">
<Button Margin="75"
Padding="50"
Click="HideModalContent">
Hide modal content
</Button>
</StackPanel>
</c:ModalContentPresenter.ModalContent>
</c:ModalContentPresenter>
</Window>
The code-behind for the main window looks like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ShowModalContent(object sender, RoutedEventArgs e)
{
modalPresenter.ShowModalContent();
}
private void HideModalContent(object sender, RoutedEventArgs e)
{
modalPresenter.HideModalContent();
}
}
Note: This example uses code-behind but this control can be used in an MVVM friendly way by binding to the IsModal property. The example was shown this way for simplicity.
Using the custom element
When the application is first run, the main window will only show the primary content:
After the button is pressed, the modal content will be displayed, like so:
The user is unable to access the content behind the modal popup until it has been closed; the content cannot be accessed with the mouse, presses of the tab key, or the use of an access key (note the first button can be invoked using alt-s).
The modal content is only modal to the content it is covering and not the entire application. This is easier to show with an example:
This shows a window that contains a grid that contains two columns. The first column contains a StackPanel which contains six buttons. The second column contains the ModalContentPresenter.
When the modal content is displayed, the user can still select the buttons contained in the stackpanel but will be unable to access any of the content that is behind the modal content whilst it is being shown.
Summary
This custom element provides the following features:
- Modal display of arbitrary content.
- Does not disable the primary content whilst the modal content is being displayed.
- Disables mouse and keyboard access to the primary content whilst the modal content is displayed.
- Is only modal to the content it is covering, not the entire application.
- Can be used in an MVVM friendly way by binding to the
IsModalproperty.
Sample code can be downloaded here.



Nice article – shame I cannot download the code
Hi Andrew.
Thank you for the feedback. Could you provide me with some more information so that I can fix the problem?
E.G. What happens when you click on the link? Does it take you to the download page?
Regards,
Benjamin
Benjamin,
I clicked on the Download button on the MediaFire website and nothing happened. Tried a few times and left it for several minutes but nothing.
However, have just tried again and it worked.
Thanks for the article
Andy
That’s great. Please let me know if there are any issues using the code you have downloaded and of course if you spot any bugs please let me know and I’ll have the sample updated.
This works very well thanks, I’m using it with templated vm’s getting swapped in and out for the modal property and haven’t seen any hiccups thus far (and actually it is on templated vm’s which can be swapping in and out of the visual tree freely as well).
Thank you for the feedback. I’m really glad someone is using this and finding it useful. If you have any suggestions for improvements please let me know.
thank you for this very awesome code.
Works very well.
I have a question concerning the MVVM way. I’m binding to the IsModal property and this shows up perfectly. I changed the code a bit, so that all my views inherit from the ModalContentPresenter, so I can get the overlay anywhere where I want, over the complete view.
The problem that i’m facing now, is instead of a border with a child of contentpresenter I want to use a view and viewmodel, which i can give a custom layout, I can add the view to the _layoutroot but that doesn’t give me access to my viewmodel.
Any thoughts on how to do this?
My advice would be to not inherit from the ModalContentPresenter as it is designed to be composed like the other WPF controls.
The control is designed to be able to display any content which is why it uses a ContentPresenter to display both pieces of content. In the example above I host a StackPanel that contains buttons but this could be anything you want such as a custom user control.
If you want the embedded user control to use a specific view and view model I would make my primary view model contain nested view models which are exposed as properties. I would then bind the primary and modal content properties to the sub-view models like this:
<c:ModalContentPresenter Name="modalPresenter" Content={Binding DataContext.PrimaryViewModel} ModalContent={Binding DataContext.SecondaryViewModel} />You can then use DataTemplates to determine which ‘view’ should be displayed:
Thank you very much.
This pointed me into the right direction, I’ve bound the ModalContent to my viewmodel with success.