PC SOFT

PROFESSIONAL NEWSGROUPS
WINDEVWEBDEV and WINDEV Mobile

Home → WINDEV 25 → WX (Windev, Webdev e WindevMobile) com Callbacks: Guia Completo com Exemplos Práticos
WX (Windev, Webdev e WindevMobile) com Callbacks: Guia Completo com Exemplos Práticos
Started by Boller, Sep., 19 2025 8:00 PM - 2 replies
Registered member
4,618 messages
Posted on September, 19 2025 - 8:00 PM
# WX (Windev, Webdev e WindevMobile) com Callbacks: Guia Completo com Exemplos Práticos

## Introdução

Os callbacks são uma ferramenta fundamental no WinDev para gerenciar operações assíncronas, eventos personalizados e comunicação entre diferentes partes da aplicação. Este guia apresenta métodos simples e eficazes para implementar callbacks em seus projetos WinDev, WebDev e WinDev Mobile.

## O que são Callbacks?

Um callback é uma função que é passada como parâmetro para outra função e é executada em um momento específico, geralmente após a conclusão de uma operação assíncrona ou quando um evento particular ocorre.

### Vantagens dos Callbacks:

- **Flexibilidade**: Permitem personalizar o comportamento sem modificar o código principal
- **Reutilização**: O mesmo código pode ter diferentes comportamentos dependendo do callback
- **Assincronismo**: Ideais para operações que não bloqueiam a interface
- **Modularidade**: Facilitam a separação de responsabilidades

## Implementação Básica de Callbacks

### Exemplo 1: Callback Simples com Procedimento

```wl
// Procedimento que aceita um callback
PROCEDURE ProcessarDados(sArquivo is string, pCallback is Procedure)
bSucesso is boolean

// Simula processamento
bSucesso = HImportText("MeuArquivo", sArquivo)

// Chama o callback com o resultado
pCallback(bSucesso, sArquivo)
END

// Callback de sucesso
PROCEDURE OnProcessamentoCompleto(bSucesso is boolean, sArquivo is string)
IF bSucesso THEN
Info("Arquivo " + sArquivo + " processado com sucesso!")
ELSE
Error("Erro ao processar arquivo " + sArquivo)
END
END

// Uso
ProcessarDados("dados.txt", OnProcessamentoCompleto)
```

### Exemplo 2: Callbacks com Parâmetros Múltiplos

```wl
// Procedimento para validação com callback
PROCEDURE ValidarFormulario(pCallbackSucesso is Procedure, pCallbackErro is Procedure)
arrErros is array of strings

// Validações
IF EDT_Nome = "" THEN
Add(arrErros, "Nome é obrigatório")
END

IF EDT_Email = "" THEN
Add(arrErros, "Email é obrigatório")
ELSE IF Position(EDT_Email, "@") = 0 THEN
Add(arrErros, "Email inválido")
END

// Executa callback apropriado
IF arrErros..Count = 0 THEN
pCallbackSucesso()
ELSE
pCallbackErro(arrErros)
END
END

// Callbacks
PROCEDURE OnValidacaoSucesso()
Info("Formulário válido!")
// Continua com o processamento
END

PROCEDURE OnValidacaoErro(arrErros is array of strings)
sMsg is string = "Erros encontrados:" + CR
FOR EACH sErro OF arrErros
sMsg += "- " + sErro + CR
END
Error(sMsg)
END
```

## Callbacks Avançados

### Exemplo 3: Sistema de Progress com Callback

```wl
// Estrutura para informações de progresso
STProgress is Structure
nPorcentagem is int
sEtapa is string
nTempoRestante is int
END

// Procedimento com callback de progresso
PROCEDURE ProcessarLote(arrArquivos is array of strings,
pCallbackProgress is Procedure,
pCallbackFinal is Procedure)

nTotal is int = arrArquivos..Count
nProcessados is int = 0
arrResultados is array of strings

FOR EACH sArquivo OF arrArquivos
// Simula processamento
ThreadPause(100)

nProcessados++

// Atualiza progresso
stProgress is STProgress
stProgress.nPorcentagem = (nProcessados * 100) / nTotal
stProgress.sEtapa = "Processando: " + ExtractFileName(sArquivo)
stProgress.nTempoRestante = (nTotal - nProcessados) * 100

pCallbackProgress(stProgress)

// Simula resultado
Add(arrResultados, sArquivo + " - OK")
END

pCallbackFinal(arrResultados)
END

// Callbacks
PROCEDURE OnProgresso(stProgress is STProgress)
PROGBAR_Principal = stProgress.nPorcentagem
STC_Status = stProgress.sEtapa
STC_Tempo = "Tempo restante: " + stProgress.nTempoRestante + "ms"

// Força atualização da interface
Multitask()
END

PROCEDURE OnProcessamentoFinal(arrResultados is array of strings)
Info("Processamento concluído!" + CR + "Arquivos processados: " + arrResultados..Count)
END
```

### Exemplo 4: Callback Chain (Encadeamento)

```wl
// Sistema de callbacks encadeados
PROCEDURE ExecutarChain(arrOperacoes is array of Procedure,
pCallbackFinal is Procedure = Null)

IF arrOperacoes..Count = 0 THEN
IF pCallbackFinal <> Null THEN
pCallbackFinal()
END
RETURN
END

// Executa primeira operação
pPrimeiraOperacao is Procedure = arrOperacoes[1]

// Remove da lista
Delete(arrOperacoes, 1)

// Cria callback para continuar a chain
PROCEDURE CallbackContinuar()
ExecutarChain(arrOperacoes, pCallbackFinal)
END

// Executa operação com callback de continuação
pPrimeiraOperacao(CallbackContinuar)
END

// Operações da chain
PROCEDURE Operacao1(pCallback is Procedure)
Info("Executando Operação 1")
pCallback()
END

PROCEDURE Operacao2(pCallback is Procedure)
Info("Executando Operação 2")
pCallback()
END

PROCEDURE Operacao3(pCallback is Procedure)
Info("Executando Operação 3")
pCallback()
END

// Callback final
PROCEDURE ChainCompleta()
Info("Todas as operações foram concluídas!")
END

// Uso
arrOps is array of Procedure = [Operacao1, Operacao2, Operacao3]
ExecutarChain(arrOps, ChainCompleta)
```

