O DispatcherPriority é uma enumeração essencial no WPF que determina a ordem de execução de operações na fila do Dispatcher, garantindo que tarefas críticas como respostas a input do utilizador tenham precedência sobre operações secundárias.
Quando Utilizar o DispatcherPriority
- Atualizar elementos da interface a partir de uma thread secundária (ex.: após porcessamento de dados em segundo plano).
- Controlar a sequência de operações, como executar código após a renderização da UI.
- Adiar tarefas não urgentes para momentos de inatividade do sistema, evitando bloqueios na interface.
- Dividir operações longas em fragmentos menores para manter a responsividade da aplicação.
Implementação Prática
Para despachar operações para a UI thread, utiliza-se o método BeginInvoke (assíncrono) ou Invoke (síncrono) do Dispatcher, especificando a prioridade desejada.
using System.Windows.Threading;
// Exemplo: atualizar um controlo a partir de uma tarefa assíncrona
private async Task CarregarDadosAsync()
{
var resultado = await ObterDadosDoServidorAsync();
// Despacha a atualização da UI com prioridade normal
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
() => {
listaItens.ItemsSource = resultado;
});
}
// Demonstração de diferentes prioridades
private void TestarPrioridades()
{
var dispatcher = Dispatcher.CurrentDispatcher;
dispatcher.BeginInvoke(DispatcherPriority.Background, () =>
Console.WriteLine("Tarefa em segundo plano"));
dispatcher.BeginInvoke(DispatcherPriority.Input, () =>
Console.WriteLine("Resposta a input"));
dispatcher.BeginInvoke(DispatcherPriority.Render, () =>
Console.WriteLine("Operação durante renderização"));
}
// Verificação de acesso à thread da UI
private void AtualizarEstado(string mensagem)
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal,
() => AtualizarEstado(mensagem));
return;
}
textoStatus.Text = mensagem;
}
Níveis de Prioridade Detalhados
| Prioridade | Valor | Descrição |
|---|---|---|
| Inativo | 0 | Operação não é processada |
| InatividadeDoSistema | 1 | Executa quando o sistema está ocioso |
| InatividadeDaAplicação | 2 | Executa quando a aplicação não está a processar input |
| ContextoInativo | 3 | Executa após operações de contexto mais importantes |
| SegundoPlano | 4 | Tarefas secundárias como pré-carregamento de dados |
| Input | 5 | Relativo a eventos de input do utilizador |
| Carregado | 6 | Após eventos de carregamento, antes do processamento de input |
| Renderização | 7 | Durante ciclos de renderização da UI |
| VinculaçãoDeDados | 8 | Atualizações de data binding |
| Normal | 9 | Prioridade padrão para a maioria das atualizações da UI |
| Envio | 10 | Máxima prioridade, execução imediata (usar com cautela) |
Cuidaods Essenciais
Evitar deadlocks: Prefira BeginInvoke em vez de Invoke sempre que possível, pois este último pode bloquear a thread chamadora se a UI thread estiver ocupada.
// Risco de deadlock se chamado de uma thread não-UI
private void AtualizarUISincronamente()
{
Dispatcher.Invoke(
DispatcherPriority.Normal,
() => { /* operação na UI */ });
}
// Alternativa segura assíncrona
private void AtualizarUIAssincronamente()
{
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
() => { /* operação na UI */ });
}
Otimização de desempenho: Evite despachar múltiplas operações de alta prioridade em ciclos rápidos; em vez disso, acumule mudanças e atualize a UI em lote.
// Ineficiente: múltiplas chamadas de alta prioridade
for (int i = 0; i < 500; i++)
{
Dispatcher.BeginInvoke(DispatcherPriority.Render, () =>
AtualizarElementoVisual());
}
// Melhoria: despacho único com processamento interno
Dispatcher.BeginInvoke(DispatcherPriority.Render, () =>
{
for (int i = 0; i < 500; i++)
AtualizarElementoVisual();
});
Cancelamento de operações: Utilize DispatcherOperation para anular operações pendentes, evitando execução desnecessária.
private DispatcherOperation _operacaoPendente;
private void IniciarProcessamento()
{
_operacaoPendente = Dispatcher.BeginInvoke(
DispatcherPriority.Background,
() => ProcessarDadosEmLote());
}
private void CancelarProcessamento()
{
if (_operacaoPendente?.Status == DispatcherOperationStatus.Pending)
_operacaoPendente.Abort();
}
Integração com async/await: Combine com InvokeAsync para manter código assíncrono legível.
private async Task ObterEAtualizarAsync()
{
var dados = await BuscarDadosAsync();
await Application.Current.Dispatcher.InvokeAsync(
() => {
gridDados.ItemsSource = dados;
},
DispatcherPriority.DataBind);
}