Clique aqui para ler este artigo em pdf imagem_pdf.jpg

msdn07_capa.JPG

Clique aqui para ler todos os artigos desta edição

 

Controle de Estoque no Pocket PC: do eVB ao VB .NET

por Leandro Machado

 

Criar projetos para dispositivos móveis é desafiador. Por um lado você tem todo o poder de um Pocket PC nas mãos, e por outro os dados que estão no servidor. Então, como se comunicar com o banco de dados no servidor e usar as funcionalidades do Pocket PC para desenvolver um projeto de coletores de dados para controlar o estoque? O objetivo deste artigo é ensinar como fazer isso usando a linguagem VB.NET e o bando de dados SQL Server. Em alguns momentos, farei uma comparação entre o eVB (Embebbed Visual Basic) e o VB.NET (Visual Basic .NET).

Inicialmente, eu precisava escolher uma linguagem de desenvolvimento para a aplicação. A Microsoft oferecia duas opções de linguagens para o desenvolvimento Embedded: o C e o modelo visual , o eVB. A opção pela linguagem baseada em visual foi natural, na medida em que toda a equipe de desenvolvimento já a utilizava para o desenvolvimento de aplicações Desktop e uma vez que o custo de aprendizado poderia ser minimizado com essa escolha.

Primeiramente, desenvolvi uma aplicação Off-line sincronizando os dados pelo ActiveSync. Os motivos: o alto custo em equipamentos para conectar os dispositivos à rede e, principalmente, a complexidade de codificar a aplicação para acessar diretamente o servidor SQL Server.

Com o lançamento do Visual Studio.Net 2003 e com a popularização da tecnologia Wi-Fi, optei novamente por trocar a plataforma de desenvolvimento. Essa decisão foi a mais fácil de minha carreira profissional, haja vista as incomparáveis facilidades proporcionadas pelo Visual Studio.Net.

A seguir, demonstrarei como fiz a primeira migração de um aplicativo desenvolvido em eVB para a plataforma .Net. Essa migração demandou apenas um dia de trabalho. Vale ressaltar que levei esse tempo porque separei algumas horas para ler o Help do programa.

Conferência de Estoque

O aplicativo em questão é simples e fácil de entender. Uma rotina do departamento de Expedição da empresa é a conferência física do estoque. No passado, o relatório de estoque era impresso em intermináveis páginas, as quais eram divididas entre os conferentes, que saíam em campo conferindo o que estava na lista. Quando encontravam algum item com uma quantidade diferente da especificada na lista, anotavam essa diferença na própria lista. O procedimento, além de muito demorado, gerava muitas dúvidas, e nunca se tinha certeza de que tudo havia sido conferido corretamente. O sistema de conferência pelo Pocket proporcionou maior velocidade e confiabilidade ao procedimento de conferência de estoque.

Tornando a aplicação On-Line

A grande mudança proporcionada pela troca da plataforma para o VS.Net consistiu em possibilitar o acesso a dados On-Line pela aplicação. A tecnologia utilizada foi a Web Services. Foi criado e publicado na Intranet da empresa um Web Service, o qual será consumido pela aplicação que roda no Pocket PC através da rede local, usando Wi-Fi.

No Web Service, foi criada uma função que retorna um DataSet que poderá ser usado em diferentes pontos do projeto. Como a aplicação trabalha desconectada, o Web Service é invocado no servidor, conecta a base de dados, monta um DataSet, retorna os dados à aplicação e se desconecta. Veja na Listagem 1 o código do Web Service com a função Pesquisa, o qual recebe como parâmetro uma variável do tipo String com a instrução SQL ou com a Stored Procedure desejada e, em seguida, processa e retorna um DataSet à aplicação.

 

Listagem 1 Web Service com a função Pesquisa

_

Public Function Pesquisa(ByVal xSql As String) As DataSet

  ‘Coloque a string de conexão com seu banco de dados

  Dim conexao As String =”String de conexão”

  Dim conn As SqlConnection

  Dim da As SqlDataAdapter

  Dim ds As DataSet

  conn = New SqlConnection(conexao)

  Try

    conn.Open()

    da = New SqlDataAdapter(xSql, conn)

    ds = New DataSet

    da.Fill(ds, "Tabela")

    Return ds

  Catch ex As Exception

    Throw ex

  Finally

    conn.Close()

  End Try

End Function

 

No Web Service, também foi criado uma Sub para operações de Insert, Update e Delete, que recebe como parâmetro uma variável do tipo String com a instrução SQL ou com a Stored Procedure desejada e processa a instrução. Caso ocorra algum erro durante o processamento da instrução, a Sub retornará o erro por meio da linha Throw ex (veja a Listagem 2).

 