## Callbacks em Operações de Rede

### Exemplo 5: Download com Callback de Progresso

```wl
// Classe para download com callbacks
CDownload is Class
m_sURL is string
m_sDestino is string
m_pCallbackProgresso is Procedure
m_pCallbackCompleto is Procedure
m_pCallbackErro is Procedure
END

// Método para iniciar download
PROCEDURE CDownload::Iniciar(sURL is string, sDestino is string,
pProgresso is Procedure = Null,
pCompleto is Procedure = Null,
pErro is Procedure = Null)

m_sURL = sURL
m_sDestino = sDestino
m_pCallbackProgresso = pProgresso
m_pCallbackCompleto = pCompleto
m_pCallbackErro = pErro

// Inicia download em thread separada
ThreadExecute("ThreadDownload", threadNormal, DownloadInterno)
END

// Método interno de download
PROCEDURE CDownload::DownloadInterno()
httpReq is httpRequest
httpReq.URL = m_sURL

// Configura callback de progresso
IF m_pCallbackProgresso <> Null THEN
httpReq.ProgressCallback = CallbackProgresso
END

httpResp is httpResponse = HTTPSend(httpReq)

IF httpResp.StatusCode = 200 THEN
IF fSaveText(m_sDestino, httpResp.Content) THEN
IF m_pCallbackCompleto <> Null THEN
ExecuteMainThread(m_pCallbackCompleto, m_sDestino)
END
ELSE
IF m_pCallbackErro <> Null THEN
ExecuteMainThread(m_pCallbackErro, "Erro ao salvar arquivo")
END
END
ELSE
IF m_pCallbackErro <> Null THEN
ExecuteMainThread(m_pCallbackErro, "Erro HTTP: " + httpResp.StatusCode)
END
END
END

// Callback interno de progresso
PROCEDURE CDownload::CallbackProgresso(nBytesRecebidos is 8-byte int,
nTotalBytes is 8-byte int)
IF m_pCallbackProgresso <> Null THEN
nPorcentagem is int = (nBytesRecebidos * 100) / nTotalBytes
ExecuteMainThread(m_pCallbackProgresso, nPorcentagem, nBytesRecebidos, nTotalBytes)
END
END

// Uso da classe
PROCEDURE OnProgressoDownload(nPorcentagem is int, nRecebido is 8-byte int, nTotal is 8-byte int)
PROGBAR_Download = nPorcentagem
STC_Status = StringBuild("Baixando: %1% (%2/%3 bytes)", nPorcentagem, nRecebido, nTotal)
END

PROCEDURE OnDownloadCompleto(sArquivo is string)
Info("Download completo: " + sArquivo)
END

PROCEDURE OnDownloadErro(sErro is string)
Error("Erro no download: " + sErro)
END

// Criação e uso
oDownload is CDownload
oDownload.Iniciar("http://exemplo.com/arquivo.zip", "C:\temp\arquivo.zip",
OnProgressoDownload, OnDownloadCompleto, OnDownloadErro)
```

## Callbacks em WebDev

### Exemplo 6: AJAX com Callbacks

```wl
// Procedimento servidor que retorna JSON
PROCEDURE WS_BuscarDados(nID is int)
stDado is Structure
nID is int
sNome is string
sEmail is string
END

// Busca dados (simulado)
stDado.nID = nID
stDado.sNome = "Usuário " + nID
stDado.sEmail = "user" + nID + "@email.com"

// Retorna JSON
RESULT VariantToJSON(stDado)
END

// Código navegador com callback
PROCEDURE BuscarDadosAsync(nID is int, pCallback is Procedure)
// Executa AJAX
AJAXExecute(CallbackAJAX, WS_BuscarDados, nID)

// Função de callback do AJAX
PROCEDURE CallbackAJAX(bSucesso is boolean, sResultado is string, sErro is string)
IF bSucesso THEN
// Converte JSON para variant
vDados is Variant = JSONToVariant(sResultado)
pCallback(True, vDados, "")
ELSE
pCallback(False, Null, sErro)
END
END
END

// Uso no navegador
PROCEDURE OnDadosRecebidos(bSucesso is boolean, vDados is Variant, sErro is string)
IF bSucesso THEN
EDT_Nome = vDados.sNome
EDT_Email = vDados.sEmail
ELSE
Error("Erro ao buscar dados: " + sErro)
END
END

// Chamada
BuscarDadosAsync(123, OnDadosRecebidos)
```

## Callbacks em WinDev Mobile

### Exemplo 7: Localização com Callback

```wl
// Procedimento para obter localização
PROCEDURE ObterLocalizacao(pCallbackSucesso is Procedure,
pCallbackErro is Procedure = Null,
nTimeout is int = 30000)

// Verifica permissões
IF GPSStatus() <> gpsEnabled THEN
IF pCallbackErro <> Null THEN
pCallbackErro("GPS não disponível")
END
RETURN
END

// Inicia obtenção de localização em thread
ThreadExecute("ThreadGPS", threadNormal, ObterLocalizacaoThread,
pCallbackSucesso, pCallbackErro, nTimeout)
END

// Thread para obtenção de localização
PROCEDURE ObterLocalizacaoThread(pCallbackSucesso is Procedure,
pCallbackErro is Procedure,
nTimeout is int)

geoPos is geoPosition
nTentativas is int = 0
nMaxTentativas is int = nTimeout / 1000

WHILE nTentativas < nMaxTentativas
geoPos = GPSGetPosition()

IF geoPos.Latitude <> 0 AND geoPos.Longitude <> 0 THEN
// Sucesso - executa callback na thread principal
ExecuteMainThread(pCallbackSucesso, geoPos)
RETURN
END

nTentativas++
ThreadPause(1000)
END

// Timeout - executa callback de erro
IF pCallbackErro <> Null THEN
ExecuteMainThread(pCallbackErro, "Timeout ao obter localização")
END
END

// Callbacks
PROCEDURE OnLocalizacaoObtida(geoPos is geoPosition)
Info(StringBuild("Localização obtida:%1Latitude: %2%1Longitude: %3",
CR, geoPos.Latitude, geoPos.Longitude))

// Atualiza interface
STC_Latitude = geoPos.Latitude
STC_Longitude = geoPos.Longitude
END

PROCEDURE OnErroLocalizacao(sErro is string)
Error("Erro ao obter localização: " + sErro)
END

// Uso
ObterLocalizacao(OnLocalizacaoObtida, OnErroLocalizacao)
```

## Melhores Práticas

### 1. Sempre Verifique se o Callback é Nulo

```wl
IF pCallback <> Null THEN
pCallback(parametros...)
END
```

### 2. Use ExecuteMainThread para Threads

```wl
// Em threads secundárias, sempre execute callbacks na thread principal
ExecuteMainThread(pCallback, parametros...)
```

### 3. Tratamento de Erros em Callbacks

```wl
PROCEDURE CallbackComTratamento(pCallback is Procedure)
TRY
pCallback()
EXCEPTION
Error("Erro no callback: " + ExceptionInfo())
END
END
```

### 4. Documentação dos Callbacks

```wl
// Sempre documente os parâmetros esperados pelos callbacks
// Callback: pCallback(bSucesso is boolean, sResultado is string, sErro is string)
PROCEDURE ProcessarComCallback(sEntrada is string, pCallback is Procedure)
// ...
END
```

### 5. Evite Callbacks Aninhados Excessivos (Callback Hell)

```wl
// Em vez de aninhar callbacks, use encadeamento ou promises
// Ruim:
PROCEDURE CallbackHell()
Operacao1(()=> {
Operacao2(()=> {
Operacao3(()=> {
// Código difícil de ler
})
})
})
END

// Melhor: Use chain ou sistema de eventos
ExecutarChain([Operacao1, Operacao2, Operacao3], CallbackFinal)
```

## Conclusão

Os callbacks são uma ferramenta poderosa no WinDev que permite criar aplicações mais flexíveis, modulares e responsivas. Seja para operações assíncronas, validações dinâmicas, ou comunicação entre componentes, dominar os callbacks irá elevar significativamente a qualidade do seu código.

### Pontos-chave:

- Use callbacks para operações assíncronas e eventos personalizados
- Sempre verifique se o callback não é nulo antes de executá-lo
- Em threads secundárias, use ExecuteMainThread para executar callbacks
- Documente claramente os parâmetros esperados pelos callbacks
- Evite o “callback hell” usando encadeamento ou sistemas de eventos

Implementar esses padrões em seus projetos WinDev, WebDev e WinDev Mobile resultará em código mais limpo, manutenível e profissional.

--
Adriano José Boller
______________________________________________
Consultor e Representante Oficial da
PcSoft no Brasil
+55 (41) 99949 1800
adrianoboller@gmail.com
skype: adrianoboller
http://wxinformatica.com.br/
Registered member
4,618 messages
Posted on September, 19 2025 - 8:02 PM
Conceitos Fundamentais:
• O que são callbacks e suas vantagens
• Implementação básica com procedimentos
📱 Exemplos Práticos por Plataforma:
• WinDev: Validação de formulários, sistema de progresso, encadeamento de operações
• WebDev: Requisições AJAX com callbacks
• WinDev Mobile: Obtenção de localização GPS
🚀 Técnicas Avançadas:
• Callback chains (encadeamento)
• Download com progresso em tempo real
• Sistema de eventos personalizados
• Tratamento de threads com ExecuteMainThread
✅ Melhores Práticas:
• Verificação de callbacks nulos
• Tratamento de erros
• Documentação adequada
• Como evitar “callback hell”
O artigo fornece código WLanguage funcional que você pode usar diretamente em seus projetos. Cada exemplo é documentado e inclui casos de uso reais que você provavelmente encontrará no desenvolvimento com a plataforma PC Soft.

--
Adriano José Boller
______________________________________________
Consultor e Representante Oficial da
PcSoft no Brasil
+55 (41) 99949 1800
adrianoboller@gmail.com
skype: adrianoboller
http://wxinformatica.com.br/
Registered member
4,618 messages
Posted on September, 19 2025 - 8:06 PM
Expandir o artigo com 9 novos exemplos avançados baseados em técnicas reais do WinDev:
🆕 Novos Exemplos Adicionados:
Técnicas Avançadas:
• Exemplo 8: Closures com Timer (WinDev 20+)
• Exemplo 9: Factory de Callbacks Personalizados
• Exemplo 10: Callbacks com APIs/DLLs Windows
• Exemplo 11: Sistema de Notificações Multi-Callback
Padrões Arquiteturais:
• Exemplo 12: Sistema de Callback com Filtros
• Exemplo 13: Observer Pattern Completo
• Exemplo 14: Debug e Profiling de Callbacks
Técnicas de Performance:
• Exemplo 15: Promise-like Implementation
• Exemplo 16: Throttle e Debounce (Rate Limiting)
🎯 Destaques dos Novos Exemplos:
🔥 Closures e Contexto:
• Timer com closure que “lembra” variáveis locais
• Factory que cria callbacks personalizados com estado
⚡ Performance e Control:
• Throttle para limitar execuções por tempo
• Debounce para campos de pesquisa
• Sistema de filtros condicionais
🏗️ Arquitetura Profissional:
• Observer Pattern completo
• Sistema de notificações extensível
• Promise-like para async/await style
🐛 Debugging:
• Wrapper de debug com timing
• Logging automático de parâmetros
• Tratamento de exceções em callbacks
🔌 Integração:
• Callbacks com DLLs Windows (EnumWindows)
• APIs externas com callback nativo
Esses exemplos cobrem desde técnicas básicas até padrões arquiteturais avançados, fornecendo um arsenal completo para desenvolver aplicações WinDev de nível profissional com callbacks eficientes e maintíveis!​​​​​​​​​​​​​​​​

# WinDev e Callbacks: Guia Completo com Exemplos Práticos

