Skip to content

Tag-icone-mini.png 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.

UtilidadeNome técnicoComponente UtilizadoPaleta
Apresentar textos e identificação de campos.
Inclusive em textos com mais de uma linha.
labelTLabelStandard
Representar campo textoeditTwwDBEditIP Controls
Representar número com casas decimaiseditTwwDBEditIP Controls
Representar número inteirospinTwwDBSpinEditIP Controls
Representar uma data.
Abre calendário para usuário escolher.
datetimepickerTwwDBDateTimePickerIP Controls
Usado em filtros de data (inicial e final)filtrodatattcFiltroDataTotali
Representar campo lógicocheckboxTwwCheckBoxIP Controls
Mostra uma lista de opções para o usuário e vincula internamente a escolha a uma chavecomboboxTwwDBComboBoxIP Controls
Mostra uma lista de opções para o usuário e vincula a escolha do usuário com um registro de um DataSourcelookupcomboboxTwwDBLookUpComboBoxIP Controls
Representar um Texto longo.
Com duplo clique permite visualizar o texto completo em janela a parte.
richeditTwwDBRichEditIP Controls
Tabela de dadosgridTwwDBGridIP Controls
Barra de BotõesbotõesTToolBarWin32

Componentes para forms

UtilidadeNome técnicoComponente UtilizadoPaleta
Query (DataSet)queryTwwQueryIP Access

Componentes para datamodule com BDE

UtilidadeNome técnicoComponente UtilizadoPaleta
Query (DataSet)queryTUniQueryUniDAC

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);