Em busca da interface perfeita, fico tentando variações de controles e posicionamentos diversos para criar um padrão para meu sistema, e com isto vou aprimorando o uso dos mesmos.
Neste tópico, baseado em uma opção de meu sistema, apresento juntamente com os arquivos necessários como produzir um diálogo sofisticado usando os controles 5Win como, dialog, listbox, page e o conceito de recursos embutidos.
Neste tópico voce aprenderá a manusear .RC que são arquivos textos contendo os parâmetros necessários para criação de diálogos e posicionamento de controles. Eles funcionam identicamente às DLLs com a diferença de serem linkados diretamente ao executável no final produzindo um aquivo apenas.
O primeiro passo é baixar o arquivo contendo os recursos necessários para o teste. Descompacte-os em uma pasta qualquer.
http://www.5volution.com.br/downloads/forum/forum5.zip
Vamos aos arquivos principais
O primeiro deles é o arquivo de recursos, o FORUM5.RC.
Ele possui definidas alguns diálogos que são:
TestBMPLbx - Diálogo principal
dlgAUX - Diálogo de suporte ao controle PAGE
dlgTADocto - Diálogo de suporte a tabela DOCTO
dlgTASiglas - Diálogo de suporte a tabela SIGLAS
dlgTAMensal - Diálogo de suporte a tabela MENSAL
Para abrir este arquivo e analisá-lo, basta usar o WorkShop.
Já o arquivo FORUM5.PRG contém partes interessantes que acho melhor explicá-las:
A listbox principal necessita de um vetor contendo os itens que farão o disparo da mudança de página do controle PAGE.
local aMenuItens := { "Documentos",;
"Siglas",;
"Mensalidades" }
Ao escolher um item dentro do listbox, o parâmetro ON CHANGE passa para o método oPags:SetOption() a posição numérica do item dentro do vetor usando a função ascan()
REDEFINE LISTBOX cItem ;
ON CHANGE oPags:SetOption(ascan(aMenuItens,cItem));
ITEMS aMenuItens ;
Logo no inicio do .PRG existe o comando SetHandleCount(150) que define a quantidade de arquivos que serão abertos na sessão do aplicativo e caso não seja definido obteremos erros de abertura de arquivo.
SetHandleCount(150)
Não devemos esquecer de invocar a DLL padrão da Borland para que nossas diálogo sejam executadas, portanto no .PRG principal esta chamada deverá existir sempre.
LoadLibrary("bwcc.dll")
Todas as tabelas utilizadas são abertas no modo SHARED e o parâmetro NEW ativa uma nova área de trabalho
USE DOCTO SHARED NEW INDEX DOCTO001,DOCTO002
USE SIGLAS SHARED NEW INDEX SIGLA019
USE MENSAL SHARED NEW INDEX MENSA001
Para que os controles tenham um apelo visual triidimensional ativamos o comando SET _3DLOOK ON
O diálogo principal é ativado e desenhado à partir da informações contidas no arquivo .RC e será chamado via parâmetro RESOURCE
DEFINE DIALOG oDlg RESOURCE "TestBMPLbx"
O controle PAGE será redefinido com identificar 11 e ativará os diálogos de mesmo nome dlgAUX para todas as páginas definidas, já que as mesmas terão aparencia idêntica.
REDEFINE PAGES oPags ID 11 OF oDlg DIALOGS "dlgAUX", "dlgAUX", "dlgAUX"
O passo principal de vinculo de cada listbox criada é redefini-la com a área da tabela já ativa, portanto, seleciono a tabela e redefino os controles não esquecendo de mudar as identificações dos controles para não obter erros, como oLbx1, oLbx2, aDialogs[1], aDialogs[3], etc.
// **** Documentos ****
SELE DOCTO
REDEFINE LISTBOX oLbx1 FIELDS ID 600 OF oPags:aDialogs[1]
REDEFINE BTNBMP ID 601 OF oPags:aDialogs[1] RESOURCE "bmp_plus" TOOLTIP "Adiciona registro a tabela atual" ACTION TabEDT( 1, oLbx1, "NOVO" )
REDEFINE BTNBMP ID 602 OF oPags:aDialogs[1] RESOURCE "bmp_minus" TOOLTIP "Exclui o registro da tabela atual" ACTION TabDEL( 1, oLbx1)
REDEFINE BTNBMP ID 603 OF oPags:aDialogs[1] RESOURCE "bmp_editar" TOOLTIP "Altera dados do registro atual" ACTION TabEDT( 1, oLbx1, "MOSTRA" )
REDEFINE BTNBMP ID 604 OF oPags:aDialogs[1] RESOURCE "bmp_printer" TOOLTIP "Imprime o listagem da tabela atual" ACTION TabRPT( 1, oLbx1)
oLbx1:nClrPane := { || IIF( ( oLbx1:cAlias)->(OrdKeyNo()) %2 == 1, CLR_VERDINHO,CLR_CREME)}
oLbx1:lAdjLastCol := .F.
oLbx1:nStyle := 1
oLbx1:nHeaderHeight*= 2
// **** Siglas ****
SELE SIGLAS
REDEFINE LISTBOX oLbx2 FIELDS ID 600 OF oPags:aDialogs[2]
REDEFINE BTNBMP ID 601 OF oPags:aDialogs[2] RESOURCE "bmp_plus" TOOLTIP "Adiciona registro a tabela atual" ACTION TabEDT( 2, oLbx2, "NOVO" )
REDEFINE BTNBMP ID 602 OF oPags:aDialogs[2] RESOURCE "bmp_minus" TOOLTIP "Exclui o registro da tabela atual" ACTION TabDEL( 2, oLbx2)
REDEFINE BTNBMP ID 603 OF oPags:aDialogs[2] RESOURCE "bmp_editar" TOOLTIP "Altera dados do registro atual" ACTION TabEDT( 2, oLbx2, "MOSTRA" )
REDEFINE BTNBMP ID 604 OF oPags:aDialogs[2] RESOURCE "bmp_printer" TOOLTIP "Imprime o listagem da tabela atual" ACTION TabRPT( 2, oLbx2)
oLbx2:nClrPane := { || IIF( ( oLbx2:cAlias)->(OrdKeyNo()) %2 == 1, CLR_VERDINHO,CLR_CREME)}
oLbx2:lAdjLastCol := .F.
oLbx2:nStyle := 1
oLbx2:nHeaderHeight*= 2
A função TabDEL apenas força a mudança de área para que não ocorra deleção de registros em uma tabela à partir de ação executada em outra tabela. Para os programadores mais experientes esta função poderá ser descartada pelo uso de funções mais diretas.
static function TabDEL( _num_, _lbx_ )
do case
case _num_ = 1
SELE DOCTO
case _num_ = 2
SELE SIGLAS
case _num_ = 3
SELE MENSAL
endcase
if MsgYesNo( "Tem certeza da exclusao deste registro?" )
RLOCK()
DELETE
COMMIT
_lbx_:UpStable()
_lbx_:Refresh()
endif
return .t.
A funcão TabRPT apresenta apenas um exemplo de obtenção de uma listagem à partir de uma tabela usando o comando REPORT, sendo de simples manuseio.
static function TabRPT( _num_, _lbx_ )
local oRpt
local n
local cAlias := If( _lbx_ != nil, _lbx_:cAlias, Alias() )
do case
case _num_ = 1
case _num_ = 2
REPORT oRpt TITLE "Relatorio: " + cAlias ;
HEADER "Data: " + DToC( Date() ) + ", Hora: " + Time() ;
FOOTER "Pagina: " + Str( oRpt:nPage, 3 ) ;
PREVIEW
if Empty( oRpt ) .or. oRpt:oDevice:hDC == 0
return nil
endif
COLUMN TITLE "UF" DATA SIGLAS->UF
COLUMN TITLE "ESTADO" DATA SIGLAS->ESTADO
COLUMN TITLE "ICMS" DATA SIGLAS->ICMS
COLUMN TITLE "IPI" DATA SIGLAS->IPI
COLUMN TITLE "REDUCAO" DATA SIGLAS->REDUCAO
ENDREPORT
ACTIVATE REPORT oRpt
GO TOP
case _num_ = 3
endcase
return .t.
A função TabEDT irá acionar o diálogo de manutenção de registro da tabela escolhida pelo usuário.
Os seus parâmetros dependem do que for passado via botões no diálogo principal, sendo:
REDEFINE BTNBMP ID 601 OF oPags:aDialogs[1] RESOURCE "bmp_plus" ... ACTION TabEDT( 1, oLbx1, "NOVO" )
REDEFINE BTNBMP ID 603 OF oPags:aDialogs[1] RESOURCE "bmp_editar" ... ACTION TabEDT( 1, oLbx1, "MOSTRA" )
Ou seja:
_num_ - É o número de pesquisa no DO...CASE
_lbx_ - o objeto oLbx? deve ser passado para que os métodos UpStable() e Refresh() sejam executados ainda dentro da função.
lAppend - Define o conteúdo das variáveis que serão manipuladas dentro do diálogo.
static function TabEDT( _num_, _lbx_, lAppend )
do case
case _num_ = 1
tblDocto( _num_, _lbx_, lAppend )
case _num_ = 2
tblSiglas( _num_, _lbx_, lAppend )
case _num_ = 3
tblMensal( _num_, _lbx_, lAppend )
endcase
return .t.
A função tblMensal, que é a mais completa faço uso de diversos controles e a forma de seus conteúdos devem ser analisadas.
A saida do diálogo sem acréscimo de registros na tabela devem estar desligado pela variável lSave:
local lSave := .f.
Dependendo do conteúdo de lAppend, as variáveis serão preenchidas de acordo
Aqui simulo um APPEND de TBrowse com registro em branco:
if lAppend ="NOVO"
goto bottom
skip
endif
Aqui dou um numero sequencial para o registro usando um arquivo de controle numérico:
if lAppend ="NOVO"
M->IDMENSAL := psqControle( dbf() )
endif
Aqui defino a variável usada pelo CHECKBOX como sendo lógica:
M->FIXA := IIF(MENSAL->FIXA='S',.t.,.f.)
Aqui ativo o diálogo de manutenção de mensalidades
DEFINE DIALOG oDlg RESOURCE "dlgTAMensal"
Aqui esta sendo redefinido um COMBOBOX com um vetor simples:
REDEFINE COMBOBOX oCbx1 VAR M->TIPO ITEMS { "D-DESPESA", "R-RECEITA" } ...
Aqui utilizo uma função modificada para abertura de COMBO com tabelas, pois a DBCOMBO não trabalha com tabelas muito grandes:
REDEFINE COMBO oCbx2 VAR M->IDCCUSTO ID 103 OF oDlg ;
RESOURCE "combo.bmp" ;
VALID .T. ;
ALIAS CENTROS ;
ORDER 1 ;
FIELD CENTROS->IDCCUSTO ;
HEADER STR(CENTROS->IDCCUSTO,3),CCUSTO ;
RESULT M->IDCCUSTO ;
UDF fun()
Sendo que:
ALIAS - Nome da tabela em que os dados serão pesquisados
ORDER - Ordem de indice nesta tabela em que o dado será pesquisado
FIELD - Campo que terá o conteúdo retornado dentro da variável em RESULT
HEADER - Colunas que serão apresentadas no listbox de pesquisa.
RESULT - Variável que contém o resultado da busca.
UDF - função do usuário que será executada após ter escolhido o resultado.
Notem que as funções de manutenção das tabelas seguem um padrão:
local lSave := .f.
//
// Aqui seleciono a tabela a ser manuseada
//
if lAppend ="NOVO"
goto bottom
skip
endif
//
// Aqui as variáveis serão preenchidas com dados ou zeradas
//
if lAppend ="NOVO"
//
// No caso de inclusão, se alguma variável necessitar de dado padrão.
//
endif
SET 3DLOOK ON
DEFINE DIALOG oDlg ...
//
// Aqui são colocados todos os REDEFINEs de controles para o diálogo
//
REDEFINE BUTTON ID 1 OF oDlg ACTION ( lSave := .t. , oDlg:End() )
REDEFINE BUTTON ID 2 OF oDlg ACTION ( lSave := .f. , oDlg:End() )
ACTIVATE DIALOG oDlg CENTERED
if lSave // Depende do resultado retornado pelo BUTTON acionado
//
// Aqui seleciono a tabela a ser manuseada
//
if lAppend ="NOVO"
APPEND BLANK
else
RLOCK()
endif
//
// Aqui salvo o conteúdo das variáveis em seus respectivos campos
//
COMMIT
_lbx_:UpStable() // Devem existir sempre
_lbx_:Refresh()
endif
return .t.
Por fim analisem o funcionamento da função psqControle, ela mantém em apenas um arquivo todos os numeros de controles de que necessitar.
Leiam também: viewtopic.php?t=874
@braços :?)