## Introdução

Os callbacks são uma ferramenta fundamental no WinDev para gerenciar operações assíncronas, eventos personalizados e comunicação entre diferentes partes da aplicação. Este guia apresenta métodos simples e eficazes para implementar callbacks em seus projetos WinDev, WebDev e WinDev Mobile.

## O que são Callbacks?

Um callback é uma função que é passada como parâmetro para outra função e é executada em um momento específico, geralmente após a conclusão de uma operação assíncrona ou quando um evento particular ocorre.

### Vantagens dos Callbacks:

- **Flexibilidade**: Permitem personalizar o comportamento sem modificar o código principal
- **Reutilização**: O mesmo código pode ter diferentes comportamentos dependendo do callback
- **Assincronismo**: Ideais para operações que não bloqueiam a interface
- **Modularidade**: Facilitam a separação de responsabilidades

## Implementação Básica de Callbacks

### Exemplo 1: Callback Simples com Procedimento

```wl
// Procedimento que aceita um callback
PROCEDURE ProcessarDados(sArquivo is string, pCallback is Procedure)
bSucesso is boolean

// Simula processamento
bSucesso = HImportText("MeuArquivo", sArquivo)

// Chama o callback com o resultado
pCallback(bSucesso, sArquivo)
END

// Callback de sucesso
PROCEDURE OnProcessamentoCompleto(bSucesso is boolean, sArquivo is string)
IF bSucesso THEN
Info("Arquivo " + sArquivo + " processado com sucesso!")
ELSE
Error("Erro ao processar arquivo " + sArquivo)
END
END

// Uso
ProcessarDados("dados.txt", OnProcessamentoCompleto)
```

### Exemplo 2: Callbacks com Parâmetros Múltiplos

```wl
// Procedimento para validação com callback
PROCEDURE ValidarFormulario(pCallbackSucesso is Procedure, pCallbackErro is Procedure)
arrErros is array of strings

// Validações
IF EDT_Nome = "" THEN
Add(arrErros, "Nome é obrigatório")
END

IF EDT_Email = "" THEN
Add(arrErros, "Email é obrigatório")
ELSE IF Position(EDT_Email, "@") = 0 THEN
Add(arrErros, "Email inválido")
END

// Executa callback apropriado
IF arrErros..Count = 0 THEN
pCallbackSucesso()
ELSE
pCallbackErro(arrErros)
END
END

// Callbacks
PROCEDURE OnValidacaoSucesso()
Info("Formulário válido!")
// Continua com o processamento
END

PROCEDURE OnValidacaoErro(arrErros is array of strings)
sMsg is string = "Erros encontrados:" + CR
FOR EACH sErro OF arrErros
sMsg += "- " + sErro + CR
END
Error(sMsg)
END
```

## Callbacks Avançados

### Exemplo 3: Sistema de Progress com Callback

```wl
// Estrutura para informações de progresso
STProgress is Structure
nPorcentagem is int
sEtapa is string
nTempoRestante is int
END

// Procedimento com callback de progresso
PROCEDURE ProcessarLote(arrArquivos is array of strings,
pCallbackProgress is Procedure,
pCallbackFinal is Procedure)

nTotal is int = arrArquivos..Count
nProcessados is int = 0
arrResultados is array of strings

FOR EACH sArquivo OF arrArquivos
// Simula processamento
ThreadPause(100)

nProcessados++

// Atualiza progresso
stProgress is STProgress
stProgress.nPorcentagem = (nProcessados * 100) / nTotal
stProgress.sEtapa = "Processando: " + ExtractFileName(sArquivo)
stProgress.nTempoRestante = (nTotal - nProcessados) * 100

pCallbackProgress(stProgress)

// Simula resultado
Add(arrResultados, sArquivo + " - OK")
END

pCallbackFinal(arrResultados)
END

// Callbacks
PROCEDURE OnProgresso(stProgress is STProgress)
PROGBAR_Principal = stProgress.nPorcentagem
STC_Status = stProgress.sEtapa
STC_Tempo = "Tempo restante: " + stProgress.nTempoRestante + "ms"

// Força atualização da interface
Multitask()
END

PROCEDURE OnProcessamentoFinal(arrResultados is array of strings)
Info("Processamento concluído!" + CR + "Arquivos processados: " + arrResultados..Count)
END
```

### Exemplo 4: Callback Chain (Encadeamento)

```wl
// Sistema de callbacks encadeados
PROCEDURE ExecutarChain(arrOperacoes is array of Procedure,
pCallbackFinal is Procedure = Null)

IF arrOperacoes..Count = 0 THEN
IF pCallbackFinal <> Null THEN
pCallbackFinal()
END
RETURN
END

// Executa primeira operação
pPrimeiraOperacao is Procedure = arrOperacoes[1]

// Remove da lista
Delete(arrOperacoes, 1)

// Cria callback para continuar a chain
PROCEDURE CallbackContinuar()
ExecutarChain(arrOperacoes, pCallbackFinal)
END

// Executa operação com callback de continuação
pPrimeiraOperacao(CallbackContinuar)
END

// Operações da chain
PROCEDURE Operacao1(pCallback is Procedure)
Info("Executando Operação 1")
pCallback()
END

PROCEDURE Operacao2(pCallback is Procedure)
Info("Executando Operação 2")
pCallback()
END

PROCEDURE Operacao3(pCallback is Procedure)
Info("Executando Operação 3")
pCallback()
END

// Callback final
PROCEDURE ChainCompleta()
Info("Todas as operações foram concluídas!")
END

// Uso
arrOps is array of Procedure = [Operacao1, Operacao2, Operacao3]
ExecutarChain(arrOps, ChainCompleta)
```

## Callbacks em Operações de Rede

### Exemplo 5: Download com Callback de Progresso

