VB.NET - SQL Server e Entity Framework na Copa do mundo de 2014 - II
Na primeira parte do artigo eu apresentei o programa, criei o projeto no visual Studio, o banco de dados SQL Server, as tabelas , referenciei o Entity Framework e gerei o modelo de entidades a partir do banco de dados. Muito trabalho não é mesmo ? |
Vamos continuar o nosso projeto para gerenciar os jogos da copa mundo 2014 usando o VB .NET, o SQL Server e o Entity Framework definindo a interface do projeto e implementando para cada formulário as operações CRUD de manutenção de dados.
Como o tempo esta curto e a copa se aproxima e vou resumir bastante as explicações me atendo somente ao que for realmente essencial para poder entregar o projeto completo antes do dia 12 de junho.
Esse é um dos motivos pelo qual eu não vou criar uma aplicação em camadas.
Nosso projeto terá a camada de apresentação e camada de acesso a dados juntas em um mesmo projeto. A lógica de negócio estará presente nos formulários que acessara diretamente a camada de acesso a dados representada pelo Entity Data Model do Entity Framework. Então estamos criando uma aplicação que vai funcionar mas que não estará aderente ás boas práticas.
No mundo real as situações costumam ser bem mais complexas do que no mundo teórico. Na verdade toda a teoria, seja ela em qualquer área , procura idealizar um modelo para explicar um comportamento/fenômeno do mundo real. Essa teoria terá tanto mais crédito/sucesso quanto melhor poder explicar tal comportamento/fenômeno.
Quando trabalhamos com aplicações que gerenciam informações onde estão envolvidos vários ambientes e milhares de usuários simultâneos, a complexidade vai muito além daquele ambiente onde você acessa diretamente o banco de dados, i.e, um ambiente cliente/servidor onde o cliente esta diretamente ligado á fonte de informações.
Não seria possível implantar e manter, em termos de custos e mesmo de desempenho, o modelo onde um aplicativo cliente faz uma conexão com uma fonte de dados e mantém esta conexão aberta por um longo período de tempo. Multiplique isto por milhares de conexões e teremos um problema gigantesco.
Além disto, como o código para a manipulação dos dados esta no aplicativo cliente, qualquer alteração ou atualização de código deverá ser feita no cliente com reinstalação do aplicativo. Sem contar que devido aos diversos ambientes ( Windows , Unix , OS/2 ... ) a complexidade para atualizar cada front-end aumenta. Multiplique novamente este efeito para milhares de front-ends em diversos ambientes e imagine o tamanho do problema.
Para tentar enfrentar esses problemas temos um modelo teórico : O desenho de aplicativos em 3 camadas.
Por que três camadas ?
Ora, porque o problema é dividido em três partes.(didaticamente falando). As três camadas são:
|
|
Este modelo desloca a lógica de negócios e a conexão com o banco de dados da camada do cliente para a camada de negócios e a camada de dados. Se você precisar fazer qualquer alteração na lógica de negócios ou no código de acesso aos dados não vai ter que alterar nada nos aplicativos clientes . Além disto você poderá reutilizar os componentes em muitos clientes mesmo com ambientes diferentes.
Por isso em um projeto comercial adote uma arquitetura em camadas para a sua aplicação.
Aplicando o esquema tático - Implementando a camada de interface e suas funcionalidades - I
Em cada formulário eu vou destacar somente o código mais relevante. Como as funcionalidades para incluir, alterar e excluir estão presentes em todos os formulário eu não vou repetir o código para cada um dos formulários, vou apresentar o código apenas uma vez.
1- Formulário de cadastro dos Grupos das Seleções
Vamos começar definindo o formulário para gerenciar os grupos da copa do mundo. Abaixo vemos o leiaute do formulário onde o usuário poderá inserir, alterar e excluir os grupos definidos para as seleções do mundial de 2014.
Neste formulário temos os seguintes controles:
No evento Load do formulário iremos chamar a rotina carregaGrid para exibir os grupos cadastrados no DataGridView:
Private Sub FormGrupos_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
carregaGrid()
Catch ex As Exception
MsgBox(" Erro " & ex.Message)
End Try
End Sub
|
O código da rotina carregaGrid() é dado a seguir:
Private Sub carregaGrid()
Dim ctx As New Copa2014Entities
Try
Dim grupos = From grp In ctx.Grupos Select grp.GrupoId, grp.Nome
dgvGrupos.DataSource = grupos.ToList()
formataGrid()
Catch ex As Exception
MessageBox.Show("Erro " + ex.Message, "Erro::Exibir", MessageBoxButtons.OK, MessageBoxIcon.Error)
Finally
ctx.Dispose()
End Try
End Sub
|
Criamos uma instância do nosso contexto representado por Copa2014Entities e realizamos uma consulta LINQ (From grp In ctx.Grupos Select grp.GrupoId, grp.Nome) para obter os grupos cadastrados e atribuímos o resultado (grupo.ToList()) ao DataGridView.
Chamamos a rotina formataGrid() para realizar a formatação do DataGridView conforme código a seguir:
Private Sub formataGrid()
With dgvGrupos
.AutoGenerateColumns = False
.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders
.ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single
'altera o nome das colunas
.Columns(0).HeaderText = "Id"
.Columns(1).HeaderText = "Grupo"
.Columns(0).Width = 40
.Columns(1).Width = 80
'seleciona a linha inteira
.SelectionMode = DataGridViewSelectionMode.FullRowSelect
'não permite seleção de multiplas linhas
.MultiSelect = False
.DefaultCellStyle.WrapMode = DataGridViewTriState.True
.Columns("Nome").DefaultCellStyle.Alignment = DataGridViewContentAlignment.TopCenter
End With
End Sub
|
Agora temos o código implementando no evento Click de cada um dos botões de comando :
1- Inserir
Private Sub btnIncluir_Click(sender As Object, e As EventArgs) Handles btnIncluir.Click
Try
Using ctx As New Copa2014Entities
Dim grupo As New Grupos
grupo.Nome = txtGrupo.Text
ctx.Grupos.Add(grupo)
ctx.SaveChanges()
MessageBox.Show("Grupo incluído com sucesso : " & grupo.Nome.ToString, "Incluir", MessageBoxButtons.OK, MessageBoxIcon.Information)
txtGrupo.Text = ""
txtGrupo.Focus()
End Using
Catch ex As Exception
MessageBox.Show("Erro " + ex.Message, "Erro::Incluir", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
|
Criamos uma instância do nosso contexto de entidades (Copa2014Entities) e a seguir criamos uma instância da entidade Grupos. A seguir atribuímos os valores informados nas caixas de texto para cada propriedade da entidade e adicionamos o objeto ao contexto persistindo as informações via método SaveChanges().
2- Alterar
Private Sub btnAlterar_Click(sender As Object, e As EventArgs) Handles btnAlterar.Click
Dim codigo As Integer
If String.IsNullOrEmpty(txtId.Text) Then
MessageBox.Show("Informe o código do Grupo", "Alterar", MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
Else
codigo = Convert.ToInt32(txtId.Text)
End If
If String.IsNullOrEmpty(txtGrupo.Text) Then
MessageBox.Show("Informe o nome do Grupo", "Alterar", MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
End If
Try
Using ctx As New Copa2014Entities
Dim grupo = (From grp In ctx.Grupos Where grp.GrupoId = codigo).SingleOrDefault
If IsNothing(grupo) Then
MessageBox.Show("Categoria não localizada", "Localizar", MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
End If
grupo.Nome = txtGrupo.Text
ctx.SaveChanges()
End Using
Catch ex As Exception
MessageBox.Show("Erro " + ex.Message, "Erro::Alterar", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
|
No código acima validamos os valores das caixas de texto txtId e txtGrupo a seguir realizarmos uma consulta LINQ na entidade Grupos para localizar o Id informado pelo usuário na caixa de texto. Se for encontrado o Id então realizamos a alteração e salvamos os novos dados usando o método SaveChanges().
3- Excluir
Private Sub btnExcluir_Click(sender As Object, e As EventArgs) Handles btnExcluir.Click
Dim codigo As Integer
If String.IsNullOrEmpty(txtId.Text) Then
MessageBox.Show("Informe o código do Grupo", "Excluir", MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
Else
codigo = Convert.ToInt32(txtId.Text)
End If
Try
Using ctx As New Copa2014Entities
Dim grupo As Grupos = ctx.Grupos.First(Function(grp) grp.GrupoId = codigo)
txtGrupo.Text = grupo.Nome
ctx.Grupos.Remove(grupo)
ctx.SaveChanges()
End Using
Catch ex As Exception
MessageBox.Show("Erro " + ex.Message, "Erro::Excluir", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
|
Na exclusão verificamos se o Id informado pelo usuário na caixa de texto txtId é válido e realizamos uma consulta LINQ para encontrar o Id na entidade Grupos. Se for encontrado então usamos o método Remove para remover a entidade do contexto e persistimos as informações no banco de dados usando SaveChanges.
Finalmente vamos usar o evento CellClick do controle DataGridView para permitir que ao clicar em uma célula o seu valor seja exibido no formulário:
Private Sub dgvGrupos_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles dgvGrupos.CellClick
Try
If dgvGrupos.Rows(e.RowIndex).Cells(0).Value <> Nothing Then
Dim codigo As Integer = dgvGrupos.Rows(e.RowIndex).Cells(0).Value
Try
Using ctx As New Copa2014Entities
Dim grupo = (From grp In ctx.Grupos Where grp.GrupoId = codigo).SingleOrDefault
txtId.Text = grupo.GrupoId
txtGrupo.Text = grupo.Nome
End Using
Catch ex As Exception
MessageBox.Show("Erro " + ex.Message, "Erro::Localizar", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End If
Catch ex As Exception
MessageBox.Show("Erro " & ex.Message)
End Try
End Sub
|
No evento CellClick do datagridivew quando o usuário clica em uma linha ou célula do grid obtemos o código do grupo e obtemos os dados via consulta LINQ da entidade Grupos e preenchemos as caixas de texto do formulário.
Isso é tudo que precisamos para cadastrar, alterar e excluir os grupos das seleções da copa mundo de 2014.Todas essas funcionalidades se repetem nos demais formulários.
2- Formulário de Cadastro das Seleções do Mundial de 2014
No formulário para cadastrar as seleções o usuário deve informar o nome da seleção, a localização e nome da bandeira da seleção, que será armazenada em uma pasta na aplicação e o grupo a que a seleção pertence.
Neste formulário temos os seguintes controles:
O leiaute do formulário é visto a seguir:
Além disso temos neste formulário um método para localizar uma seleção pelo seu código e um método para localizar a bandeira da seleção. Eu estou usando imagens .png com tamanho 30 por 30 salvas em uma pasta chamada Bandeiras na raiz do projeto.
Para criar a pasta Bandeiras clique em PROJECT e a seguir em New Folder e informe o nome Bandeiras. A seguir copie os arquivos usados como imagens para as bandeiras nesta pasta.
1- Código para procurar uma seleção pelo seu código
Private Sub btnProcurar_Click(sender As Object, e As EventArgs) Handles btnProcurar.Click
Dim codigo As Integer
If String.IsNullOrEmpty(txtId.Text) Then
MessageBox.Show("Informe o código da seleção", "Localizar", MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
Else
codigo = Convert.ToInt32(txtId.Text)
End If
Try
Using ctx As New Copa2014Entities
Dim selecao = (From sel In ctx.Selecoes Where sel.SelecaoId = codigo).SingleOrDefault
If IsNothing(selecao) Then
MessageBox.Show("Seleção não localizada", "Localizar", MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
End If
txtSelecao.Text = selecao.NomeSelecao
txtBandeira.Text = selecao.Bandeira
cboGrupo.SelectedIndex = selecao.GrupoId - 1
exibeBandeira(selecao.Bandeira)
End Using
Catch ex As Exception
MessageBox.Show("Erro " + ex.Message, "Erro::Localizar", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
|
Neste código destacamos a rotina exibeBandeira() que exibe a bandeira da seleção no controle PicBandeira:
Private Sub exibeBandeira(ByVal nomeArquivo As String)
Try
Dim bm As New Bitmap(nomeArquivo)
picBandeira.Image = bm
picBandeira.SizeMode = PictureBoxSizeMode.AutoSize
Catch ex As Exception
Throw ex
End Try
End Sub
|
2- Código que abre uma janela de diálogo para selecionar uma bandeira para a seleção
No evento Load do formulário vamos carregar o controle Combobox - cboGrupo - com os grupos já cadastradas conforme o código abaixo:
Private Sub FormSelecoes_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
Using ctx As New Copa2014Entities
Dim consulta = From grupo In ctx.Grupos
Select grupo
cboGrupo.DisplayMember = "Nome"
cboGrupo.ValueMember = "GrupoId"
cboGrupo.DataSource = consulta.ToList
End Using
carregaGrid()
Catch ex As Exception
MsgBox(" Erro " & ex.Message)
End Try
End Sub
|
O código faz uma consulta LINQ obtendo todos os registros cadastrados e existentes na entidade Grupos e usando as propriedades DataSource, DisplayMember e ValueMember exibe os valores no controle Combobox.
Usamos também o evento SelectedIndexChanged para que qualquer alteração na seleção no controle combobox a rotina carregaGrid será chamada o que faz com que somente as seleções do Grupo selecionado na combo sejam exibidas no DataGridView.
Private Sub cboGrupo_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboGrupo.SelectedIndexChanged
carregaGrid()
End Sub
|
No evento Click do botão de comando que esta no final da caixa de texto Bandeira usamos o componente OpenFileDialog - ofd1 - para abrir uma caixa de diálogo onde o usuário vai selecionar a bandeira da seleção:
Private Sub btnBandeira_Click(sender As Object, e As EventArgs) Handles btnBandeira.Click
Dim arquivo As String = ""
Me.ofd1.Multiselect = True
Me.ofd1.Title = "Selecione a Bandeira"
ofd1.InitialDirectory = "C:\_vbn\Copa_2014\Copa_2014\Bandeiras"
'filtra para exibir somente arquivos de imagens
ofd1.Filter = "Texts (*.gif;*.png)|*.gif;*.png|" & "All files (*.*)|*.*"
ofd1.CheckFileExists = True
ofd1.CheckPathExists = True
ofd1.FilterIndex = 2
ofd1.RestoreDirectory = True
ofd1.ReadOnlyChecked = True
ofd1.ShowReadOnly = True
Dim dr As DialogResult = Me.ofd1.ShowDialog()
If dr = System.Windows.Forms.DialogResult.OK Then
Try
' Aqui fica o que deve ser executado com os arquivos selecionados.
arquivo = ofd1.FileName
Dim arq As String = ofd1.SafeFileName
txtBandeira.Text = arquivo
retornaNomePais(arq)
exibeBandeira(arquivo)
Catch ex As SecurityException
' O usuário não possui permissão para ler arquivos
MessageBox.Show((("Erro de segurança Contate o administrador de segurança da rede." & vbLf & vbLf & "Mensagem : ") +
ex.Message & vbLf & vbLf & "Detalhes (enviar ao suporte):" & vbLf & vbLf) + ex.StackTrace)
Catch ex As Exception
' Não pode carregar o arquivo (problemas de permissão)
MessageBox.Show(("Não é possível exibir a imagem : " & arquivo.Substring(arquivo.LastIndexOf("\"c))))
End Try
Else
MessageBox.Show("Operação cancelada...")
End If
End Sub
|
No código que seleciona a imagem extraímos o nome do país e usando o método retornaNomePais() que esta a baixo. Neste código usamos o método PrimeiraLetraEmCaixaAlta para que o nome do país retorne com a primeira letra em maiúscula e atribuímos o valor retornado à caixa de texto txtSelecao de forma que ao selecionar a bandeira do país o nome será preenchido automaticamente.
Private Sub retornaNomePais(ByVal nomeArquivo As String)
Try
Dim posicao_ = nomeArquivo.IndexOf("_")
Dim nomePais As String = nomeArquivo.Substring(0, posicao_)
txtSelecao.Text = PrimeiraLetraEmCaixaAlta(nomePais)
Catch ex As Exception
Throw ex
End Try
End Sub
Function PrimeiraLetraEmCaixaAlta(ByVal texto As String) As String
' verifica se é null
If String.IsNullOrEmpty(texto) Then
Return texto
End If
' Converte para um array de caracteres
Dim array() As Char = texto.ToCharArray
' caixa alta no primeiro caractere
array(0) = Char.ToUpper(array(0))
' Retorna uma nova string
Return New String(array)
End Function
|
3- Formulário de Cadastro dos Jogadores das Seleções do Mundial de 2014
No formulário para cadastrar os jogadores a caixa de combinação será preenchida com todas as seleções e o usuário deverá selecionar a seleção desejada e a seguir informar o nome do jogador e selecionar a sua foto que será carregada no controle PictureBox.
Eu criei uma pasta chamada Jogadores no projeto e nela inclui algumas fotos dos jogadores do Brasil apenas para exemplo.
Neste formulário estamos armazenando a foto no banco de dados SQL Server e para mostrar como incluir e alterar eu vou publicar as rotinas para os respectivos botões de comando.
Neste formulário temos os seguintes controles:
O leiaute do formulário é visto a seguir:
A seguir o código do evento Click do botão que fica ao lado da caixa de texto Código e é usado para procurar um jogador pelo seu código:
Private Sub btnProcurar_Click(sender As Object, e As EventArgs) Handles btnProcurar.Click
Dim codigo As Integer
If String.IsNullOrEmpty(txtId.Text) Then
MessageBox.Show("Informe o código do jogador", "Localizar", MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
Else
codigo = Convert.ToInt32(txtId.Text)
End If
Try
Using ctx As New Copa2014Entities
Dim jogador = (From jog In ctx.Jogadores Where jog.JogadorId = codigo).SingleOrDefault
If IsNothing(jogador) Then
MessageBox.Show("Jogador não localizado", "Localizar", MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
End If
txtNome.Text = jogador.Nome
cboSelecao.SelectedValue = jogador.SelecaoId
Dim imageData As Byte() = DirectCast(jogador.JogadorFoto, Byte())
If Not imageData Is Nothing Then
Using ms As New MemoryStream(imageData, 0, imageData.Length)
ms.Write(imageData, 0, imageData.Length)
picJogadorFoto.Image = Image.FromStream(ms, True)
End Using
Else
carregaImagem()
End If
End Using
Catch ex As Exception
MessageBox.Show("Erro " + ex.Message, "Erro::Localizar", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
|
Neste código o destaque é para o código em azul que obtém a imagem da entidade Jogadores e a converte para um array de bytes(ImageData) e usando um MemoryStream a exibe no controle PictJogadorFoto.
O código para selecionar uma imagem é dado a seguir:
Private Sub btnSelecionaFoto_Click(sender As Object, e As EventArgs) Handles btnSelecionaFoto.Click
If ofd1.ShowDialog() = Windows.Forms.DialogResult.OK Then
picJogadorFoto.Image = Image.FromFile(ofd1.FileName)
Else
MessageBox.Show("Operação cancelada", "Selecionar Foto", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
End Sub
|
O código usa o componente OpenFileDialog (ofd1) e permite ao usuário selecionar uma imagem exibindo-a no controle picJogadorFoto.
No evento Click do botão Incluir temos o código a seguir:
Private Sub btnIncluir_Click(sender As Object, e As EventArgs) Handles btnIncluir.Click
Try
Using ctx As New Copa2014Entities
Dim jogador As New Jogadores
jogador.Nome = txtNome.Text
jogador.SelecaoId = cboSelecao.SelectedValue
If IsNothing(picJogadorFoto.Image) Then
jogador.JogadorFoto = Nothing
Else
Dim ms As New MemoryStream()
picJogadorFoto.Image.Save(ms, picJogadorFoto.Image.RawFormat)
Dim data As Byte() = ms.GetBuffer()
jogador.JogadorFoto = data
End If
ctx.Jogadores.Add(jogador)
ctx.SaveChanges()
MessageBox.Show("Seleção incluída com sucesso : " & jogador.Nome.ToString, "Incluir", MessageBoxButtons.OK, MessageBoxIcon.Information)
txtNome.Text = ""
cboSelecao.SelectedIndex = -1
picJogadorFoto.Image = Nothing
txtNome.Focus()
End Using
Catch ex As Exception
MessageBox.Show("Erro " + ex.Message, "Erro::Incluir", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
|
Neste código o destaque fica para o código em azul que obtém a imagem do controle picJogadorFoto a converte para um array de bytes e salva na propriedade escalar jogadorFoto da entidade. Para salvar a imagem no banco de dados usamos o método SaveChanges().
Para alterar os dados de um jogador usamos o código abaixo no evento Click do botão Alterar:
Private Sub btnAlterar_Click(sender As Object, e As EventArgs) Handles btnAlterar.Click
Dim codigo As Integer
If String.IsNullOrEmpty(txtId.Text) Then
MessageBox.Show("Informe o código do Jogador", "Alterar", MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
Else
codigo = Convert.ToInt32(txtId.Text)
End If
If String.IsNullOrEmpty(txtNome.Text) Then
MessageBox.Show("Informe o nome do Jogador", "Alterar", MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
End If
Try
Using ctx As New Copa2014Entities
Dim jogador = (From jog In ctx.Jogadores Where jog.JogadorId = codigo).SingleOrDefault
If IsNothing(jogador) Then
MessageBox.Show("Jogador não localizado", "Localizar", MessageBoxButtons.OK, MessageBoxIcon.Information)
Return
End If
jogador.Nome = txtNome.Text
jogador.SelecaoId = cboSelecao.SelectedValue
If IsNothing(picJogadorFoto.Image) Then
jogador.JogadorFoto = Nothing
Else
Dim ms As New MemoryStream()
picJogadorFoto.Image.Save(ms, picJogadorFoto.Image.RawFormat)
Dim data As Byte() = ms.GetBuffer()
jogador.JogadorFoto = data
End If
ctx.SaveChanges()
End Using
Catch ex As Exception
MessageBox.Show("Erro " + ex.Message, "Erro::Alterar", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
|
No código em azul estamos o obtendo a nova imagem de picJogadorFoto e salvando na entidade da mesma forma que fizemos no botão Incluir.
Quando uma imagem não for selecionado estamos carregando o controle PictureBox - picJogadorFoto - com o arquivo semfoto.jpg usando a rotina a seguir:
Private Sub
carregaImagem() Dim arquivoSemFoto As String = Application.StartupPath & "\semfoto.jpg" picJogadorFoto.Image = Image.FromFile(arquivoSemFoto) End Sub |
No próximo artigo vou apresentar os formulários para cadastro das Sedes e dos Jogos da 1a fase e das oitavas de finais.
Tito 2:11 Porque a graça de Deus se manifestou, trazendo salvação a todos os homens,
Tito 2:12 ensinando-nos, para que, renunciando à impiedade e às paixões mundanas, vivamos no presente mundo sóbria, e justa, e piamente,
Tito 2:13 aguardando a bem-aventurada esperança e o aparecimento da glória do nosso grande Deus e Salvador Cristo Jesus,
Tito 2:14 que se deu a si mesmo por nós para nos remir de toda a iniqüidade, e purificar para si um povo todo seu, zeloso de boas obras.
Veja os Destaques e novidades do SUPER DVD VB (sempre atualizado) : clique e confira ! Quer migrar para o VB .NET ? Veja mais sistemas completos para a plataforma .NET no Super DVD .NET , confira... Quer aprender C# ??
Chegou o
Super DVD C# com exclusivo material de
suporte e vídeo aulas com curso básico sobre C# |
Gostou ? Compartilhe no Facebook Compartilhe no Twitter
Referências: