Estou tentando criar um controle de entrada de pesquisa simples com funcionalidade de rejeição baseada em um temporizador.
Search.razor
:
<input type="search" placeholder="Search" @oninput=@((e) => SearchChanged(e)) />
@code {
[Parameter]
public EventCallback<string> OnSearchChanged { get; set; }
private System.Timers.Timer? _debounceTimer;
string Text = string.Empty;
public void SearchChanged(ChangeEventArgs e)
{
Text = e.Value == null ? string.Empty : e.Value.ToString()!;
// Debounce search
_debounceTimer?.Stop();
_debounceTimer?.Dispose();
_debounceTimer = new System.Timers.Timer(200);
_debounceTimer.Elapsed += DebounceTimerElapsed;
_debounceTimer.Enabled = true;
_debounceTimer.Start();
}
private void DebounceTimerElapsed(object? sender, EventArgs e)
{
_debounceTimer?.Dispose();
OnSearchChanged.InvokeAsync(Text); // <<< Dispatcher exception
}
}
Acho que faz sentido incluir a funcionalidade Debounce com este componente para não precisar repeti-la para cada pai. Mas não consigo usar um EventCallback para informar ao componente pai que o temporizador expirou. Isso lança a The current thread is not associated with the Dispatcher
exceção infame.
Esta página na Blazor University explica que eventos não-UI, como Timer.Elapsed
spawn em outro thread. O Timer é de fato o culpado porque obviamente funciona se eu ligar OnSearchChanged.InvokeAsync()
diretamente de SearchChanged
. Mas InvokeAsync()
não parece sincronizar de volta com o thread da interface do usuário neste caso. No entanto, acho que deveria haver uma maneira simples de fazer isso funcionar.
Existe uma maneira (simples) de informar um componente pai que um temporizador decorreu em um componente filho?
A mensagem de erro que você encontra é comum ao tentar atualizar a IU de um thread que não é da IU no Blazor. Essa situação geralmente ocorre em manipuladores de eventos de operações assíncronas ou temporizadores, como no seu caso com o
System.Timers.Timer
. Como o evento Elapsed do timer é executado em um thread do pool de threads, e não no thread do Blazor Dispatcher, a tentativa de modificar o estado do componente leva diretamente a esse erro.Para corrigir isso, você precisa garantir que a lógica de atualização da IU seja executada no thread do Blazor Dispatcher. Você pode conseguir isso usando o método InvokeAsync para empacotar a chamada de volta para o contexto do componente Blazor. Aqui está como você pode modificar seu método de pesquisa para usar
InvokeAsync
:(Supondo que você reutilize este componente e queira
@bind
fazê-lo.DebouncedSearch.razor
Index.razor ou qualquer outro pai