```wl
// Classe para download com callbacks
CDownload is Class
m_sURL is string
m_sDestino is string
m_pCallbackProgresso is Procedure
m_pCallbackCompleto is Procedure
m_pCallbackErro is Procedure
END

// Método para iniciar download
PROCEDURE CDownload::Iniciar(sURL is string, sDestino is string,
pProgresso is Procedure = Null,
pCompleto is Procedure = Null,
pErro is Procedure = Null)

m_sURL = sURL
m_sDestino = sDestino
m_pCallbackProgresso = pProgresso
m_pCallbackCompleto = pCompleto
m_pCallbackErro = pErro

// Inicia download em thread separada
ThreadExecute("ThreadDownload", threadNormal, DownloadInterno)
END

// Método interno de download
PROCEDURE CDownload::DownloadInterno()
httpReq is httpRequest
httpReq.URL = m_sURL

// Configura callback de progresso
IF m_pCallbackProgresso <> Null THEN
httpReq.ProgressCallback = CallbackProgresso
END

httpResp is httpResponse = HTTPSend(httpReq)

IF httpResp.StatusCode = 200 THEN
IF fSaveText(m_sDestino, httpResp.Content) THEN
IF m_pCallbackCompleto <> Null THEN
ExecuteMainThread(m_pCallbackCompleto, m_sDestino)
END
ELSE
IF m_pCallbackErro <> Null THEN
ExecuteMainThread(m_pCallbackErro, "Erro ao salvar arquivo")
END
END
ELSE
IF m_pCallbackErro <> Null THEN
ExecuteMainThread(m_pCallbackErro, "Erro HTTP: " + httpResp.StatusCode)
END
END
END

// Callback interno de progresso
PROCEDURE CDownload::CallbackProgresso(nBytesRecebidos is 8-byte int,
nTotalBytes is 8-byte int)
IF m_pCallbackProgresso <> Null THEN
nPorcentagem is int = (nBytesRecebidos * 100) / nTotalBytes
ExecuteMainThread(m_pCallbackProgresso, nPorcentagem, nBytesRecebidos, nTotalBytes)
END
END

// Uso da classe
PROCEDURE OnProgressoDownload(nPorcentagem is int, nRecebido is 8-byte int, nTotal is 8-byte int)
PROGBAR_Download = nPorcentagem
STC_Status = StringBuild("Baixando: %1% (%2/%3 bytes)", nPorcentagem, nRecebido, nTotal)
END

PROCEDURE OnDownloadCompleto(sArquivo is string)
Info("Download completo: " + sArquivo)
END

PROCEDURE OnDownloadErro(sErro is string)
Error("Erro no download: " + sErro)
END

// Criação e uso
oDownload is CDownload
oDownload.Iniciar("http://exemplo.com/arquivo.zip", "C:\temp\arquivo.zip",
OnProgressoDownload, OnDownloadCompleto, OnDownloadErro)
```

## Callbacks em WebDev

### Exemplo 6: AJAX com Callbacks

```wl
// Procedimento servidor que retorna JSON
PROCEDURE WS_BuscarDados(nID is int)
stDado is Structure
nID is int
sNome is string
sEmail is string
END

// Busca dados (simulado)
stDado.nID = nID
stDado.sNome = "Usuário " + nID
stDado.sEmail = "user" + nID + "@email.com"

// Retorna JSON
RESULT VariantToJSON(stDado)
END

// Código navegador com callback
PROCEDURE BuscarDadosAsync(nID is int, pCallback is Procedure)
// Executa AJAX
AJAXExecute(CallbackAJAX, WS_BuscarDados, nID)

// Função de callback do AJAX
PROCEDURE CallbackAJAX(bSucesso is boolean, sResultado is string, sErro is string)
IF bSucesso THEN
// Converte JSON para variant
vDados is Variant = JSONToVariant(sResultado)
pCallback(True, vDados, "")
ELSE
pCallback(False, Null, sErro)
END
END
END

// Uso no navegador
PROCEDURE OnDadosRecebidos(bSucesso is boolean, vDados is Variant, sErro is string)
IF bSucesso THEN
EDT_Nome = vDados.sNome
EDT_Email = vDados.sEmail
ELSE
Error("Erro ao buscar dados: " + sErro)
END
END

// Chamada
BuscarDadosAsync(123, OnDadosRecebidos)
```

## Callbacks em WinDev Mobile

### Exemplo 7: Localização com Callback

```wl
// Procedimento para obter localização
PROCEDURE ObterLocalizacao(pCallbackSucesso is Procedure,
pCallbackErro is Procedure = Null,
nTimeout is int = 30000)

// Verifica permissões
IF GPSStatus() <> gpsEnabled THEN
IF pCallbackErro <> Null THEN
pCallbackErro("GPS não disponível")
END
RETURN
END

// Inicia obtenção de localização em thread
ThreadExecute("ThreadGPS", threadNormal, ObterLocalizacaoThread,
pCallbackSucesso, pCallbackErro, nTimeout)
END

// Thread para obtenção de localização
PROCEDURE ObterLocalizacaoThread(pCallbackSucesso is Procedure,
pCallbackErro is Procedure,
nTimeout is int)

geoPos is geoPosition
nTentativas is int = 0
nMaxTentativas is int = nTimeout / 1000

WHILE nTentativas < nMaxTentativas
geoPos = GPSGetPosition()

IF geoPos.Latitude <> 0 AND geoPos.Longitude <> 0 THEN
// Sucesso - executa callback na thread principal
ExecuteMainThread(pCallbackSucesso, geoPos)
RETURN
END

nTentativas++
ThreadPause(1000)
END

// Timeout - executa callback de erro
IF pCallbackErro <> Null THEN
ExecuteMainThread(pCallbackErro, "Timeout ao obter localização")
END
END

// Callbacks
PROCEDURE OnLocalizacaoObtida(geoPos is geoPosition)
Info(StringBuild("Localização obtida:%1Latitude: %2%1Longitude: %3",
CR, geoPos.Latitude, geoPos.Longitude))

// Atualiza interface
STC_Latitude = geoPos.Latitude
STC_Longitude = geoPos.Longitude
END

PROCEDURE OnErroLocalizacao(sErro is string)
Error("Erro ao obter localização: " + sErro)
END

// Uso
ObterLocalizacao(OnLocalizacaoObtida, OnErroLocalizacao)
```

