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

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