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