## Melhores Práticas

### 1. Sempre Verifique se o Callback é Nulo

```wl
IF pCallback <> Null THEN
pCallback(parametros...)
END
```

### 2. Use ExecuteMainThread para Threads

```wl
// Em threads secundárias, sempre execute callbacks na thread principal
ExecuteMainThread(pCallback, parametros...)
```

### 3. Tratamento de Erros em Callbacks

```wl
PROCEDURE CallbackComTratamento(pCallback is Procedure)
TRY
pCallback()
EXCEPTION
Error("Erro no callback: " + ExceptionInfo())
END
END
```

### 4. Documentação dos Callbacks

```wl
// Sempre documente os parâmetros esperados pelos callbacks
// Callback: pCallback(bSucesso is boolean, sResultado is string, sErro is string)
PROCEDURE ProcessarComCallback(sEntrada is string, pCallback is Procedure)
// ...
END
```

### 5. Evite Callbacks Aninhados Excessivos (Callback Hell)

```wl
// Em vez de aninhar callbacks, use encadeamento ou promises
// Ruim:
PROCEDURE CallbackHell()
Operacao1(()=> {
Operacao2(()=> {
Operacao3(()=> {
// Código difícil de ler
})
})
})
END

// Melhor: Use chain ou sistema de eventos
ExecutarChain([Operacao1, Operacao2, Operacao3], CallbackFinal)
```

## Closures e Procedimentos Internos (WinDev 20+)

### Exemplo 8: Timer com Closure

```wl
// Procedimento que usa closure com timer
PROCEDURE ExemploClosure()
nContador is int = 0

// Closure - procedimento interno que acessa variável local
PROCEDURE INTERNAL TimerCallback()
nContador++
Info("Contador: " + nContador)

// Para após 5 execuções
IF nContador >= 5 THEN
TimerEnd("MonTimer")
END
END

// Inicia timer com a closure
Timer("MonTimer", 1000, TimerCallback)
END
```

### Exemplo 9: Factory de Callbacks Personalizados

```wl
// Factory que cria callbacks específicos com estado
PROCEDURE CriarCallbackPersonalizado(sNome is string, nIDUsuario is int) : Procedure

// Retorna uma closure que "lembra" dos parâmetros
PROCEDURE INTERNAL CallbackPersonalizado(bSucesso is boolean, vDados is Variant)
sLogEntry is string = StringBuild("[%1] Usuário %2: %3",
DateTimeToString(Now()),
nIDUsuario,
sNome)

IF bSucesso THEN
sLogEntry += " - Sucesso: " + VariantToJSON(vDados)
ELSE
sLogEntry += " - Erro: " + vDados
END

// Log personalizado
Trace(sLogEntry)

// Notificação específica do usuário
Info(StringBuild("Operação para %1 (ID: %2) %3!",
sNome, nIDUsuario,
iif(bSucesso, "concluída", "falhou")))
END

RESULT CallbackPersonalizado
END

// Uso da factory
pCallbackJoao is Procedure = CriarCallbackPersonalizado("João Silva", 123)
pCallbackMaria is Procedure = CriarCallbackPersonalizado("Maria Santos", 456)

// Cada callback tem seu próprio contexto
ProcessarDados("dados1.txt", pCallbackJoao)
ProcessarDados("dados2.txt", pCallbackMaria)
```

## Callbacks com APIs Externas

### Exemplo 10: Callback com DLL Windows

```wl
// Declaração de callback para API Windows
PROCEDURE CallbackEnumWindows(hWnd is system int, lParam is system int) : boolean
sTitle is string = GetWindowText(hWnd, 256)
sClassName is string = GetClassName(hWnd, 256)

IF Length(sTitle) > 0 THEN
// Adiciona à lista (passada via lParam)
pLista is Pointer = lParam
ListAdd(pLista^, sTitle + " (" + sClassName + ")")
END

RESULT True // Continua enumeração
END

// Procedimento principal
PROCEDURE ListarJanelas() : array of strings
arrJanelas is array of strings

// Chama API Windows com callback
EnumWindows(&CallbackEnumWindows, &arrJanelas)

RESULT arrJanelas
END
```

### Exemplo 11: Sistema de Notificações com Múltiplos Callbacks

```wl
// Classe para gerenciar notificações
CNotificationManager is Class
m_arrCallbacks is associative array of array of Procedure
END

// Registrar callback para evento
PROCEDURE CNotificationManager::Subscribe(sEvento is string, pCallback is Procedure)
IF NOT m_arrCallbacks..Exist[sEvento] THEN
m_arrCallbacks[sEvento] = new array of Procedure
END

Add(m_arrCallbacks[sEvento], pCallback)
END

// Disparar evento
PROCEDURE CNotificationManager::Trigger(sEvento is string, vParametros is Variant = Null)
IF m_arrCallbacks..Exist[sEvento] THEN
FOR EACH pCallback OF m_arrCallbacks[sEvento]
TRY
IF vParametros = Null THEN
pCallback()
ELSE
pCallback(vParametros)
END
EXCEPTION
Error("Erro no callback: " + ExceptionInfo())
END
END
END
END

// Remover callback
PROCEDURE CNotificationManager::Unsubscribe(sEvento is string, pCallback is Procedure)
IF m_arrCallbacks..Exist[sEvento] THEN
nIndex is int = Seek(m_arrCallbacks[sEvento], pCallback)
IF nIndex > 0 THEN
Delete(m_arrCallbacks[sEvento], nIndex)
END
END
END

// Uso do sistema de notificações
GLOBAL
g_oNotificationManager is CNotificationManager

// Callbacks específicos
PROCEDURE OnUsuarioLogou(vDados is Variant)
Info("Usuário " + vDados.sNome + " fez login")
END

PROCEDURE OnProdutoVendido(vDados is Variant)
Info("Produto vendido: " + vDados.sProduto + " - R$ " + vDados.nValor)
END

PROCEDURE OnErroSistema(vDados is Variant)
Error("Erro do sistema: " + vDados.sErro)
// Enviar email, log, etc.
END

// Registrar eventos
g_oNotificationManager.Subscribe("usuario_login", OnUsuarioLogou)
g_oNotificationManager.Subscribe("produto_vendido", OnProdutoVendido)
g_oNotificationManager.Subscribe("erro_sistema", OnErroSistema)

// Disparar eventos
stDadosLogin is Structure
sNome is string = "João"
nID is int = 123
END
g_oNotificationManager.Trigger("usuario_login", stDadosLogin)
```

