Programação
Referenciado por: Análise de Interfaces | Como criar interface para nova coluna de configuração no Config | Como criar interface para nova coluna de domínio no Config | Como criar interface para nova coluna de filiais no Config | Componentes Devart | Dicas Técnicas | Documentação em Fontes de Delphi | Estrutura de Pastas | Filosofias de Programação | Predefinição:Guia de programação |
| Guia de programação | |
| Padrões Delphi | |
| Filosofias | |
| Interface Commerce | |
| Uso de componentes | |
| Mais sobre Interface | |
| Documentação em Fontes | |
| Estrutura de Pastas |
Esse artigo define a utilidade dos componentes visuais usados no Totali Commerce na programação em Delphi.
Tabela de Componentes
Estão descritos nessa tabela os componentes mais utilizamos nas soluções em Delphi da Totali.
| Utilidade | Nome técnico | Componente Utilizado | Paleta |
| Apresentar textos e identificação de campos. Inclusive em textos com mais de uma linha. | label | TLabel | Standard |
| Representar campo texto | edit | TwwDBEdit | IP Controls |
| Representar número com casas decimais | edit | TwwDBEdit | IP Controls |
| Representar número inteiro | spin | TwwDBSpinEdit | IP Controls |
| Representar uma data. Abre calendário para usuário escolher. | datetimepicker | TwwDBDateTimePicker | IP Controls |
| Usado em filtros de data (inicial e final) | filtrodata | ttcFiltroData | Totali |
| Representar campo lógico | checkbox | TwwCheckBox | IP Controls |
| Mostra uma lista de opções para o usuário e vincula internamente a escolha a uma chave | combobox | TwwDBComboBox | IP Controls |
| Mostra uma lista de opções para o usuário e vincula a escolha do usuário com um registro de um DataSource | lookupcombobox | TwwDBLookUpComboBox | IP Controls |
| Representar um Texto longo. Com duplo clique permite visualizar o texto completo em janela a parte. | richedit | TwwDBRichEdit | IP Controls |
| Tabela de dados | grid | TwwDBGrid | IP Controls |
| Barra de Botões | botões | TToolBar | Win32 |
Componentes para forms
| Utilidade | Nome técnico | Componente Utilizado | Paleta |
| Query (DataSet) | query | TwwQuery | IP Access |
Componentes para datamodule com BDE
| Utilidade | Nome técnico | Componente Utilizado | Paleta |
| Query (DataSet) | query | TUniQuery | UniDAC |
Componentes para datamodule sem BDE
Padrões Simples
TToolButton (e demais botões)
Por padrão utilizamos ActiveControls := nil no início do OnClick de qualquer botão.
Assim, garantimos que todos os eventos do grid vinculado a query foram executados.
TwwDBEdit
Utilizamos o padrão de nomenclatura DB<coluna> para componentes diretamente ligados a uma coluna da query.
Esse padrão se estende para demais componentes que fazem esse trabalho.
TwwDBComboBox e TwwDBLookUpComboBox
Normalmente, utiliza-se o combo com as seguintes propriedades:
AllowClearKey=True AutoDropDown=True Style=csDropDownList
Utilizamos DropDownWidth quando as opções do combo são textos maiores que o próprio combo.
Soluções Comentadas
Nessa parte do artigo, vamos fazer comentário em algumas soluções utilizadas na programação do Totali Commerce.
TwwDBGrid
Readonly básico
Se a tela possui um grid simples que terá algumas colunas sempre readonly, basta ao abrir a query, setar o readonly das colunas do grid.
O grid de fornecedores da tela de manutenção de produtos nos revela a resposta.
with gd_Fornecedor do begin For i := 0 to FieldCount-1 do if (Columns[i].FieldName = capslock('REFBAS')) or (Columns[i].FieldName = capslock('REFPRO')) or (Columns[i].FieldName = capslock('DESMAT')) or (Columns[i].FieldName = capslock('ITEDIV')) or (Columns[i].FieldName = capslock('ITESUB')) or (Columns[i].FieldName = capslock('ESPMAT')) or (Columns[i].FieldName = capslock('NOMCLI')) or (Columns[i].FieldName = capslock('ULTNOM')) or (Columns[i].FieldName = capslock('MARCA')) or (Columns[i].FieldName = capslock('ORICUS')) or (Columns[i].FieldName = capslock('DOCUME')) or (Columns[i].FieldName = capslock('DATA')) then Fields[i].ReadOnly := True else Fields[i].ReadOnly := False; Repaint; end;
Exemplo da tela de manutenção de produtos [TTFMANPRO.pas]
O Repaint utilizado na tela parece ser desnecessário.
Além disso, temos que banir o uso do with.
Readonly por linha
Se o readonly precisar variar de acordo com o conteúdo de uma coluna da query, é necessário mudar um pouco de estratégia, já que atribuir readonly a uma coluna do grid não vai resolver o problema.
Podemos verificar a solução no grid saldo da tela de manutenção de produtos.
1. ÿ feito o tratamento de readonly básico.
2. ÿ tratado no CalcCellColors para as colunas que mudam de acordo com o conteúdo terem suas cores alteradas por isso, e não por estarem readonly.
procedure TfrmManPro.GridSaldoCalcCellColors(Sender: TObject; Field: TField; State: TGridDrawState; Highlight: Boolean; AFont: TFont; ABrush: TBrush); begin if (Field = Field.DataSet.FieldByName('INI_CM')) or (Field = Field.DataSet.FieldByName('VLR_CM')) or (Field = Field.DataSet.FieldByName('QTD_CM')) or (Field = Field.DataSet.FieldByName('VLRCMG')) then begin if Field.DataSet.FieldByName('CODMUL').AsInteger <> 0 then begin ABrush.Color := clBtnFace; AFont.Color := clBlack; end; end else begin if Field.ReadOnly then begin ABrush.Color := ClBtnFace; AFont.Color := clBlack; end; end; end;
Exemplo da tela de manutenção de produtos [TTFMANPRO.pas]
3. Tratado no CellChanged para ficar readonly somente quando o usuário entrar nelas, e se for verificada a condição.
procedure TfrmManPro.GridSaldoCellChanged(Sender: TObject); begin with dmManPro.qrDin_Saldo do begin if (FieldByName('CODMUL').AsInteger <> 0) then begin FieldbyName('INI_CM').Readonly := True; FieldbyName('QTD_CM').Readonly := True; FieldbyName('VLR_CM').Readonly := True; FieldbyName('VLRCMG').Readonly := True; end else begin FieldbyName('INI_CM').Readonly := False; FieldbyName('QTD_CM').Readonly := False; FieldbyName('VLR_CM').Readonly := False; FieldbyName('VLRCMG').Readonly := False; end; end; end;
Exemplo da tela de manutenção de produtos [TTFMANPRO.pas]
Botão Replicar
Em alguns casos precisamos fazer um botão para replicar os dados de um determinado registro para os demais.
Esse botão funciona em uma coluna específica, e nem toda a coluna deve permitir replicação de dados.
Por isso, temos a necessidade de habilitar ou desabilitar esses botões.
Vamos entender a implementação da tela de promoções [TTFCDCPM.pas].
1. Incluir botão com DropDown desabilitado vinculado a um TPopupMenu.
2. Normalmente, é incluído um checkbox no grid para o usuário poder selecionar múltiplos registros para replicar.
3. O evento OnPopup fica responsável em verificar se pode ou não replicar a coluna.
procedure TfrmCadCpm.pm_ReplicarPopup(Sender: TObject); var coluna: String; oGrid: TwwDbGrid; begin oGrid := gdPro; if (oGrid.Columns[oGrid.GetActiveCol-1].FieldName = CapsLock('PRECOP')) and(not oGrid.Columns[oGrid.GetActiveCol-1].ReadOnly) then begin coluna := StringReplace(oGrid.Columns[oGrid.GetActiveCol-1].DisplayLabel,'~',' ',[rfReplaceAll]); mi_ReplicarParaTodos.Caption := 'Replicar ** + coluna + ** para todos'; mi_ReplicarParaMarcados.Caption := 'Replicar ** + coluna + ** para marcados'; bt_Replicar.Enabled := True; end else begin bt_Replicar.Enabled := False; end; end;
Exemplo da tela de promoções [TTFCDCPM.pas]
4. O método ReplicarPopup é chamado em diversos eventos para garantir que desabilite ou habilite corretamente o botão replicar.
- Evento OnKeyDown do Form;
- Evento OnColEnter do Grid;
- Evento OnMouseDown do Grid.
5. No método que replica efetivamente, deve se tomar os seguintes cuidados:
- Verificar se o dado é replicável;
- Guardar posição atual no grid, para fazer todo o processo e deixar na posição que estava;
- Tratar os eventos do grids de maneira manual, já que o processo será feito desabilitando os controles do grid.
procedure TfrmCadCpm.bt_ReplicarClick(Sender: TObject); var CampoAtivo : String; VarString : String; VarFloat : Double; VarDateTime : TDateTime; VarBoolean : Boolean; Marca : TBookmark; oQuery : TwwQuery; oGrid : TwwDbGrid; begin ActiveControl := nil; oQuery := dmCadCpm.qrPro; oGrid := gdPro; CampoAtivo := UpperCase(oGrid.GetActiveField.FieldName); case oGrid.GetActiveField.DataType of ftFloat : VarFloat := oGrid.GetActiveField.AsFloat; ftString : VarString := oGrid.GetActiveField.AsString; ftDateTime : VarDateTime := oGrid.GetActiveField.AsDateTime; ftBoolean : VarBoolean := oGrid.GetActiveField.AsBoolean; else begin Mensagem('Este tipo de dado não pode ser replicado','Aviso',1); Exit; end; end; oQuery.DisableControls; Marca := oQuery.GetBookmark; oQuery.First; while not oQuery.Eof do begin if (TComponent(Sender).Name = 'mi_ReplicarParaTodos') or ((TComponent(Sender).Name <> 'mi_ReplicarParaTodos') and (oQuery.FieldByName('VIRTUAL').AsString = 'T')) then begin oQuery.Edit; case oGrid.GetActiveField.DataType of ftFloat : oQuery.FieldByName(CampoAtivo).AsFloat := VarFloat; ftString : oQuery.FieldByName(CampoAtivo).AsString := VarString; ftDateTime : oQuery.FieldByName(CampoAtivo).AsDateTime := VarDateTime; ftBoolean : oQuery.FieldByName(CampoAtivo).AsBoolean := VarBoolean; end; end; oQuery.Next; end; oQuery.GotoBookmark(Marca); oQuery.FreeBookmark(Marca); oGrid.RefreshDisplay; oQuery.EnableControls; if oGrid.CanFocus then oGrid.SetFocus; end;
Exemplo da tela de promoções [TTFCDCPM.pas]
TwwDBLookUpComboBox
Vamos entender a implementação do LookUpComboBox de contas financeiras na tela de compromissos [TTFCDTIP.pas].
1. Foi criado a query para carregar as opções a serem apresentadas no combo.
2. Foi incluído o lookupcombobox na tela.
3. Foi feito no componente o vínculo com a query, e foi escolhida a coluna TITCTA para aparecer no combo.
LookupField=TITCTA LookupTable=dmTituloAPagar.CTA
4. Para o caso de edição do compromisso, foi necessário vincular no DataField do combo à coluna do registro da fatura.
DataField=TITCTA DataSource=dmTituloAPagar.SrcFatura
5. Combo foi definido para não permitir escolha fora das opções que são apresentadas.
Style = csDropDownList DropDownWidth = 300 AutoDropDown = True AllowClearKey = True
6. No método OnChange do combo os campos que compõe a chave são passados da query do Lookup para a query em que serão gravados.
If CTA.Active = false then exit; If cmbCTA.Text = * then exit;*
qrRateio.ParamByName('CODFIL').AsString := CTA.FieldByName('CODFIL').AsString; qrRateio.ParamByName('SEQUEN').AsString := CTA.FieldByName('SEQUEN').AsString;
7. A query CTA é aberta no OnShow e fichada no OnClose do Form.
TwwDBComboBox
Combo de múltipla escolha
Para criar um combo de múltipla escolha, como o que tem a tela de promoções [TTFCDCPM.pas] para escolha de planos de pagamento, é necessário seguir os seguintes passos.
1. Colocar o combo e os 3 botões de escolha.
As propriedades que a que devemos atentar são:
Style = csOwnerDrawFixed AllowClearKey = False AutoDropDown = True AutoSelect = False
2. Criar variáveis globais para controlar a seleção.
CHECKCOMBO_aItems : array of Boolean; CHECKCOMBO_bMudou : Boolean; CHECKCOMBO_Sel : String;
3. Tratar controle de mudança no método OnChange do combo.
CHECKCOMBO_bMudou := True;
4. Atualizar lista dos selecionados no OnCloseup do combo.
var count : Integer; begin if CHECKCOMBO_bMudou then begin CHECKCOMBO_Sel := EmptyStr; CHECKCOMBO_aItems[cm_MultiploPlanos.ItemIndex] := not CHECKCOMBO_aItems[cm_MultiploPlanos.ItemIndex]; Count := VerificaMultiploPlanos; // Traz a contagem de planos seleciondos if Count > 0 then begin // Mostra a quantidade que foi selecionada CHECKCOMBO_Sel := IntToStr(count) + ' plano(s) selecionado(s)'; end else begin CHECKCOMBO_Sel := ; end; CHECKCOMBO_bMudou := False; cm_MultiploPlanos.ItemIndex := -1; end; end;
5. No OnDrawItem do combo, tratar para aumentar (ou diminuir) o tamanho da lista dos selecionados de acordo com a quantidade do combo; e também desenhar o quadro que permite a seleção.
SetLength(CHECKCOMBO_aItems, TwwDBComboBox(Control).Items.Count); if Index >= 0 then begin with TwwDBComboBox(Control).Canvas do begin FillRect(rect); Rect.Left := Rect.Left + 1; Rect.Right := Rect.Left + 13; Rect.Bottom:= Rect.Bottom; Rect.Top := Rect.Top; if not (odSelected in State) and (CHECKCOMBO_aItems[Index]) then DrawFrameControl(Handle, Rect, DFC_BUTTON, DFCS_BUTTONCHECK or DFCS_CHECKED or DFCS_FLAT) else if (odFocused in State) and (CHECKCOMBO_aItems[Index]) then DrawFrameControl(Handle, Rect, DFC_BUTTON, DFCS_BUTTONCHECK or DFCS_CHECKED or DFCS_FLAT) else if (CHECKCOMBO_aItems[Index]) then DrawFrameControl(Handle, Rect, DFC_BUTTON, DFCS_BUTTONCHECK or DFCS_CHECKED or DFCS_FLAT) else if not (CHECKCOMBO_aItems[Index]) then DrawFrameControl(Handle, Rect, DFC_BUTTON, DFCS_BUTTONCHECK or DFCS_FLAT); TextOut(Rect.Left + 15, Rect.Top, TwwDBComboBox(Control).Items[Index]); end; end else begin with TwwDBComboBox(Control).Canvas do TextOut(Rect.Left,Rect.Top,CHECKCOMBO_Sel); end;
6. ÿ comum que se queira gravar o que foi escolhida na múltipla seleção no .ini.
Para isso, foram implementados os métodos CarregaListaPlanos, executado no OnShow.
E o método GravaListaPlanos, executado no OnClick do Pesquisar e do Novo.
7. O método ListaPlano, enfim, monta os planos na query.
function TfrmPromocoes.ListaPlanos():String; // Igor - 64229 var // Monta a lista de planos na query cListaPlanos : String; i : integer; begin cListaPlanos := ; for i := 0 to cm_MultiploPlanos.Items.Count-1 do begin if CHECKCOMBO_aItems[i] then begin if cListaPlanos<> then cListaPlanos := cListaPlanos +','; cListaPlanos := cListaPlanos + ' + cm_MultiploPlanos.Items[i] + '; end; end; result := cListaPlanos; end;
Query ficou assim:
' AND PLA.APEPGT in (%s) ',Filtro18, [ListaPlanos()]);
TwwQuery
Filter
O conceito de filter permite apresentar um número reduzidos de registros de uma query.
ÿ bastante usado em queries vinculadas a grid dependentes ou para determinados filtros feitos por usuários.
Vamos observar o exemplo da tela de pesquisa de notas recebidas [TTFLCCOM.pas].
Basicamente foi setada duas propriedades
Filter := '(FLGEST <> '#39'D'#39') and (SEQFRE is NULL)';
ÿ a regra que o filtro respeitará. As colunas precisam estar na query.
Filtered := not chkMostraTodos.Checked;
Indica se é para efetivamente filtrar.
O filtro ativa os eventos do grid.
Por isso, é necessário desabilitar os controles também.
DisableControls
O grid normalmente está vinculado a uma query.
Quando o usuário interage com o grid editando, clicando, etc. é necessário que o componente grid se preocupe com colocar a query em edição, dar next, e dar post.
Entretanto, em alguns momentos, o programar precisa implementar um evento do grid que faz alguma dessas operações.
Nesse momento, para que a programação fique lógica, e para que eventos do grid não fiquem executando em paralelo a programação do evento (que também edit, dá next e post na query), é necessário desabilitar os controles.
Para desabilitar os controles utilizamos o DisableControls.
Posteriormente, temos que reabilitar os controles.
OnRowChange dmDataModule.qr_query.DisableControls; dmDataModule.qr_query.EnableControls;
Porém, desabilitar os controles não evita que os eventos sejam disparados.
Imaginando que na implementação do OnRowChange exista uma alteração de valor na query.
Isso vai disparar o evento OnFieldChange.
Se no evento OnFieldChange houver outra rotina para desabilitar controles, haverá o problema do componente habilitar os controles prematuramente.
OnRowChange dmDataModule.qr_query.DisableControls; dmDataModule.qr_query.Campo := Valor; OnFieldChange dmDataModule.qr_query.DisableControls; dmDataModule.qr_query.EnableControls; dmDataModule.qr_query.EnableControls;
Para evitar isso, devemos utilizar a propriedade ControlsDisabled.
ÿ necessário utilizá-lo também ao desabilitar, porque o DisableControls funciona como uma pilha.
Ao desabilitar duas vezes, é necessário habilitar duas vezes.
Então no método, ficaria assim.
OnRowChange try ControlsDisabled := dmDataModule.qr_query.ControlsDisabled; if not ControlsDisabled then begin dmDataModule.qr_query.DisableControls; end; dmDataModule.qr_query.Campo := Valor; OnFieldChange try ControlsDisabled := dmDataModule.qr_query.ControlsDisabled; if not ControlsDisabled then begin dmDataModule.qr_query.DisableControls; end; finally if not ControlsDisabled then begin dmDataModule.qr_query.EnableControls; end; end; finally if not ControlsDisabled then begin dmDataModule.qr_query.EnableControls; end; end;
Bookmark
Serve para guardar a posição atual da query.
Normalmente usada quando precisamos percorrer todos os registros de uma query que está vinculada a um grid.
1. Utilizar unit DB.
uses DB
2. Declarar variável do objeto responsável.
Lock: TBookMark;
3. Guardar posição atual.
Tanto faz se é antes ou depois de desabilitar os controles.
oDataModule.Item.DisableControls; Lock := oDataModule.Item.GetBookMark;
4. Reposicionar query no registro guardado e liberar objeto da memória.
Deve ser ante de habilitar os controles porque dispara eventos do grid.
GotoBookMark(Lock); FreeBookMark(Lock); oDataModule.Item.EnableControls;
TPanel
DesabilitePanel
Existe uma função na unit TTUFuncoesTela chamada DesabilitePanel.
Essa função é muito útil para desabilitar áreas inteiras do form, não precisando se preocupar com cada componente.
1. Colocar no uses TTUFuncoesTela.
2. Chamar função passando o Panel (ou algum outro objeto de grupo) e se é para habilitar ou desabilitar.
// Objetivo: Dasabilitar os componentes dentro de um container // Parametros | Objeto => Panel que contem os componentes a serem desabilitados // | Abilite => Indica se deve habilitar(True) ou desabilitar(False) // | BooRecursiva => Controle interno da função, Nunca deve ser usado para chamar a função // Obs: | Nunca se deve mandar habilitar antes de desabilitar // | Não feche o form com os componentes desabilitados, reahabilite-os antes de fechar // | A função trabalha com até 15 paineis simultaneamente, para ampliar o limite aumente o tamanho dos vetores // Cristian
procedure DesabilitePanel(Objeto: TWinControl; Abilite: Boolean; BooRecursiva: Boolean = False);