WPF - Modal Controls Via DispatcherFrame (Nested Message Pumps)

Lets say your new WPF gui has a button that loads confidential data. Lets then say that during the button's click handler you need to get some credentials from the user BEFORE continuing code execution within the handler. The usual approach would be to popup a modal window via Window.ShowDialog(), because the gui thread will block until the popup is closed (for getting credentials in this scenario) which is the desired behaviour.

But what if you wanted to do something a bit more complex, using controls in the existing logical tree ? You could have a hidden control with a high z-index that you can make visible (emulating a popup), but the gui won't block at that point, because if it does you wont be able to interact with that control (its a deadlock).

The solution is to use a little-known feature of the WPF Dispatcher, called DispatcherFrame - with which you can create nested message pumps, that allow the user to interact with controls while the main Dispatcher thread is blocked.

Here’s some sample code to illustrate the approach

 

<Window x:Class="Dispatcher.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="500" Width="500">
    <Grid>
        <TabControl>
            <TabItem Header="TabItem">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <ListBox ItemsSource="{Binding}"/>
                    <Button HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" 
                            Content="Get Data" Grid.Row="1" Margin="0,5" Click="GetData"/>
                </Grid>
            </TabItem>
            <TabItem Header="TabItem">
                <Grid/>
            </TabItem>
        </TabControl>
        <Grid Name="popupGrid" Visibility="Hidden">
            <Grid.Background>
                <SolidColorBrush Opacity="0.4" Color="#FFD8CFCF"/>
            </Grid.Background>
            <Border HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="100" 
                    BorderBrush="Black" BorderThickness="1" Background="White" Padding="5">
                <StackPanel>
                    <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Enter Number Of Items"/>
                    <TextBox HorizontalAlignment="Left" Text="10" TextWrapping="Wrap" Margin="0,3" 
                             Width="100" Name="countText"/>
                    <Button HorizontalAlignment="Left" Width="75" Content="Do Data Add" Click="DataCountEntered"/>
                </StackPanel>
            </Border>
        </Grid>
    </Grid>
</Window>

 

public partial class MainWindow : Window
{
    private DispatcherFrame frame;
    private readonly ObservableCollection<string> collection = new ObservableCollection<string>();
    public MainWindow()
    {
        InitializeComponent();
        DataContext = collection;
    }

    private void GetData(object sender, RoutedEventArgs e)
    {
        collection.Clear();
        frame = new DispatcherFrame();
        popupGrid.Visibility = Visibility.Visible;
        System.Windows.Threading.Dispatcher.PushFrame(frame); // blocks gui message pump & creates nested pump
        var count = int.Parse(countText.Text); // after DispatcherFrame is cancelled, it continues
        for (int i = 0; i < count; i++)
            collection.Add("Test Data " + i);
        popupGrid.Visibility = Visibility.Hidden;
    }

    private void DataCountEntered(object sender, RoutedEventArgs e)
    {
        frame.Continue = false; // un-blocks gui message pump
    }
}

I don't think there are many scenarios where this would be the best approach, but its a neat trick if you need to use it.

Dean

Comments (2) -

Salvador
Salvador
6/21/2011 4:56:35 PM #

Hello Dean

This a very goog example if you are looking for a modal message for an XBAP application, but what if the user press the TAB key?, did you resolve this problem?

BoJl4apa
BoJl4apa
11/15/2011 10:29:54 PM #

Wow! nice! just what I was looking for, Thanks!

Pingbacks and trackbacks (1)+

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

About the author

I am a senior .NET contract (freelance) software developer specialising in WPF and WinRT application development with C#, F#, C++. I mainly work in the Investment Bnking industry building performant and robust user interfaces for front office and middle office systems

RecentComments

Comment RSS

Month List