Listagem 2 Sub para operações de Insert, Update e Delete

_

Public Sub FazRSsr(ByVal xSql As String)

  ‘Coloque a string de conexão com seu banco de dados

  Dim conexao As String = “String de Conexão”

  Dim myConnection As New SqlConnection(conexao)

  myConnection.Open()

  Dim sql As String = xSql

  Dim myCommand As New SqlCommand(sql, myConnection)

  Dim myTrans As SqlTransaction

  myTrans = myConnection.BeginTransaction()

  myCommand.Connection = myConnection

  myCommand.Transaction = myTrans

  Try

    myCommand.ExecuteNonQuery()

    myTrans.Commit()

  Catch ex As Exception

    myTrans.Rollback()

    Throw ex

  Finally

    myConnection.Close()

  End Try

End Sub

 

Com o Web Service criado, o próximo passo é publicar no servidor com o IIS (Internet Information Server) da Intranet, já que o consumo desse Web Service seria feito apenas internamente, pela rede local.

No eVb o acesso a dados era feito por ODBC. Criava-se uma fonte de dados ODBC e usava-se o Microsoft ActiveSync para importar essa fonte de dados para o Pocket PC, gerando assim uma base de dados local no Pocket. A cada conexão do dispositivo à base, o ActiveSync cuidava de replicar os dados do Pocket com a base SQL Server. Essa sincronização eventualmente gerava conflitos, e nossos analistas eram obrigados a resolvê-los manualmente.

 

Criando uma nova aplicação

Abra o VS.Net 2003, clique em New Project ou pressione CTRL + SHIFT + N. Em Visual Basic Projects, selecione SmartDeviceApplication, escolha o nome do seu projeto e o local onde irá gravá-lo. Pressione OK e, na janela aberta, selecione em “What Platform do you want to target?” a opção “Pocket PC”. Por fim, clique no botão OK.

 

A Interface

A Toolbox do .Net é muito ampla e a quantidade de objetos e classes disponíveis é infinitamente maior do que no eVB, mas uma de minhas preocupações durante a migração foi manter o mesmo layout da interface, pois os usuários do sistema já estavam habituados com o layout desenvolvido em eVB. Mesmo optando por manter o layout, os ganhos de produtividade no desenvolvimento foram muitos e vocês verão agora o porquê.

Na Toolbox, selecione o objeto InputPanel, que será usado para mostrar um teclado na tela do Pocket PC de modo a permitir que o usuário digite os dados. Use o código da Listagem 3 para habilitar ou desabilitar a visualização do teclado.

 

Listagem 3 Habilita ou desabilita a visualização do teclado

Private Sub txt_NumeroOrdem_GotFocus( _

  ByVal sender As Object, _

  ByVal e As System.EventArgs) _

  Handles txt_NumeroOrdem.GotFocus

  InputPanel1.Enabled = True

End Sub

 

Private Sub txt_NumeroOrdem_LostFocus( _

  ByVal sender As Object, _

  ByVal e As System.EventArgs) _

  Handles txt_NumeroOrdem.LostFocus

  InputPanel1.Enabled = False

End Sub

 

Em uma aplicação para Pocket PC, não é aconselhável o uso de muitos Forms para racionalizar o uso de memória. Uma boa saída para isso é o uso de TabControls, onde o programador pode inserir um desses objetos na tela, ajustá-lo ao tamanho da tela e usar quantos tabs forem necessários para a aplicação, economizando assim o uso de Forms. Veja na Figura 1 o formulário com os tabs criados.

 

Figura 1 Telas da aplicação

image001.gif

image002.gif

image003.gif

image004.gif

 

De cara notei uma grande mudança em relação ao eVB. Embora o recurso do objeto TabControl existisse no eVB, o gerenciamento da visualização dos Tabs precisava ser feito manualmente. Para cada Tab, era necessário o uso de um frame, o que reduzia ainda o espaço disponível na tela do Pocket. Dentro de cada Frame eram colocados os objetos. Tanto em ambientes de desenvolvimento como em execução, o evento click nas Tabs não executa nada, ou seja, o desenvolvedor é quem deve disparar uma rotina do tipo Frame1.Viseble=true e Frame2.Viseble=False.

No .Net esse gerenciamento é feito pelo próprio controle, e toda a codificação necessária no eVB torna-se desnecessária. Com o objeto inserido e dimensionado na tela, na barra Properties, clique na propriedade TabPages para exibir a janela de configuração TabPage Collection Editor, onde iremos criar e configurar cada Tab da página. No exemplo, utilizei Quatro Tabs (veja a Figura 1).

