C # WPF analogue Window.ShowDialog () or deal with DispatcherFrame
Formulation of the problem
As part of the development of one application, it was required to implement the following scheme:
- Asynchronous method requests data
- The user enters data from the keyboard
- The method receives the input result as the result of the function and continues from the same place
Additional requirement: Do not create additional windows.
It would seem simple? It turned out to be really simple. But first things first.
Decision
The first attempt to do it head-on and without searching the Internet led to a blockage of the main stream, and, therefore, to no good. And I was about to use ShowDialog, as I came across an article . The author looked at how ShowDialog is made in WPF. Exactly what is needed!
In his article, he suggests creating his own implementation of the ShowDialog method
[DllImport("user32")]
internal static extern bool EnableWindow(IntPtr hwnd, bool bEnable);
public void ShowModal()
{
    IntPtr handle = (new WindowInteropHelper(Application.Current.MainWindow)).Handle;
    EnableWindow(handle, false);
    DispatcherFrame frame = new DispatcherFrame();
    this.Closed += delegate
    {
        EnableWindow(handle, true);
        frame.Continue = false;
    };
    Show();
    Dispatcher.PushFrame(frame);
}I do not need a window lock, since everything is shown in one window, and a return value is also required. We remove a little too much, add the right one ...
        public string GetInput()
        {
            var frame = new DispatcherFrame();
            ButtonClicked += () => { frame.Continue = false; };
            Dispatcher.PushFrame(frame);
            return Text;
        }Dispatcher.PushFrame(frame)prevents exit from the method GetInput()until frame.Continueit becomes false. When a new frame is started, the main loop pauses and a new one starts. This loop processes system messages, while the execution point in the main loop does not move further. When we exit the current frame ( frame.Continue = false), the main loop continues to work from the same place.
Now it remains only to check the performance.
In MainWindow, create a button and put a handler on it that will launch the task, in which we will turn to keyboard input.
Handler Code:
 public RelayCommand ButtonClick => new RelayCommand(() =>
        {
            Task.Factory.StartNew(() =>
            {
                // имитация работы
                Thread.Sleep(1000);
                // создадим контрол-обработчик ввода
                var control = new PopupControlModel();
                // вызов метода, который останавливает выполнение главного цикла
                Result = control.GetInput();
                // имитация дальнейшей работы
                Thread.Sleep(2000);
            });
        });
    }I used this solution to enter captcha and additional code for two-factor authentication. But there can be a huge number of applications.
! The sample code contains violations of the mvvm principle, anddon't hit hard missing design
Github source code: Proof of concept
useful links
Custom ShowDialog 
article A scant description of the DispatcherFrame class using machine translation
 
Waiting for completion via await is given in this article.