News & Events
Tutorial: Criando meu app step-by-step Parte IV
Chegamos a Parte IV de nosso Tutorial: Criando meu app step-by-step em RAD Studio XE7. No capítulo anterior, mais curto que os demais, aprendemos a montar nossa tela de Listagem de Títulos conectada em dados reais através de um banco de dados SQLite. Nós fizemos a navegação, vinculamos o banco ao projeto e deixamos tudo preparado para iniciarmos a parte de Inclusão, Exclusão e Alteração de títulos. Confira abaixo:
- Criação do DataModule principal;
- Configuração dos principais componentes de acesso ao banco de dados;
- Criação dos métodos de listagem de Titulos e Detalhes dos Titulos;
- Montagem das telas funcionais.
Nessa nova etapa veremos como implementar o CRUD (Create, Read, Update e Delete, ou seja, Inserção, Leitura, Atualização e Exclusão) em nosso aplicativo. Vejamos a pauta:
- Criação de campo do tipo imagem na tabela de Títulos;
- Criação de método para auto-numerar ID de Títulos;
- Binding da imagem com as listagem e detalhes do Título;
- Inclusão, Alteração e Exclusão de Títulos;
- Seleção de fotos da biblioteca ou câmera;
Requisitos
- Leitura: Tutorial: Criando meu app step-by-step parte III;
- Assistir ao vídeo: TDevRocks – Binding vs TListViews
Retomando o raciocínio
Nossa sessão Retomando o raciocínio de hoje tem a ver com o que fizemos até o presente momento. No capítulo anterior nós iniciamos, como dito anteriormente, com uma parte crucial: inclusão de dados reais. Entretanto deixamos para trás duas coisas:
- Auto-numeração do ID de Títulos;
- Criação do campo Foto;
A foto será usada para mostrar a capa do título, DVD, em questão. Tanto no TListView quanto em detalhes. E no caso do ID do título, como vamos ter outros numeradores, vamos criar uma função no DataModule que receba a tabela e o nome do campo e devolvemos já um Integer com o ID, assim poderemos reutilizar a function em outras tabelas quando necessário.
Alterando o banco de dados
Vamos começar abrindo o SQLite Expert e alterando nossa tabela TITULOS. Clicando em TITULOS acesse a aba Design e em seguida clique em ADD na parte inferior para adicionarmos um novo campo. Digite FOTO no campo Name e IMAGE no Type para escolhermos o tipo de dado (Figura 1).
Figura 1. Criando o campo FOTO
Feito isso, teremos que alterar a instrução SQL que traz os campos. Por isso, retorne ao Delphi e acesse o DataModule (DM) para que possamos modificar o componente qryTitulos. Selecione a propriedade SQL e clique duas vezes nela e inclua o campo FOTO na instrução assim como na Listagem 1.
SELECT ID_TITULO, TITULO, SUBTITULO, ANO_LANCAMENTO, FOTO FROM TITULOS
Listagem 1. Select alterada de TITULOs
Agora precisamos adicionar o novo campo a lista de campos Fields Editor do qryTitulos. Para isso clique com o botão direito do mouse do componente e selecione Fields Editor. Com o direito na pequena janela que aparece, clique em Add Fields. Isso fará com que o campo FOTO apareça.
#Dica: Certifique-se de que abriu o banco Locadora.sqlite correto no SQLite Expert e que o banco apontado na propriedade Database no FDConnection também esteja apontado para o banco que sofreu a alteração, do contrário nosso campo não aparecerá na janela.
Muito bom até aqui, precisamos somente fazer o binding do novo campo com os componentes de imagem da tela de títulos frmTitulos.
Binding de Fotos
Há quem prefira salvar as fotos em uma pasta específica. Também prefiro, mas depende muito do tipo de aplicação. Nesse caso prefiro salvar direto no banco, já que seria mais chato fazer o binding com as fotos soltas no device. Esse processo é tão simples quanto o que fizemos no capítulo anterior.
Acesse a janela frmTitulos e em seguida acione o Bind Visually. Selecione o TListView correspondente a lista de títulos e observe que ele possui um item chamado Item.Bitmap em sua lista de campos na janela Binding. Ligue o campo FOTO a esse item. Do mesmo modo, mude para a aba de Detalhes e ligue o campo FOTO a propriedade Bitmap do TImage que colocamos para receber a foto.
#Dica: Caso não apareça a propriedade Bitmap em qualquer dos casos, clique nas reticências do objeto que deseja fazer a ligação do Binding, e localize a propriedade desejada na caixa de diálogo. Marque-a e então ela aparecerá no bind.
Pronto! A primeira parte do binding está finalizada. O que precisamos fazer agora é preparar a parte de codificação para que possamos possibilitar mudar a foto do item na lista. Veja as ligações dessa sessão na Figura 2.
Figura 2. Binding de fotos
Errata da Parte II
Na Parte II nós fizemos a codificação para vincular nosso banco de dados em runtime no evento OnCreate do DataModule, mas acabei publicando um erro na utilização de diretivas de compilação que só notei com o início dos testes nesse capítulo. No OnCreate utilizamos a seguinte linha:
[...] {$IF DEFINED (IOS) || (ANDROID)} [...]
Quando na verdade o correto seria:
[...] {$IF DEFINED(IOS) OR DEFINED(ANDROID)} [...]
Alguns leitores também reclamaram que ao passar pela linha Connected := True, o banco dados não abre e ainda indica um erro na aplicação, que está relacionado a uma configuração não passada no capítulo III. Clicando duas vezes do componente FDConnection (fdConexao), repare que em nossas configurações existe uma propriedade chamada OpenMode que está setada como CreateUTF8. Troque-a para ReadWrite. Isso se faz necessário, pois estamos distribuindo o banco de dados SQLite e não criando-o em runtime.
Em outras palavras, já temos o banco de dados criado e faremos a distribuição dele quando fecharmos a aplicação para a loja ou para nossos testes no device. Para que não fique totalmente confuso, observe a Listagem 2 que contém o código completo corrigido.
procedure TDM.DataModuleCreate(Sender: TObject); var sPath: String; begin with fdcConexao do begin {$IF DEFINED(IOS) OR DEFINED(ANDROID)} Params.Values['DriverID'] := 'SQLite'; Params.Values['OpenMode'] := 'ReadWrite'; try sPath := TPath.Combine(TPath.GetDocumentsPath, 'Locadora.sqlite'); Params.Values['Database'] := TPath.Combine(TPath.GetDocumentsPath, 'Locadora.sqlite'); Connected := True; except on E: Exception do begin ShowMessage(E.Message); end; end; {$ELSE} try Params.Values['Database'] := 'CAMINHO_DO_BANCO_DE_DADOS\Database\Locadora.sqlite'; Connected := True; except on E: Exception do begin ShowMessage(E.Message); end; end; {$ENDIF} end; end;
Listagem 2. Código do evento OnCreate do DataModule corrigido
Fonte de estudo clique aqui.
Editando um registro
Há inúmeras maneiras de fazemos a edição dos dados em tela, portanto faremos a princípio na maneira mais simples e objetiva possível. Vá até a aba Detalhes do Título e adicione três novos speedbuttons. Eles farão o trabalho de Editar, Salvar e Cancelar uma edição. Configure-os como a seguir:
- Name: spbEditar
- StyleLookup:
- Align: Right
- Margins.Right: 8px
- Text: Editar
- Visible: False
- Name: spbSalvar
- StyleLoockup: donetoolbutton
- Align: Right
- Margins.Right: 8px
- Text: Salvar
- Visible: False
- Name: spbCancelar
- StyleLookup: deletetoolbutton
- Align: Left
- Margins.Left: 8px
- Text: Cancelar
- Visible: False
Note que todos os nossos botões possuem a propriedade Visible segada como False. Isso porque vamos deixa-los como visíveis de acordo com a situação. Criaremos um método para fazer esse trabalho. Por isso crie uma nova procedure na seção Public do formulário e codifique como na Listagem 3.
procedure TfrmTitulos.AtualizarBotoes; begin spbVoltar.Visible := (DM.qryTitulos.State = dsBrowse); spbEdicao.Visible := (DM.qryTitulos.State = dsBrowse); spbCancelar.Visible := (DM.qryTitulos.State in dsEditModes); spbSalvar.Visible := (DM.qryTitulos.State in dsEditModes); end;
Listagem 3. Ativa e Desativa botões
Muito bom, agora precisamos apenas fazer as devidas chamadas ao método AtualizarBotoes. A princípio podemos colocar nos eventos OnCreate do formulário de Títulos e também no OnItemClick do ListView de listagem de títulos, assim quando o usuário tocar em um título para ver os detalhes, os botões ficarão disponíveis para a edição. No evento OnClick de cada botão novo teremos que codificar para fazer a edição do registro selecionado. O código é bem simples. Veja a Listagem4 com toda a seqüência de código de cada botão.
procedure TfrmTitulos.spbCancelarClick(Sender: TObject); begin DM.qryTitulos.Cancel; AtualizarBotoes; end; procedure TfrmTitulos.spbEdicaoClick(Sender: TObject); begin DM.qryTitulos.Edit; AtualizarBotoes; end; procedure TfrmTitulos.spbSalvarClick(Sender: TObject); begin DM.qryTitulos.Post; AtualizarBotoes; end;
Listagem 4. Código dos botões de edição
Para quem é mais experiente, esse código realmente é simples ao extremo. Claro que poderíamos fazer uma série de checks para evitar que o usuário deixe de preencher um determinado campo, tratamentos de erro, etc. Mas por enquanto faremos o mais grosso da programação. Com isso codificado, já podemos testar nossa aplicação.
Ao tocar no botão Editar, os botões Salvar e Cancelar devem aparecer e o botões Editar e Voltar devem sumir, assim o usuário somente poderá Salvar ou Cancelar a ação.
Para tirarmos uma foto ou escolhermos da biblioteca, usaremos ações do TActionList. Por isso adicione ao formulário um TActionList, dê o nome de AcoesGeral e clique duas vezes nele. Na janelinha que se abre clique na setinha preta que aparece ao lado botão de criação de novas ações (Figura 3 e 4).
Figura 3. Criando nova ação
Figura 4. Criando nova ação
Fiz o print das telas propositadamente com o cursor selecionado na opção Media Library. Essas são ações prontas do Delphi. Na seqüência vemos:
- TTakePhotoFromLibraryAction
- TTakePhotoFromCameraAction
- TShowShareSheetAction
As duas primeiras são usadas respectivamente para selecionar uma foto da Galeria (Android) ou Biblioteca (iOS) e a segunda é usada para tirar uma novo nova. A terceira opção é usada para invocar a tela de compartilhamento nas redes sociais, etc.
Adicione uma ação para Câmera e outra para Biblioteca. Chame-as de actFotoCamera e actFotoBiblioteca. Depois de criadas nós podemos clicar na ação e acessar suas propriedades e eventos. O que precisamos aqui é só do evento OnDidFinishTaking, que será codificado por nós. Clique duas vezes nesse evento para a ação actFotoCamera.
Esse evento ocorre logo depois da captura da foto, ou seja, após o usuário tirar a foto e escolher usa-la. Nós carregaremos a foto diretamente para o campo FOTO em nosso banco de dados. Para isso apenas digite no evento:
DM.qryTitulosFOTO.Assign(Image);
O parâmetro Image vem do próprio evento e é carregado automaticamente pelo Delphi após a captura de foto. Essa mesma linha pode ser digitada no mesmo evento para a segunda ação, faça isso.
Se você escreveu os código corretamente para cada botão como indicado mais acima, então até aqui não teremos problemas e tudo estará funcionando perfeitamente.
Retornaremos em breve aqui, pois teremos que codificar o momento certo de chamar esses métodos.
Gerando um novo registro
No início no post mencionei que iríamos montar uma função para gerar automaticamente nossos IDs, e é isso que faremos agora. Algo relativamente simples e que nos ajudará com o reaproveitamento de código. Acesse nosso DataModule e crie uma nova function na seção Public no formulário.
function GetIDFromTable(ATable: String) : Integer;
Pressione Ctrl + Shift + C para que o Delphi crie o escopo no método e então digite o código da Listagem 5. Perceba que criamos uma constante chamada _SQL que recebe ‘SELECT MAX(ROWID)+1 AS NEW_ID FROM %s’. Isso é bem simples.
RowID é um campo oculto nas tabelas do SQLite que é bastante comum em alguns bancos de dados, tal como Oracle. Ele é uma espécie de controle interno do banco de dados. O que faremos é igualar o campo ID_TITULO de nossa tabela ao valor desse campo. Usamos Max para pegar o maior valor nesse campo e adicionamos +1. O ‘%s’ é um ‘coringa’ que usaremos para substitui-lo pelo nome da tabela, ou seja, teremos uma instrução SQL assim:
SELECT MAX(ROWID)+1 AS NEW_ID FROM TITULOS
Resultaremos o valor do campo NEW_ID.
function TDM.GetIDFromTable(ATable: String): Integer; const _SQL = 'SELECT MAX(ROWID)+1 AS NEW_ID FROM %s'; begin with qryAuxiliar1 do begin Active := False; SQL.Clear; SQL.Text := Format(_SQL, [ATable]); Active := True; Result := FieldByName('NEW_ID').AsInteger; end; end;
Listagem 5. Gerando ID da tabela
Também peço que altera a instrução SQL da query qryTitulos incluindo o campo ROWID no início da select e também incluindo seu campo no Fields Editor. Consulte a parte II desse tutorial para ver como faz caso tenha dificuldades. Por fim adicione um novo componente query a tela chamando-o de qryAuxiliar1. Todas instruções SQL que precisarmos fazer e que sejam temporárias, usaremos esse componente.
Agora retorne ao frmTitulos e acessa a primeira aba, a de listagem dos Títulos. Crie um novo speedbutton na tela dentro de nossa barra de ferramentas superior. Alinhe esse botão a direita com Margem Direita igual a 8px. Selecione o StyleLoookup addtoolbutton para termos um botão de adição de novo título.
Esse botão deverá fazer algo bem simples.
- Chamar o método de atualização dos botões em tela;
- Adicionar um novo registro a tabela Títulos;
- Chamar a Aba de Detalhes.
A aba de detalhes deverá receber uma alteração grande agora, pois não temos onde digitar os valores Título, SubTítulo e Ano de Lançamento, pois colocamos apenas Labels nessa tela. Portanto faremos a troca desses labels por Edits. Troque os três labels por edits, na seqüência: edtTitulo, edtSubTitulo e edtAnoLancamento. Refaça o Binding, você ligará novamente cada campo da tabela aos respectivos edits. Você já sabe fazer isso, basta consultar os demais tutoriais ou o vídeo sobre Binding disponível em nosso YouTube.
Veja como deve ficar seu Layout nas Figuras 5, 6 e 7.
Figura 5. Exemplo de Tela
Figura 6. Exemplo de Tela
Figura 7. Exemplo de Tela
A mágica é simples. Quando entrarmos em modo de Edição ou Inserção, mudaremos a propriedade Enabled de cada controle para aceitar nossas digitação. Quando estiver em modo Browse, ou seja, apenas visualização, os edits ficaram desativados. Mas além disso mudaremos também o StyleLookup de cada controle para que fiquem transparentes, dando a impressão de Labels.
Retorne a nossa função AtualizarBotoes e acrescente o código da Listagem 6.
edtTitulo.Enabled := (DM.qryTitulos.State in dsEditModes); edtSubTitulo.Enabled := (DM.qryTitulos.State in dsEditModes); edtAno.Enabled := (DM.qryTitulos.State in dsEditModes); if (DM.qryTitulos.State in dsEditModes) then begin edtTitulo.StyleLookup := 'editstyle'; edtSubTitulo.StyleLookup := 'editstyle'; edtAno.StyleLookup := 'editstyle'; end else begin edtTitulo.StyleLookup := 'transparentedit'; edtSubTitulo.StyleLookup := 'transparentedit'; edtAno.StyleLookup := 'transparentedit'; end;
Listagem 6. Complemento do código
Repare que igualamos a propriedade Enabled ao estado da tabela. E ainda modificamos o StyleLookup dos componentes. Pronto, precisamos apenas fazer a chamada do código para inclusão de títulos.
Retorne a aba principal e clique duas vezes em nosso botão ‘+’. Digite o código a seguir:
DM.qryTitulos.Append; tabctrlTitulos.Next; AtualizarBotoes;
Não dá para ser mais simples que isso. Append para criar um novo registro, mudamos para a aba de detalhes e atualizamos os botões que consequentemente também ativará os edits para inclusão de dados.
Criando um pop-up menu
Nós preparamos nosso código para ativarmos a câmera ou a biblioteca de fotos, mas não fizemos as devidas chamadas ainda. Faremos o seguinte: quando o usuário tocar na foto na tela de detalhes, então acionaremos um pop-up menu e perguntaremos o que o usuário deseja fazer:
- Nova Foto;
- Biblioteca
- Cancelar
Desse modo, cada item do pop-up chamará uma determinada ação. Para fazer esse menu usaremos um artifício bem comum, um ListBox. Isso mesmo! Insira um ListBox na tela e clique duas vezes nele para adicionar itens. Crie três itens e altere suas propriedades como a seguir:
- Name: lstitNovaFoto
- Text: Nova Foto
- Name: lstitBiblioteca
- Text: Biblioteca
- Name: lstCancelar
- Text: Cancelar
Eu modifiquei as fontes de cada item do listbox, mas fica a seu critério. Também é importante modificar a altura de todos os itens, use a propriedade ItemHeight do próprio ListBox e altere-a para 40 que é um bom número. A aparência desse listbox deve ser parecida como a Figura 8.
Figura 8. Popup menu
Arraste esse ListBox para o Layout do frmTitulos. Ele deve ficar como na Figura 9.
Figura 9. Estrutura de componentes.
Para que ele fique com uma sombra em volta, localize o componente TShadowEffect e arraste-o para o listbox, como na Figura 10.
Figura 10. Sombra
Perfeito! Agora temos que preparar a logística para que tudo isso funcione. Nós faremos o menu aparecer de cima para baixo com uma animação, e desaparecerá do mesmo modo. Para que possamos sumir com o menu quando o usuário tocar fora, usaremos nossa antiga técnica de criar um retângulo escuro por baixo de tudo. Nós vimos isso no primeiro tutorial. Voltaremos com a ideia.
Vá até o formulário principal, frmMain, e adicione um TRectangle de qualquer tamanho e modifique sua propriedade Opacity para 0.5. Na seção Public do form, crie dois métodos:
procedure ShowBackground(AParent: TFmxObject; AOnClick: TNotifyEvent = nil); procedure HideBackground;
E codifique-os como na Listagem 7.
procedure TfrmMain.HideBackground; begin recBackground.AnimateFloat('opacity', 0, 0.1); recBackground.Visible := False; end; procedure TfrmMain.ShowBackground(AParent: TFmxObject; AOnClick: TNotifyEvent); begin recBackground.OnClick := AOnClick; recBackground.Parent := AParent; recBackground.BringToFront; recBackground.Opacity := 0; recBackground.Visible := True; recBackground.AnimateFloat('opacity', 0.5, 0.1); end;
Listagem 7 Background em principal
O procedimento é exatamente igual ao que fizemos na primeira parte do tutorial, portanto não explicarei novamente. Apenas para entendermos, nós chamaremos ShowBackground sempre que precisarmos mostrar o fundo escuro e HideBackground para sumir como ele.
Já na tela frmTitulos nós criaremos um método para sumir com o pop-up, então crie um novo método com o nome HidePopup e digite o código a seguir:
frmMain.HideBackground; lstPopUpFoto.AnimateFloat('position.y', lstPopUpFoto.Height * -1); lstPopUpFoto.Visible := False;
Simples não? Chamamos o método HideBackground presente no formulário principal e depois as animações para esconder o menu. Por default o menu deve ficar invisível e longe do alcance dos olhos, portanto no OnCreate do formulário de títulos adicione o código da Listagem 8.
lstPopUpFoto.Width := ClientWidth - 20; lstPopUpFoto.Position.X := (ClientWidth - lstPopUpFoto.Width) / 2; lstPopUpFoto.Position.Y := lstPopUpFoto.Height * -1; lstPopUpFoto.Visible := False; AtualizarBotoes;
Listagem 8. OnCraete do formulário
O que estamos fazendo no método é, em resumo, aumentar sua largura automaticamente para que fique com um tamanho mais adequado a largura do device e depois posicionamos ele acima da barra de título do aparelho e deixando-o invisível.
Acesse agora a aba detalhes e clique duas vezes no nosso imgFoto para adicionar um novo código ao evento OnClick do objeto. Nós chamaremos agora o menu pop-up. Digite o código da Listagem 9.
procedure TfrmTitulos.imgFotoClick(Sender: TObject); begin if DM.qryTitulos.State in dsEditModes then begin frmMain.ShowBackground(Self.lytTitulos, lstitCancelarClick); lstPopUpFoto.Visible := True; lstPopUpFoto.AnimateFloat('position.y', 0); lstPopUpFoto.BringToFront; end; end;
Listagem 9. Chamada para o popup
Na Listagem 9 fazemos primeiro uma checarem para ver se o usuário colocou o registro em alteração, evitando um erro na tela. Em seguida mostramos o fundo escuro e descemos o popup usando o método AnimateFloat passando a nova posição vertical dele, position.y.
Feito. Somente precisamos agora codificar a ação de cada item do ListBox. No OnClick do primeiro item digite:
HidePopup; actFotoCamera.ExecuteTarget(Sender);
No segundo repita e mude apenas a chamada para a biblioteca.
HidePopup; actBiblioteca.ExecuteTarget(Sender);
E caso o usuário queira desistir de tirar foto, apenas chame HidePopup e HideBackground para sumir com o popup de tela.
HidePopup; frmMain.HideBackground;
Excluindo registros
Para fecharmos com chave de ouro, precisaremos fazer a exclusão de registros e o PullToRefresh, ou seja, puxe para atualizar. Usaremos gestos para isso, mas os defaults. Quando o usuário arrastar o dedo para os lados, um botão Delete deverá aparecer. Se puxar de cima para baixo, então atualizamos a tabela. Isso tudo no ListView.
Bem, o PullToRefresh já está pronto. Apenas ative a propriedade PullToRefresh e então codifique o evento OnPullToRefresh. Digite apenas:
DM.qryTitulos.Active := False; DM.qryTitulos.Active := True;
Ou seja, apenas fechamos e abrimos a query.
Para excluir basta ativar a propriedade CanSwipeDelete e codificar o evento OnDeletingItem. Nesse caso vamos a explicação.
Esse evento ocorre no momento que o usuário clica no botão para exclusão do item e o mesmo entra em modo de exclusão definitiva. Nós temos uma variável nesse evento chamada ACanDelete. Se não codificarmos nada nesse evento, o item será apagado do ListView, mas não do banco de dados, o que acabará voltando se o usuário der um PullToRefresh ou fechar e reabrir o app. Por isso excluiremos também do banco.
Levando em consideração que não sabemos exatamente qual ID do Título estamos excluindo, usaremos o INDEX da linha excluída como base. Sabemos que o ListView inicia sua contagem em 0 (zero), somaremos +1 para obtermos o ROWID exato do registro a ser excluído do banco. Portanto faremos um Locate na tabela para pararmos em cima do registro e apagaremos. Digite então o código da Listagem 10.
procedure TfrmTitulos.lsvTitulosDeletingItem(Sender: TObject; AIndex: Integer; var ACanDelete: Boolean); begin ACanDelete := MessageDlg('Confirma exclusão de título?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], 0 ) = mrYes; if ACanDelete then begin DM.qryTitulos.Locate('ID_TITULO', AIndex+1, []); DM.qryTitulos.Delete; end; end;
Listagem 10. Exclusão
Perceba. Perguntamos ao usuário se ele deseja excluir o registro e igualamos o seu resultado a variável ACanDelete. Caso ela receba True, ou seja, SIM, então localizamos o registro e excluímos.
Como isso encerramos a quarta parte de nosso tutorial. Ainda será disponibilizado um vídeo com mais alguns detalhes. Então fiquem de olho, pois esse post sofrerá algumas modificações.
Conclusão
Nesse tutorial vimos muitas das operações triviais de qualquer aplicativo. Mas também vimos técnicas simples, porém eficazes para criar um pop-up de menu, muito utilizado em aplicações móveis. Vimos também a utilização de ações padrões e o uso de conteúdo do aparelho, como fotos.
O código fonte desse exemplo pode ser baixado em nosso Bitbutcket
Author: Adriano Santos
2 Comentários
Comments are closed.
[…] #4 Binding And Filtering Fields Plus Loading Images From The Camera Creating the image field type in the titles table; Creation method for auto-numbering ID Securities; Binding image in the listing title and details; Inclusion, Exclusion and Change of Titles; Selection of photos from library or camera; […]
[…] Leitura: Tutorial: Criando meu app step-by-step parte IV; […]