Na Tabpage1, serão exibidas informações das conferências geradas na Aplicação Desktop, que trabalha em conjunto com a aplicação do Pocket PC. Foram usados um DataGrid e um Label. Na Tabpage2, serão exibidos os dados da pesquisa de cada item de estoque a ser pesquisado e conferido. Foram usados nove TextBox, um Label para cada TextBox, quatro Buttons e um VScrollBar. Na Tabpage3, serão exibidos em um Grid os itens já conferidos. Foram usados dois Buttons, um DataGrid e um Label. Na Tabpage4, serão exibidos os itens conferidos, porém com dados físicos diferentes dos dados que constam na base SQL Server, denominados “Dúvidas”. Foram usados dois Buttons, um DataGrid e um Label.

 Códigos

Antes de tudo precisamos fazer referência ao WebService que iremos consumir. Abra a janela do Solution Explorer, clique com o botão direito no nome do projeto e selecione Add Web Reference. Digite a URL completa do local (http://servidor/projeto/pagina.asmx) onde está publicado seu Web Service e use o botão GO para confirmar a existência deste. Em seguida, é só clicar em Add Reference.

Pressione F7 para abrir a janela de código e defina as seguintes variáveis globais:

Option Explicit

'código da conferência selecionada

Dim cellValue As Integer

'Código do Num de ordem conferida selecionado

Dim cellConf As Integer = 0

 

O Tabpage1 é o padrão e é nele que o usuário deve selecionar a conferência de estoque com a qual pretende trabalhar. Essas conferências são geradas na aplicação Desktop pelos responsáveis por cada estoque da empresa. Como a empresa possui diversos estoques diferentes, o DataGrid colocado nesse Tabpage serve para listar os códigos de todas as conferências geradas no sistema Desktop. No evento Load do Form, digite o código da Listagem 4.

 

Listagem 4 Código do Load do formulário

Private Sub Conferencia_Frm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

  Cursor.Current = Cursors.WaitCursor

  CarregaGridConferencias()

  Cursor.Current = Cursors.Default

End Sub

 

Private Sub CarregaGridConferencias()

  'Configura o estilo de formatação do grid

  'A propriedade MappingName = "Tabela" é devido ao

  'retorno .Tables("Tabela") do WebService

  Dim style As New DataGridTableStyle

  style.MappingName = "Tabela"

 

  'Para cada coluna do grid, que deseja formatar

  'deve-se criar um DataGridTextBoxColumn

  Dim tcol As New DataGridTextBoxColumn

  With tcol

    .HeaderText = "Conferência"

    .MappingName = "ConfCodigo"

    .Width = 100

  End With

  Dim tcol2 As New DataGridTextBoxColumn

  With tcol2

    .HeaderText = "Data"

    .MappingName = "ConfData"

    .Width = 100

  End With

 

  'Adicione os DataGridTextBoxColumn ao

  'DataGridTableStyle

  style.GridColumnStyles.Add(tcol)

  style.GridColumnStyles.Add(tcol2)

 

  'Adicione os DataGridTableStyle ao DataGrid

  dtg_Conferencia.TableStyles.Add(style)

 

  'Instancia o WebService e carrega o grid

  Try

    Dim WS As New WebReference.Service1

    dtg_Conferencia.DataSource = WS.Pesquisa("sp_PDA_Conferencia").Tables("Tabela").DefaultView

  Catch ex As Exception

    Msgbox("Erro : " & ex.Message, MsgBoxStyle.Critical, "ERRO!")

  End Try

End Sub

 

Note que na rotina CarregaGridConferencias() a maior parte da codificação trata apenas da formatação do DataGrid, tanto das linhas quanto das colunas. Usei a classe DataGridTableStyle porque ela facilita a formatação e exibição dos dados no Grid.

Os dados do banco de dados SQL Server são carregados no DataGrid através do consumo do Web Service criado no início do artigo. No consumo do Web Service, usei a estrutura Try/Catch para tratar o erro e, se não fosse esse tratamento, a codificação estaria resumida a duas linhas.

 

Dim WS As New WebReference.Service1

dtg_Conferencia.DataSource = WS.Pesquisa("sp_PDA_Conferencia").Tables("Tabela").DefaultView

 

O parâmetro do Web Service é o nome de Stored Procedure (veja a Listagem 5), mas seria possível passar qualquer código SQL.

 

Listagem 5 Stored Procedure

CREATE Procedure sp_PDA_Conferencia

as

Select ConfCodigo, ConfData

From ConfereEstoque

GO

 

No eVb não existe o DataGrid, mas sim algo parecido com o MSFlexGrid. Esse recurso era outro grande gerador de código, pois a formatação e a população do Grid eram feitas manualmente, linha por linha, coluna por coluna, célula por célula. Tudo manualmente. É necessário dizer que esse método manual de popular o Grid torna o carregamento dos dados extremamente lento, devido ao fato de o código processar individualmente todas as linhas do Recordset gerado para a tarefa.

Como os dados estão exibidos no GRID, o usuário precisa selecionar a conferência desejada usando a caneta no Grid que contém a conferência em que irá trabalhar (veja a Listagem 6).

 

Listagem 6 Seleciona um item no DataGrid

Private Sub dtg_Conferencia_Click( _

    ByVal sender As System.Object, _

    ByVal e As System.EventArgs) _

    Handles dtg_Conferencia.Click

  Cursor.Current = Cursors.WaitCursor

  Try

    LimpaCampos(TabPage2.Controls)

    ' Valor que guarda a Linha selecionada

    Dim vIntRow As Integer

    vIntRow = dtg_Conferencia.CurrentRowIndex

    'Seta a seleção para a Coluna 0, que é a coluna

    'do código. A linha da seleção é mantida na

    'variável vIntRow

    dtg_Conferencia.CurrentCell = _

        New DataGridCell(vIntRow, 0)

 

    Dim selectedCell As DataGridCell

    selectedCell = dtg_Conferencia.CurrentCell

    Dim selectedItem As Object

    selectedItem = _

      dtg_Conferencia.Item(selectedCell.RowNumber, _

      selectedCell.ColumnNumber)

    cellValue = CInt(selectedItem)

    'Preenche o Label com o Código da Conferencia

    'selecionado no grid

    lbl_Codigo.Text = "Código Selecionado:" & _

        cellValue.ToString

    Me.Text = "Conferência:" & cellValue.ToString

  Catch ex As Exception

    Msgbox("Erro : " & ex.Message, MsgBoxStyle.Critical, "ERRO!")

  Finally

    Cursor.Current = Cursors.Default

  End Try

End Sub

 

Selecionado o Código da Conferência, nossa Tab de trabalho passa a ser a Tabpage2. Nessa Tab, o usuário digita o número de ordem que identifica o item a ser conferido. Quando ele selecionar o Botão “Pesquisa”, será feito um novo consumo ao Web Service para carregar as características do item na tela Pocket PC (veja a Listagem 7).

 

Listagem 7 Código do botão Pesquisa

Private Sub btn_Pesquisa_Click( _

  ByVal sender As System.Object, _

  ByVal e As System.EventArgs) _

  Handles btn_Pesquisa.Click

  Cursor.Current = Cursors.WaitCursor

  If cellValue = 0 Then

     Msgbox("Você não selecionou nenhum Código de Conferência!", MsgBoxStyle.Information, "Atenção!")

     Cursor.Current = Cursors.Default

     Exit Sub

  ElseIf IsNumeric(txt_NumeroOrdem.Text.Trim) = False Then

     Msgbox("Você não digitou números válidos!", MsgBoxStyle.Information, "Atenção!")

     Cursor.Current = Cursors.Default

     Exit Sub

  Else

     MoveCampos()

     Cursor.Current = Cursors.Default

  End If

End Sub

   

Sub MoveCampos()

  Try

    Dim WS As New WebReference.Service1

    Dim dts As New DataSet

    dts = WS.Pesquisa("sp_PDA_PesquisaSubOrdem " & cellValue & "," & CInt(txt_NumeroOrdem.Text))

    If dts.Tables("Tabela").Rows.Count = 0 Then

       Dim vStrMsg As String = _

       Msgbox("Produto não encontrado!Deseja Marcar como Dúvida?", MsgBoxStyle.YesNo, "Atenção!")

       If vStrMsg = MsgBoxResult.No Then

          Exit Sub

       Else

          HabilitaText(TabPage2.Controls)

          txt_Ordem.Text = txt_NumeroOrdem.Text.Trim

          txt_Status.Enabled = False

          btn_Confirma.Enabled = False

          btn_SalvaDuvida.Text = "Salvar Dúvida!"

       End If

    Else

       LimpaCampos(TabPage2.Controls)

       txt_NumeroOrdem.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubOrdem"))

       txt_Descricao.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubDescricao"))

       txt_Diametro.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubDiametro"))

       txt_Formato.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubFormato"))

       txt_Gm2.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubGramatura"))

       txt_Largura.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubLargura"))

       txt_Ordem.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubOrdem"))

       txt_Quantidade.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "SubQuantidade"))

       txt_Status.DataBindings.Add(New Binding("Text", dts.Tables("Tabela"), "ConfSimNaoDuvida"))

       If txt_Status.Text = "C" Then

          txt_Status.Text = "CONFERIDO"

       ElseIf txt_Status.Text = "D" Then

          txt_Status.Text = "DÚVIDA"

       ElseIf txt_Status.Text = "N" Then

          txt_Status.Text = "NÃO CONFERIDO"

       End If

    End If

  Catch ex As Exception

    Msgbox("Erro : " & ex.Message, MsgBoxStyle.Critical, "ERRO!")

  Finally

    Cursor.Current = Cursors.Default

  End Try