## Callbacks Condicionais e Filtros

### Exemplo 12: Sistema de Callback com Filtros

```wl
// Estrutura para callback com filtro
STCallbackFiltrado is Structure
pCallback is Procedure
pFiltro is Procedure // Função que retorna boolean
sPrioridade is string
END

// Gerenciador de callbacks filtrados
PROCEDURE ExecutarCallbacksFiltrados(arrCallbacks is array of STCallbackFiltrado,
vDados is Variant)

// Ordena por prioridade (alta, media, baixa)
ArraySort(arrCallbacks, asAscending, "sPrioridade")

FOR EACH stCallback OF arrCallbacks
bExecutar is boolean = True

// Aplica filtro se existir
IF stCallback.pFiltro <> Null THEN
TRY
bExecutar = stCallback.pFiltro(vDados)
EXCEPTION
bExecutar = False
Trace("Erro no filtro: " + ExceptionInfo())
END
END

// Executa callback se passou no filtro
IF bExecutar THEN
TRY
stCallback.pCallback(vDados)
EXCEPTION
Trace("Erro no callback: " + ExceptionInfo())
END
END
END
END

// Filtros
PROCEDURE FiltroUsuarioAdmin(vUsuario is Variant) : boolean
RESULT vUsuario.sTipo = "ADMIN"
END

PROCEDURE FiltroValorAlto(vTransacao is Variant) : boolean
RESULT vTransacao.nValor >= 1000
END

// Callbacks
PROCEDURE CallbackAdminLogin(vUsuario is Variant)
Info("ADMIN logou: " + vUsuario.sNome)
// Registra log especial para admins
END

PROCEDURE CallbackTransacaoAlta(vTransacao is Variant)
Info("Transação de alto valor: R$ " + vTransacao.nValor)
// Notifica gerência
END

// Configuração
arrCallbacksLogin is array of STCallbackFiltrado
stCallbackAdmin is STCallbackFiltrado
stCallbackAdmin.pCallback = CallbackAdminLogin
stCallbackAdmin.pFiltro = FiltroUsuarioAdmin
stCallbackAdmin.sPrioridade = "alta"
Add(arrCallbacksLogin, stCallbackAdmin)
```

## Padrão Observer com Callbacks

### Exemplo 13: Observer Pattern Implementado

```wl
// Interface Observer
CObserver is Class
PROCEDURE OnNotificacao(sEvento is string, vDados is Variant) : virtual
END

// Observador concreto
CLogObserver inherits CObserver
PROCEDURE OnNotificacao(sEvento is string, vDados is Variant) : override
sLog is string = StringBuild("[%1] %2: %3",
DateTimeToString(Now()),
sEvento,
VariantToJSON(vDados))
fWriteLine("log.txt", sLog)
END
END

// Subject observável
CSubjectObservavel is Class
m_arrObservers is array of CObserver
END

PROCEDURE CSubjectObservavel::AdicionarObserver(oObserver is CObserver)
Add(m_arrObservers, oObserver)
END

PROCEDURE CSubjectObservavel::RemoverObserver(oObserver is CObserver)
nIndex is int = Seek(m_arrObservers, oObserver)
IF nIndex > 0 THEN
Delete(m_arrObservers, nIndex)
END
END

PROCEDURE CSubjectObservavel::NotificarObservers(sEvento is string, vDados is Variant)
FOR EACH oObserver OF m_arrObservers
oObserver.OnNotificacao(sEvento, vDados)
END
END

// Uso com callbacks funcionais também
PROCEDURE CSubjectObservavel::AdicionarCallbackObserver(pCallback is Procedure)
// Cria adapter que converte callback em observer
PROCEDURE INTERNAL CallbackAdapter(sEvento is string, vDados is Variant)
pCallback(sEvento, vDados)
END

// Pode adicionar como observer funcional
// (implementação específica dependeria da arquitetura)
END
```

## Debugging e Profiling de Callbacks

### Exemplo 14: Sistema de Debug para Callbacks

```wl
// Wrapper para debug de callbacks
PROCEDURE WrapCallbackComDebug(pCallback is Procedure, sNome is string) : Procedure

PROCEDURE INTERNAL CallbackComDebug(*)
dInicio is DateTime = Now()
sParametros is string = ""

// Constrói string dos parâmetros (limitado)
FOR nI = 1 _TO_ MyParameters()..Count
IF nI > 1 THEN sParametros += ", "
sParametros += VariantToString(MyParameters()[nI])
END

Trace(StringBuild("CALLBACK [%1] INICIADO - Parâmetros: %2", sNome, sParametros))

TRY
// Executa callback original
ExecuteProcedure(pCallback, MyParameters())

dFim is DateTime = Now()
nDuracao is int = DateTimeDifference(dFim, dInicio)
Trace(StringBuild("CALLBACK [%1] CONCLUÍDO - Duração: %2ms", sNome, nDuracao))

EXCEPTION
Trace(StringBuild("CALLBACK [%1] ERRO: %2", sNome, ExceptionInfo()))
Error("Erro no callback " + sNome + ": " + ExceptionInfo())
END
END

RESULT CallbackComDebug
END

// Uso do wrapper de debug
PROCEDURE MeuCallback(sResultado is string)
// Simula processamento
ThreadPause(100)
Info("Resultado: " + sResultado)
END

// Wrapping do callback para debug
pCallbackComDebug is Procedure = WrapCallbackComDebug(MeuCallback, "ProcessamentoArquivo")

// Usar o callback com debug
ProcessarDados("teste.txt", pCallbackComDebug)
```

## Callbacks Assíncronos Avançados

### Exemplo 15: Promise-like com Callbacks

