Declaração do problema
Como parte do desenvolvimento de um aplicativo, foi necessário implementar o seguinte esquema:
- O método assíncrono solicita dados
- O usuário digita dados do teclado
- O método recebe o resultado da entrada como resultado da execução da função e continua do mesmo local
Requisito adicional: Não crie janelas adicionais.
Parece simples? Acabou sendo muito simples. Mas as primeiras coisas primeiro.
Solução
A primeira tentativa de fazê-lo de frente e sem pesquisar na Internet levou a um bloqueio do fluxo principal e, portanto, não adiantou. E eu estava prestes a usar o ShowDialog, como me deparei com um artigo . O autor analisou como o ShowDialog é feito no WPF. O que você precisa!
Em seu artigo, ele sugere criar sua própria implementação do método ShowDialog
[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); }
Não preciso de um bloqueio de janela, pois tudo é mostrado em uma janela e também é necessário um valor de retorno. Removemos um pouco demais, adicionamos o correto ...
public string GetInput() { var frame = new DispatcherFrame(); ButtonClicked += () => { frame.Continue = false; }; Dispatcher.PushFrame(frame); return Text; }
Dispatcher.PushFrame(frame)
impede que o método GetInput()
até o frame.Continue
se tornar false
. Quando um novo quadro é iniciado, o loop principal faz uma pausa e um novo é iniciado. Esse loop processa as mensagens do sistema, enquanto o ponto de execução no loop principal não se move mais. Quando saímos do quadro atual ( frame.Continue = false
), o loop principal continua funcionando do mesmo local.
Agora resta apenas verificar o desempenho.
No MainWindow, crie um botão e coloque um manipulador que iniciará a tarefa, na qual passaremos para a entrada do teclado.
Código do manipulador:
public RelayCommand ButtonClick => new RelayCommand(() => { Task.Factory.StartNew(() => {
Usei esta solução para inserir captcha e código adicional para autenticação de dois fatores. Mas pode haver um grande número de aplicativos.
! O código de amostra contém violações do princípio mvvm e não bata forte falta de design
Código fonte do Github: Prova de conceito
Links úteis
Artigo "ShowDialog personalizado"
Descrição escassa da classe DispatcherFrame usando tradução automática
A espera pela conclusão via aguardar é fornecida neste artigo.