End Sub

 

O mais importante do código da Listagem 7 é novamente o consumo do Web Service. Se o Web Service não retornar nenhum valor no DataSet, sugerimos ao usuário marcar o item como “Dúvida”, caso contrário colocamos os dados de retorno do Web Service que estão no Data Set nos TextBox correspondentes.

Veja na Listagem 8 um exemplo para gravar Item como conferido no banco de dados.

 

Listagem 8 Gravar dados no banco de dados

Private Sub btn_Confirma_Click( _

   ByVal sender As System.Object, _

   ByVal e As System.EventArgs) _

   Handles btn_Confirma.Click

  'Verifica se há alguma coisa preenchida na tela

  'antes de fazer o update

  Cursor.Current = Cursors.WaitCursor

  If txt_Ordem.Text = "" Then

     Msgbox("Não há nada em Tela para ser Marcado como Conferido!", MsgBoxStyle.Exclamation, "Atenção!")

     Cursor.Current = Cursors.Default

     Exit Sub

  End If

  'Rotina para ver se número não foi conferido

  If txt_Status.Text <> "NÃO CONFERIDO" Then

     Msgbox("Esse número consta como conferido. Você não pode salvar duas vezes!", MsgBoxStyle.Exclamation, "Atenção!")

     Cursor.Current = Cursors.Default

     Exit Sub

  End If

  Dim WS As New WebReference.Service1

  Dim xSql As String = "sp_PDA_UpdateConferido " & cellValue & "," & txt_Ordem.Text

  Try

     WS.FazRSsr(xSql)

     Msgbox("Conferido!", MsgBoxStyle.Information, "OK!")

     LimpaCampos(TabPage2.Controls)

     dtg_Conferidos.DataSource = Nothing

     dtg_Duvida.DataSource = Nothing

  Catch ex As Exception

     Msgbox("Erro Confirmar: " & ex.Message, MsgBoxStyle.Critical, "ERRO!")

  Finally

     Cursor.Current = Cursors.Default

  End Try