```wl
// Classe que simula Promises
CPromise is Class
m_bCompleto is boolean = False
m_bSucesso is boolean = False
m_vResultado is Variant
m_sErro is string
m_arrCallbacksSucesso is array of Procedure
m_arrCallbacksErro is array of Procedure
END

// Método Then (para sucesso)
PROCEDURE CPromise::Then(pCallback is Procedure) : CPromise
IF m_bCompleto THEN
IF m_bSucesso THEN
pCallback(m_vResultado)
END
ELSE
Add(m_arrCallbacksSucesso, pCallback)
END

RESULT this
END

// Método Catch (para erro)
PROCEDURE CPromise::Catch(pCallback is Procedure) : CPromise
IF m_bCompleto THEN
IF NOT m_bSucesso THEN
pCallback(m_sErro)
END
ELSE
Add(m_arrCallbacksErro, pCallback)
END

RESULT this
END

// Resolver promise com sucesso
PROCEDURE CPromise::Resolver(vResultado is Variant)
IF m_bCompleto THEN RETURN

m_bCompleto = True
m_bSucesso = True
m_vResultado = vResultado

// Executa todos os callbacks de sucesso
FOR EACH pCallback OF m_arrCallbacksSucesso
pCallback(vResultado)
END
END

// Rejeitar promise com erro
PROCEDURE CPromise::Rejeitar(sErro is string)
IF m_bCompleto THEN RETURN

m_bCompleto = True
m_bSucesso = False
m_sErro = sErro

// Executa todos os callbacks de erro
FOR EACH pCallback OF m_arrCallbacksErro
pCallback(sErro)
END
END

// Uso da Promise
PROCEDURE ProcessarComPromise(sArquivo is string) : CPromise
oPromise is CPromise

// Processa em thread
ThreadExecute("ProcessThread", threadNormal, ThreadProcessamento, oPromise, sArquivo)

RESULT oPromise
END

PROCEDURE ThreadProcessamento(oPromise is CPromise, sArquivo is string)
IF fFileExist(sArquivo) THEN
sConteudo is string = fLoadText(sArquivo)
ExecuteMainThread(oPromise.Resolver, sConteudo)
ELSE
ExecuteMainThread(oPromise.Rejeitar, "Arquivo não encontrado: " + sArquivo)
END
END

// Uso elegante com encadeamento
ProcessarComPromise("dados.txt")
.Then((sConteudo) => { Info("Sucesso: " + Left(sConteudo, 50) + "...") })
.Catch((sErro) => { Error("Erro: " + sErro) })
```

## Callbacks com Rate Limiting

### Exemplo 16: Throttle e Debounce

```wl
// Implementação de Throttle (limita execuções por tempo)
PROCEDURE ThrottleCallback(pCallback is Procedure, nIntervaloMS is int) : Procedure
LOCAL
dUltimaExecucao is DateTime

PROCEDURE INTERNAL CallbackThrottled(*)
dAgora is DateTime = Now()

IF DateTimeDifference(dAgora, dUltimaExecucao) >= nIntervaloMS THEN
dUltimaExecucao = dAgora
ExecuteProcedure(pCallback, MyParameters())
END
END

RESULT CallbackThrottled
END

// Implementação de Debounce (executa apenas após período sem chamadas)
PROCEDURE DebounceCallback(pCallback is Procedure, nDelayMS is int) : Procedure
LOCAL
sTimerID is string = GetIdentifier()

PROCEDURE INTERNAL CallbackDebounced(*)
// Cancela timer anterior se existir
TimerEnd(sTimerID)

// Captura parâmetros atuais
arrParametros is array of Variant
FOR nI = 1 _TO_ MyParameters()..Count
Add(arrParametros, MyParameters()[nI])
END

// Cria closure que será executada pelo timer
PROCEDURE INTERNAL ExecutarCallback()
ExecuteProcedure(pCallback, arrParametros)
END

// Agenda execução
Timer(sTimerID, nDelayMS, ExecutarCallback)
END

RESULT CallbackDebounced
END

// Exemplo de uso
PROCEDURE OnPesquisar(sTexto is string)
// Simula pesquisa cara
Info("Pesquisando: " + sTexto)
END

// Callback com debounce para campo de pesquisa
pCallbackDebounced is Procedure = DebounceCallback(OnPesquisar, 500)

// No evento de modificação do campo
PROCEDURE EDT_Pesquisa::OnModification()
pCallbackDebounced(EDT_Pesquisa)
END
```

## Conclusão

Os callbacks são uma ferramenta poderosa no WinDev que permite criar aplicações mais flexíveis, modulares e responsivas. Com as técnicas avançadas apresentadas, incluindo closures, promises, observers e rate limiting, você pode criar arquiteturas sofisticadas que rivalizam com frameworks modernos.

### Pontos-chave adicionais:

- **Closures (WinDev 20+)**: Permitem callbacks que “lembram” do contexto
- **Procedimentos Internos**: Ideais para callbacks específicos e temporários
- **Rate Limiting**: Throttle e Debounce para controlar frequência de execução
- **Promise-like**: Padrão para gerenciar operações assíncronas de forma elegante
- **Observer Pattern**: Para sistemas de notificação escaláveis
- **Debug Wrappers**: Facilita debugging e profiling de callbacks
- **APIs Externas**: Integração com DLLs Windows usando callbacks

### Boas Práticas Específicas:

- Use closures para callbacks que precisam de contexto
- Implemente rate limiting em callbacks de interface (pesquisa, validação)
- Wrapping para debug em desenvolvimento
- Sistema de filtros para callbacks condicionais
- Pattern Observer para arquiteturas orientadas a eventos

Implementar esses padrões avançados em seus projetos WinDev, WebDev e WinDev Mobile resultará em código mais limpo, manutenível, profissional e com melhor performance.

--
Adriano José Boller
______________________________________________
Consultor e Representante Oficial da
PcSoft no Brasil
+55 (41) 99949 1800
adrianoboller@gmail.com
skype: adrianoboller
http://wxinformatica.com.br/
Message modified, September, 19 2025 - 8:08 PM