End Sub

 

O consumo do Web Service no código da Listagem 8 destina-se a gravar dados no banco de dados. Passei uma Stored Procedure com o código do item conferido para o Web Service, que cuidou de gravar os dados na base SQL Server. Caso o Web Service tenha algum problema para gravar os dados, o tratamento do Catch mostrará um MsgBox com o erro.

Os Tabpages3 e 4 possuem DataGrids para ajudar o usuário a verificar o que foi gravado como conferido e o que foi gravado como dúvida. O código é basicamente consumir o mesmo Web Service para carregar o DataGrid e a formatação usa a mesma Classe de Formatação de Grid utilizada acima.

Quando o item pesquisado não for encontrado na Base de Dados, o usuário deverá digitar os dados do item e gravá-lo na base como “Dúvida”. Para executar esse procedimento, consumi a mesma Sub do Web Service usado para gravar o Item como “Conferido”, passando para isso o nome da Stored Procedure adequada ao Web Service.

Conclusão

Com este projeto, o tempo de execução de conferências de estoque foi reduzido à metade. E, com o complemento do programa na aplicação Desktop, o tempo para solução das dúvidas também foi reduzido à metade, pois com a aplicação migrada e funcionando On-Line os encarregados puderam acompanhar de seus Desktops o andamento da conferência em tempo real.

A partir deste projeto, a equipe ganhou em produtividade e desenvolveu outros aplicativos com sucesso em muito menos tempo do que era feito no eVB. Além disso, não foram mais necessárias correções manuais resultantes de falhas na replicação e sincronização dos dados.

O projeto em .Net economizou muito na codificação, se comparado ao eVB, mas o que mais tornou interessante a migração foi o fato de ela nos permitir utilizar a mesma plataforma de desenvolvimento que a aplicação Desktop. Como as duas aplicações se completam, isso torna o desenvolvimento muito mais produtivo.