Skip Navigation Links



Translate this page now :



»Programação
»Programação.NET
»Banco de Dados
»Webdesign
»Office
» Certificações Microsoft 4
»Treinamentos4
»Programação 4
»Webdesign«
»Office & User Tips«
»Grupos de UsuĆ”rios
»CĆ©lulas AcadĆŖmicas«
intcontpiada : 118
O melhor teclado da microsoft
Você já está cadastrado e participa do grupo de usuários de sua cidade ? Se não, comente o porque.
 
 
FaƧa um pequeno teste com 10 questƵes de VB
.:.
Teste seus conhecimentos em Visual Basic, SQL Server e ASP 3.0 com nossas provas on-line
.:.
Aprimore seus conhecimentos em programaĆ§Ć£o com nosso treinamento on-line de lĆ³gica de programaĆ§Ć£o
.:.
Veja nosso calendƔrio de treinamentos
Gostou da PƔgina?
Então

para um amigo!

Pesquisa personalizada
Pesquisar Dicas:

 






Imprimir

Baixe os fontes

Obtendo Schemas de base de dados utilizando o ADO.NET

Existem muitas aplicações que precisam obter informações sobre a estrutura de objetos na base de dados, isso é algo bem frequente. Aplicações para geração de código, por exemplo, precisam de amplas informações sobre a estrutura das bases de dados.

O data provider para OLEDB implementa recursos para obtermos a estrutura de bases de dados. Como trata-se de um data provider ligado a inúmeros bancos de dados (através dos providers OLEDB), podemos obter a estrutura de inúmeros tipos de banco de dados diferentes.

Toda essa possibilidade encontra-se no método chamado GetOleDbSchemaTable. Este método recebe 2 parâmetros : Um GUID que identifica o tipo de objeto que desejamos (tabelas, procedures, views, etc..) e um 2o parâmetro que atua como filtro para os dados que serão retornados. Este método devolve uma dataTable, tornando simples a manipulação dos dados no client.

Vamos então montar um pequeno projeto que nos permita acessar e trabalhar com os dados sobre a estrutura do banco, demonstrando desta forma como manipular estes dados, entre outros exemplos úteis.

Vamos inicialmente criar uma Windows Application com um form e colocar dois objetos no form : Uma listBox (lstObjetos) e uma DataGrid (dg).

Vamos preencher a listbox com uma lista dos tipos de objetos que poderemos estar requisitando da base de dados. Mas este preenchimento não será tão simples : Os tipos de objetos não estão em um enum, estão disponíveis na forma de propriedades da classe System.Data.OleDb.OleDbSchemaGuid, cada uma devolvendo o GUID do tipo de objeto correspondente. Precisaremos então utilizar Reflections para podermos preencher a listbox, veja :

   58         Dim p As New ArrayList

   59         Dim t As Type

   60         t = GetType(System.Data.OleDb.OleDbSchemaGuid)

   61         p.AddRange(t.GetFields)

   62         lstObjetos.DataSource = p

   63         lstObjetos.DisplayMember = "Name"

Desta forma vinculamos a listbox a um arrayList de objetos Field, que devolvem os campos existentes na classe OleDbSchemaGuid.

Sempre que houver um click na listbox vamos obter os dados referentes a este tipo de objeto e vamos exibir tais dados na dataGrid. A lista de objetos, porém, é genérica. Portanto nem todos os objetos estão disponíveis para todos os tipos de banco, precisaremos tratar isso também. Veja como fica :

   58         Dim ds As New DataSet

   59         Dim nomeTabela As String

   60         nomeTabela = DirectCast(lstObjetos.SelectedItem, FieldInfo).Name

   61 

   62 

   63         Dim dt As DataTable

   64         CN.Open()

   65         Try

   66             dt = CN.GetOleDbSchemaTable(DirectCast(lstObjetos.SelectedItem, FieldInfo).GetValue(GetType(System.Data.OleDb.OleDbSchemaGuid)), Nothing)

   67         Catch er As Exception

   68             MsgBox("Este tipo de elemento não está disponível neste provider OLEDB" & vbCrLf & er.Message)

   69             Exit Sub

   70         Finally

   71             CN.Close()

   72         End Try

   73 

   74         ds.Tables.Add(dt)

   75 

   76 

   77         dg.DataSource = ds.Tables(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name)

Observe a expressão, um pouco mais complexa, utilizada como primeiro parâmetro no método GetOledbSchemaTable. Tendo o objeto FieldInfo, precisamos utilizar o método GetValue deste objeto para obter o valor de uma propriedade. Porém como as propriedades em questão são Shared, a aplicação do GetValue recebe como parâmetro um type, a própria classe OleDbSchemaGuid .

Pronto. Com essa codificação simples já temos acesso a informações de estrutura da base de dados. Agora vamos tornar esta aplicação um pouco mais sofisticada. Vamos adicionar a possibilidade do usuário fazer personalizações no que é exibido (por exemplo, cortando tipos de objetos desnecessários na listbox e colunas desnecessárias na grid) e manter a configuração feita pelo usuário entre as execuções da aplicação.

Vamos começar com a listbox. Além de possibilitar a deleção de itens da listbox precisaremos guardar os itens que foram deletados em um arraylist, o arrayList dos deletados. Poderemos serializar este arraylist para um arquivo em disco e recupera-lo posteriormente, quando a aplicação for novamente aberta. Então vamos ver como isso fica :

   86     Private Sub lstObjetos_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles lstObjetos.KeyDown

   87         If e.KeyData = Keys.Delete Then

   88             Dim c As CurrencyManager

   89             ar.Add(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name)

   90             remover(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name)

   91 

   92             c = Me.BindingContext(p)

   93             c.SuspendBinding()

   94             c.ResumeBinding()

   95 

   96         End If

   97     End Sub

Observe que temos então 2 arrayLists : Um arraylist com os elementos exibidos e um arraylist para guardar os elementos deletados. No exemplo acima adicionamos o nome do item deletado no arrayList de deletados (AR) e eliminamos o item deletado do array de itens exibidos. Para isso criei uma sub a parte chamada remover.

Quanto ao uso do CurrencyManager, acima, isso se deve ao fato de que uma listbox, quando vinculada a um arraylist, não se atualiza automaticamente quando o arrayList se atualiza. Esse assunto foi tema de uma dica no site BufaloInfo, veja em http://www.bufaloinfo.com.br/dicas.asp?cod=725

Veja como fica a sub Remover :

   99     Private Sub remover(ByVal nome As String)

  100         For Each f As FieldInfo In p

  101             If f.Name = nome Then

  102                 p.Remove(f)

  103                 Exit Sub

  104             End If

  105         Next

  106     End Sub

Como o arrayList ligado a combo possui objetos FieldInfo foi necessário localizar o objeto FieldInfo correto para poder elimina-lo.

Agora vejamos como fica o processo de serialização e deserialização do arrayList. Este código é especialmente interessante pois esta tarefa pode ser realizada com qualquer objeto que tenha capacidade de serialização - e são muitos.

Veja o código de serialização :

   58         If File.Exists(NomeHash) Then

   59             File.Delete(NomeHash)

   60         End If

   61         Dim obj As New BinaryFormatter

   62         Dim st As New FileStream(NomeHash, FileMode.Create)

   63         obj.Serialize(st, ar)

   64         st.Close()

Como trata-se da gravação de um arquivo em disco, tomei o cuidado de verificar se o arquivo existe e, caso exista, deleta-lo.

Com a classe BinaryFormatter (que fica em System.Runtime.Serialization.Formatters.Binary ) fica fácil fazer o processo de serialização e deserialização de um objeto, como pode observar acima. O FileStream, localizado em System.IO, completa o trabalho fazendo a gravação do resultado da serialização em um arquivo em disco.

Veja como fica a deserialização, tão simples quanto :

   59         If File.Exists(NomeHash) Then

   60             Dim obj As New BinaryFormatter

   61             Dim st As New FileStream(NomeHash, FileMode.Open)

   62             ar = obj.Deserialize(st)

   63             st.Close()

   64             For Each f As String In ar

   65                 remover(f)

   66             Next

   67         End If

Observe que após a deserialização já implementei o que desejamos : faço uma varredura no arraylist e a eliminação dos elementos deletados do arraylist principal.

Neste trecho de código utilizei um truque interessante : Em todos os pontos nos quais me refiro ao nome do arquivo utilizei uma chamada a uma propriedade, NomeHash.

O nome do arquivo propriamente é uma montagem que depende do local onde a aplicação estiver. Utilizando uma propriedade, NomeHash, centralizo em um único ponto do código a geração do nome do arquivo. Assim sendo se em algum momento for necessário alterar o código de geração do nome deste arquivo bastará altera-lo em um único local. Veja como fica a propriedade :

   98     Private ReadOnly Property NomeHash() As String

   99         Get

  100             Return (Application.StartupPath & "\hash.bin")

  101         End Get

  102     End Property

Vamos agora permitir que o usuário personalize as colunas na DataTable e mantenha essa personalização através das execuções da aplicação.

Para permitir a deleção de colunas na grid vamos criar um contextMenu. Porém para que possamos saber em que coluna o mouse estava no momento em que o contextMenu for ativado não irei vincula-lo diretamente na propriedade contextMenu da grid, mas sim ativa-lo através do evento mouseUp. Veja como fica :

  104     Private Sub dg_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles dg.MouseUp

  105         If e.Button = MouseButtons.Right Then

  106             pt = New Point(e.X, e.Y)

  107             ContextMenu1.Show(dg, pt)

  108         End If

  109     End Sub

Observe que guardei na variável pt um objeto point com as coordenadas do click. Desta forma poderemos utilizar este objeto para descobrir qual foi a coluna clicada. Veja como fica o click do menu :

  111     Private Sub MenuItem1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItem1.Click

  112 

  113         Dim hit As DataGrid.HitTestInfo

  114         hit = dg.HitTest(pt)

  115         DirectCast(dg.DataSource, DataTable).Columns.RemoveAt(hit.Column)

  116 

  117     End Sub

Através do método HitTest podemos obter informações sobre um determinado ponto com relação a datagrid, descobrindo, por exemplo, qual foi a coluna clicada, para remove-la em seguida como é feito no código acima.

Partimos então para o próximo problema : Como manter a escolha das colunas através das execuções da aplicação ?

A melhor forma de fazer isso é guardando o schema do DataSet em um arquivo em disco. Precisaremos então fazer pequenas adaptações na forma de carregar o dataSet : No nosso exemplo anterior, simplesmente adicionavamos a dataTable. Agora precisaremos manter um dataSet único e chegar se a dataTable já existe ou não. Se não existe, simplesmente adicionamos, mas se já existe então precisamos preencher os dados dentro da estrutura da dataTable já existente, fazendo um Merge.

Veja como fica :

  119     Private Sub LerSchema(ByVal nome As System.Guid)

  120         Dim dt As DataTable

  121         CN.Open()

  122         Try

  123             dt = CN.GetOleDbSchemaTable(nome, Nothing)

  124         Catch er As Exception

  125             MsgBox("Este tipo de elemento não está disponível neste provider OLEDB" & vbCrLf & er.Message)

  126             Exit Sub

  127         Finally

  128             CN.Close()

  129         End Try

  130         If Not ds.Tables.Contains(dt.TableName) Then

  131             ds.Tables.Add(dt)

  132         Else

  133             ds.Merge(dt, False, MissingSchemaAction.Ignore)

  134         End If

  135     End Sub

Observe no código acima que o Merge faz uso do MissingSchemaAction. Isso porque o usuário pode ter eliminado alguns campos da tabela. Se o Merge não definisse essa opção os campos seriam re-adicionados.

Outra questão interessante é que neste ponto já transformei essa rotina em uma sub LerSchema. Essa sub é chamada do SelectedIndexChanged da listbox. Veja como fica :

  137     Private Sub lstObjetos_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstObjetos.SelectedIndexChanged

  138 

  139         Dim nomeTabela As String

  140         nomeTabela = DirectCast(lstObjetos.SelectedItem, FieldInfo).Name

  141         If Not ds.Tables.Contains(nomeTabela) Then

  142 

  143             LerSchema(DirectCast(lstObjetos.SelectedItem, FieldInfo).GetValue(GetType(System.Data.OleDb.OleDbSchemaGuid)))

  144         ElseIf ds.Tables(nomeTabela).Rows.Count = 0 Then

  145             LerSchema(DirectCast(lstObjetos.SelectedItem, FieldInfo).GetValue(GetType(System.Data.OleDb.OleDbSchemaGuid)))

  146         End If

  147 

  148         dg.DataSource = ds.Tables(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name)

  149 

  150     End Sub

Se a tabela não existe no dataSet ou se está sem linhas é chamada a sub LerSchemas, do contrário é porque os dados já haviam sido carregados antes, então a tabela é apenas exibida novamente.

Serializar e deserializar o schema deste dataSet será uma tarefa bem simples, mais que no caso anterior : O dataSet não precisa que utilizemos uma classe de serialização, como antes, ele próprio possui métodos para fazer a serialização do schema.

Serializar :

   59         ds.WriteXmlSchema(NomeSchema)

Deserializar :

   59         If IO.File.Exists(NomeSchema) Then

   60             ds.ReadXmlSchema(NomeSchema)

   61         End If

Propriedade NomeSchema :

  146     Private ReadOnly Property NomeSchema() As String

  147         Get

  148             Return (Application.StartupPath & "\schema.xml")

  149         End Get

  150     End Property

Neste ponto já conseguimos permitir que o usuário personalize a aplicação e que esta personalização seja mantida, o que já torna a aplicação bem interessante.

Mas para que a aplicação fique ainda mais útil torna-se necessário permitir que a aplicação guarde não apenas uma, mas diversas configurações nomeadas. Assim sendo o usuário pode se conectar a diferentes bancos de dados e guardar as configurações de como deseja visualizar cada um.

Vamos começar criando uma classe Configuracao para agregar os dados que cada configuracao deve ter. Uma configuração terá um nome, uma string de conexão e irá também gerar um código que será anexado aos nomes de arquivos que guardarão as características da configuração. Veja como fica :

  152     <Serializable()> Public Class config

  153         Public NomeConfig As String

  154         Public Conexao As String

  155         Private _id As String

  156 

  157         Public Sub New()

  158 

  159         End Sub

  160 

  161         Public Sub New(ByVal cf As String, ByVal con As String, ByVal id As Integer)

  162             NomeConfig = cf

  163             Conexao = con

  164         End Sub

  165 

  166         Public ReadOnly Property CodigoArquivo() As String

  167             Get

  168                 Return (NomeConfig.Substring(0, 3) & _id)

  169             End Get

  170         End Property

  171 

  172         Public Overrides Function tostring() As String

  173             Return (NomeConfig)

  174         End Function

  175 

  176     End Class

Criei um construtor recebendo parâmetros, para simplificar a configuração desta classe. Uma propriedade readonly chamada CodigoArquivo, que irá gerar uma pequena string a ser anexada o nome dos arquivos. A função ToString levou um overrides para podermos definir o que desejamos que apareça em uma combo quando esta classe for inserida em uma combo.

Vamos então preparar este formulário que temos para poder lidar com essa classe. Esse formulário irá receber uma instância desta classe como parâmetro quando for aberto (o usuário já terá escolhido a configuração - faremos isso depois) e deverá utilizar as informações contidas nesta classe.

Primeiramente, uma propriedade para controlar o recebimento desta classe :

  178     Public Property configuracao() As frmConfig.config

  179         Get

  180             Return (_Configuracao)

  181         End Get

  182         Set(ByVal Value As frmConfig.config)

  183             _Configuracao = Value

  184         End Set

  185     End Property

As propriedades que geram os nomes dos arquivos passarão a utilizar esta propriedade configuração, veja :

  187     Private ReadOnly Property NomeSchema() As String

  188         Get

  189             Return (Application.StartupPath & "\schema" & Configuracao.CodigoArquivo & ".xml")

  190         End Get

  191     End Property

  192 

  193     Private ReadOnly Property NomeHash() As String

  194         Get

  195             Return (Application.StartupPath & "\hash" & Configuracao.CodigoArquivo & ".bin")

  196         End Get

  197     End Property

Podemos também exibir no título do form o nome da configuração selecionada :

   59         Me.Text = Me.configuracao.NomeConfig

Precisaremos de um botão que permita ao usuário trocar a configuração selecionada. Neste caso o ideal será transformarmos a inicialização do form (deserializações) e a finalização (serializações) em subs que possam ser chamadas de mais de um local. Veja como fica :

  198     Sub inicializarForm()

  199         Dim t As Type

  200 

  201         salvarconfig()

  202 

  203         Dim c As CurrencyManager

  204         c = Me.BindingContext(p)

  205         c.SuspendBinding()

  206 

  207         ar.Clear()

  208         p.Clear()

  209 

  210         ds = New DataSet

  211         lstObjetos.DataSource = Nothing

  212         lstObjetos.Items.Clear()

  213 

  214         Me.Text = Me.configuracao.NomeConfig

  215 

  216         t = GetType(System.Data.OleDb.OleDbSchemaGuid)

  217         p.AddRange(t.GetFields)

  218 

  219         If File.Exists(NomeHash) Then

  220             Dim obj As New BinaryFormatter

  221             Dim st As New FileStream(NomeHash, FileMode.Open)

  222             ar = obj.Deserialize(st)

  223             st.Close()

  224             For Each f As String In ar

  225                 remover(f)

  226             Next

  227         End If

  228 

  229         If IO.File.Exists(NomeSchema) Then

  230             ds.ReadXmlSchema(NomeSchema)

  231         End If

  232 

  233 

  234         lstObjetos.DataSource = p

  235         lstObjetos.DisplayMember = "Name"

  236         c.ResumeBinding()

  237     End Sub

  238 

  239     Public Sub salvarconfig()

  240         ds.WriteXmlSchema(NomeSchema)

  241 

  242 

  243         If File.Exists(NomeHash) Then

  244             File.Delete(NomeHash)

  245         End If

  246         Dim obj As New BinaryFormatter

  247         Dim st As New FileStream(NomeHash, FileMode.Create)

  248         obj.Serialize(st, ar)

  249         st.Close()

  250     End Sub

Desta forma podemos chamar essas subs do evento Load, closing, do botão e uma chama a outra (ao trocar a configuração, salva-se a anterior).

Vamos então criar um 2o form, que permitirá ao usuário selecionar a configuração desejada ou criar uma nova configuração. A aplicação passará a ser disparada por uma sub main que interligará os dois forms, veja :

  253 Module Module1

  254     Public Sub Main()

  255         Dim frm As New frmConfig

  256         Dim f As New Form1

  257         frm.ShowDialog()

  258         If frm.DialogResult = DialogResult.OK Then

  259             f.configuracao = frm.Configuracao

  260             Application.Run(f)

  261         End If

  262     End Sub

  263 End Module

No formulário para escolha da configuração, vamos chama-lo de frmConfig, vamos inserir uma combo para listar as configurações existentes, um botão para disparar a criação de uma nova configuração e um botão para dar ok na seleção de uma configuração e seguir adiante.

Teremos mais um arquivo serializado, que conterá a lista de configurações existentes. Precisaremos no load do form fazer a deserialização deste arquivo. Veja como fica :

   59         If File.Exists(arquivoConfig) Then

   60             Dim obj As New BinaryFormatter

   61             Dim st As New FileStream(arquivoConfig, FileMode.Open)

   62             hs = obj.Deserialize(st)

   63             st.Close()

   64         End If

   65         cmbConfigs.DataSource = hs

  258     Public ReadOnly Property arquivoConfig() As String

  259         Get

  260             Return (Application.StartupPath & "/configs.bin")

  261         End Get

  262     End Property

Quando um item na combo for selecionado, habilitamos o botão ok. Quando o botão Ok for clicado, atribuimos a classe selecionada na variável Configuracao. Veja como fica :

  265     Public Configuracao As config

  266 

  267     Private Sub cmbConfigs_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmbConfigs.SelectedIndexChanged

  268         cmdOk.Enabled = True

  269     End Sub

  270 

  271     Private Sub cmdOk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdOk.Click

  272         Me.Configuracao = DirectCast(cmbConfigs.SelectedItem, config)

  273     End Sub

Quando o usuário clicar no botão para adicionar uma nova configuração precisaremos fazer os seguintes passos :

  • Perguntar o nome da nova configuração
  • Permitir que o usuário monte uma string de conexão
  • Criar uma nova instância da classe config, adicionar no arrayList e fazer refresh da combo


Veja como fica :

   59         Dim nomeConfig As String

   60         nomeConfig = InputBox("Informe o nome da nova configuração", "Nova Configuração")

   61 

   62         If nomeConfig = "" Then

   63             Exit Sub

   64         End If

   65 

   66         Dim dataLink As Object = Microsoft.VisualBasic.Interaction.CreateObject("DataLinks")

   67         dataLink.hWnd = Me.Handle

   68 

   69         Dim o As Object = dataLink.PromptNew()

   70         If o Is Nothing Then

   71             Exit Sub

   72         Else

   73             Dim cl As New config(nomeConfig, o.connectionstring.ToString, hs.Count)

   74             hs.Add(cl)

   75             Dim c As CurrencyManager

   76             c = Me.BindingContext(hs)

   77             c.SuspendBinding()

   78             c.ResumeBinding()

   79 

   80         End If

Observe que neste código utilizei um truque para exibir para o usuário a própria janela de configuração de uma conexão do OLEDB, permitindo desta forma que o usuário crie a string de conexão em um ambiente já familiar a ele. Isso foi exposto em uma dica em http://www.bufaloinfo.com.br/dicas.asp?cod=430

Agora que chegamos neste ponto e a aplicação já está funcionando e guardando configurações diversas do usuário, vamos então inserir na aplicação a capacidade de fazer filtragens das informações exibidas. Vamos criar 2 tipos de filtragem : Uma filtragem simples, aplicada sobre os dados exibidos e uma filtragem relacionada, por exemplo, o usuário desejará ver os campos de uma determinada tabela.

Vamos começar, claro, pela filtragem mais simples. Precisaremos criar um formulário novo através do qual o usuário possa definir a filtragem a ser realizada. Vamos chamar este novo formulário de frmRestricoes.

Muitos objetos deste formulário deverão ser dinâmicos. Ele deverá exibir os campos existentes na DataTable que será filtrada (exibir mesmo campos que tenham sido excluidos pelo usuário) e permitir que o usuário digite, para cada campo, o critério de filtro, ou deixe o campo vazio. Assim sendo deverão ser criados labels e textboxes tantos quantos forem os campos da dataTable sendo filtrada.

Mas isso se complica um pouco mais. As DataTables possuem os campos devolvidos pelo provider OLEDB, fornecendo informações sobre determinados tipos de objetos no banco. O problema é que alguns dos campos em cada DataTable são específicos de determinados data providers e, por isso, não podem ser usados como parte das restrições. As DataTables tem um conjunto de campos genéricos, estes podendo ser usados nas restrições, e um conjunto de campos específicos, que não podem. O número de campos que podem ou não serem utilizados nas restrições variam para cada DataTable.

No endereço http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/oledbschema_rowsets.asp encontrei documentação sobre quantas colunas de cada tipo de DataTable podem ser usadas como critério. Isso acaba sendo algo fixo, veja como codifiquei, para resolver o problema :

  292     Private Sub DefinirRestricoes()

  293         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Assertions, 3)

  294         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Catalogs, 1)

  295         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Character_Sets, 3)

  296         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Check_Constraints, 3)

  297         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Check_Constraints_By_Table, 6)

  298         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Collations, 3)

  299         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Column_Domain_Usage, 3)

  300         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Column_Privileges, 6)

  301         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Columns, 4)

  302         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Constraint_Column_Usage, 7)

  303         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Constraint_Table_Usage, 5)

  304         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Foreign_Keys, 6)

  305         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Indexes, 5)

  306         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Key_Column_Usage, 7)

  307         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Primary_Keys, 3)

  308         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Procedure_Columns, 4)

  309         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Procedure_Parameters, 4)

  310         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Procedures, 4)

  311         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Provider_Types, 2)

  312         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Referential_Constraints, 3)

  313         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Schemata, 3)

  314         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Statistics, 3)

  315         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Table_Constraints, 7)

  316         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Table_Privileges, 5)

  317         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Table_Statistics, 7)

  318         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Tables, 4)

  319         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Tables_Info, 4)

  320         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Translations, 3)

  321         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Usage_Privileges, 6)

  322         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.View_Column_Usage, 3)

  323         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.View_Table_Usage, 3)

  324         hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Views, 3)

  325     End Sub

Assim sendo, utilizei esta hashTable definida em hard-code para guardar estas definições, que são fixas, e utiliza-las no algorítimo em código.

O formulário de restrições precisará receber o tipo de objeto para o qual estão sendo montadas as restrições. Também precisará receber uma instância da classe config (frmconfig.config), pois ela contém a string de conexão da base utilizada no momento. Como resultado o formulário irá gerar um array de strings para ser utilizado como critério para a DataTable selecionada.

  327     Public TipoObjeto As System.Guid

  328     Public Configuracao As frmConfig.config

  329 

  330     Dim filtro() As String

  331 

  332 

  333     Public Property ItensFiltro() As String()

  334         Get

  335             Return (filtro)

  336         End Get

  337         Set(ByVal Value As String())

  338             filtro = Value

  339         End Set

  340     End Property

 

Duas funcionalidades a mais deste form são importantes de ressaltar, antes de vermos o código :

O formulário pode estar criando uma restrição, mas pode também estar editando uma restrição já existente. Neste 2o caso o formulário já terá recebido um array de strings que precisará ser preenchido nas textbox.

A entrada no form pode ter sido realizada através do click com o botão direito sobre uma coluna específica. Neste caso é provável que o usuário deseje criar um filtro para essa coluna, portanto podemos já colocar o foco nela.

Ao carregar o formulário precisaremos fazer os seguintes passos :

  • Verificar se o TipoObjeto encontra-se na hashTable de restrições, caso não esteja não é possível criar restrições.
  • Recuperar uma DataTable deste tipo de objeto
  • Criar N TextBox e Labels dinamicamente, sendo N o número indicado na hashTable
  • Comparar o nome criado para cada textBox com o nome de coluna recebido (se houver). Se for igual, dar o foco ao controle
  • Se foi recebido um array de strings, utiliza-lo para preencher as textBox
  • Redimensionar o form adequadamente

Veja como fica :

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

  343         Dim dt As DataTable

  344 

  345         DefinirRestricoes()

  346 

  347         If Not hsNumRestricoes.Contains(TipoObjeto) Then

  348             MsgBox("Este elemento não aceita restricoes")

  349             Me.DialogResult = DialogResult.Cancel

  350         End If

  351 

  352         cn.ConnectionString = Configuracao.Conexao

  353         cn.Open()

  354         dt = cn.GetOleDbSchemaTable(TipoObjeto, Nothing)

  355         cn.Close()

  356 

  357         For icnt As Integer = 0 To hsNumRestricoes(TipoObjeto) - 1

  358             Dim dc As DataColumn

  359             Dim l As New Label

  360             dc = dt.Columns(icnt)

  361 

  362             l.Text = dc.ColumnName

  363             l.Top = y

  364             l.Left = x1

  365             l.Width = 190

  366             Me.Controls.Add(l)

  367 

  368             Dim t As New TextBox

  369             t.Text = ""

  370             t.Name = dc.ColumnName

  371             t.Top = y

  372             t.Left = x2

  373             t.Width = 170

  374             Me.Controls.Add(t)

  375             arText.Add(t)

  376             If UCase(dc.ColumnName) = UCase(nomeColuna) Then

  377                 ControleFoco = t

  378             End If

  379             y += t.Height + 15

  380         Next

  381 

  382         If Not IsNothing(filtro) AndAlso filtro.Length > 0 Then

  383             For icnt As Integer = 0 To filtro.Length - 1

  384                 arText(icnt).text = filtro(icnt)

  385             Next

  386         End If

  387 

  388         Me.Height = y + GroupBox1.Height + 50

  389         Me.Top = 0

  390         Me.Width = 415

  391 

  392     End Sub

 

O código é bem simples, basicamente recursos de montagem dinâmica da interface.

O botão cancelar deste form não precisa ter código, pode apenas estar configurado com o dialogResult Cancel. Já o botão Ok, além de estar configurado com o DialogResult Ok precisará montar um array de strings para gerar o critério. Veja como fica :

  395     Private Sub cmdOk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdOk.Click

  396         ReDim filtro(arText.Count - 1)

  397         For a As Integer = 0 To arText.Count - 1

  398             If arText(a).text = "" Then

  399                 filtro(a) = Nothing

  400             Else

  401                 filtro(a) = arText(a).text

  402             End If

  403         Next

  404     End Sub

Observem que no form_load foi feito um array de TextBox justamente para simplificar este processamento. Se a textbox estiver vazia atribuimos Nothing no array, caso contrário atribuimos o conteúdo da textbox.

Feito isso precisamos controlar a chamada deste form de restrições a partir do form principal. Isso será feito com um item a mais no menu de contexto. Podemos também criar um ícone sobre a dataGrid, ícone que só apareça quando houverem filtros na dataGrid, e chamar este formulário a partir do duplo clique neste ícone. Ficamos então com 2 pontos de entrada, ambos deverão transmitir para o formulário de restrições o filtro existente, para que seja alterado.

Veja como fica :

  406     Private Sub PicFiltro_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles PicFiltro.DoubleClick

  407         Dim frm As New frmRestricoes

  408 

  409         exibirRestricoes(frm)

  410     End Sub

  411 

  412     Private Sub mnuRestricoes_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuRestricoes.Click

  413         Dim hit As DataGrid.HitTestInfo

  414         Dim frm As New frmRestricoes

  415         hit = dg.HitTest(pt)

  416         frm.nomeColuna = DirectCast(dg.DataSource, DataTable).Columns(hit.Column).ColumnName

  417 

  418 

  419         exibirRestricoes(frm)

  420     End Sub

  421 

  422     Private Sub exibirRestricoes(ByVal frm As frmRestricoes)

  423         frm.Configuracao = Me.configuracao

  424         frm.TipoObjeto = DirectCast(lstObjetos.SelectedItem, FieldInfo).GetValue(GetType(System.Data.OleDb.OleDbSchemaGuid))

  425 

  426         If hsFiltros.Contains(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name) Then

  427             frm.ItensFiltro = hsFiltros(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name)

  428         End If

  429         frm.ShowDialog()

  430         If frm.DialogResult = DialogResult.OK Then

  431             If hsFiltros.Contains(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name) Then

  432                 hsFiltros(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name) = frm.ItensFiltro

  433             Else

  434                 hsFiltros.Add(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name, frm.ItensFiltro)

  435             End If

  436         End If

  437         LerSchema(lstObjetos.SelectedItem)

  438     End Sub

Alguns detalhes são interessantes, devem ser observados :

  • Para evitar repetição de código, foi criada uma sub chamada exibirRestricoes com toda a parte comum do código.
  • Ao final da rotina de exibição do formulário, é chamada a rotina LerSchema, para fazer um "refresh" com os novos filtros.

A rotina LerSchema é a mesma que já haviamos criado anteriormente, mas agora verificando se existem ou não filtros para o item que está sendo lido. Veja como fica :

  440     Private Sub LerSchema(ByVal nome As FieldInfo)

  441         Dim dt As DataTable

  442         CN.Open()

  443         Try

  444             If hsFiltros.Contains(nome.Name) Then

  445                 dt = CN.GetOleDbSchemaTable(nome.GetValue(GetType(System.Data.OleDb.OleDbSchemaGuid)), hsFiltros(nome.Name))

  446                 PicFiltro.Visible = True

  447             Else

  448                 dt = CN.GetOleDbSchemaTable(nome.GetValue(GetType(System.Data.OleDb.OleDbSchemaGuid)), Nothing)

  449                 PicFiltro.Visible = False

  450             End If

  451         Catch er As Exception

  452             MsgBox("Este tipo de elemento não está disponível neste provider OLEDB" & vbCrLf & er.Message)

  453             Exit Sub

  454         Finally

  455             CN.Close()

  456         End Try

  457         If Not ds.Tables.Contains(dt.TableName) Then

  458             ds.Tables.Add(dt)

  459         Else

  460             ds.Tables(dt.TableName).Clear()

  461             ds.Merge(dt, False, MissingSchemaAction.Ignore)

  462         End If

  463     End Sub

Observe o sutil controle de exibição do ícone que indicará a existência de um filtro.

Por fim, para completarmos esta tarefa, precisamos garantir que esta hashTable será serializada e deserializada junto com o restante das configurações. Então teremos mais uma propriedade para gerar o nome do arquivo e precisaremos alterar as rotinas inicializarForm e SalvarConfig que já haviamos feito, adicionando um pequeno trecho para serializar e deserializar. Veja como fica :

  465     Private ReadOnly Property NomeFiltro() As String

  466         Get

  467             Return (Application.StartupPath & "\filtro" & configuracao.CodigoArquivo & ".bin")

  468         End Get

  469     End Property

 

InicializarForm :

   59         If File.Exists(NomeFiltro) Then

   60             Dim obj As New BinaryFormatter

   61             Dim st As New FileStream(NomeFiltro, FileMode.Open)

   62             hsFiltros = obj.Deserialize(st)

   63             st.Close()

   64         End If

SalvarConfig :

   59         If File.Exists(NomeFiltro) Then

   60             File.Delete(NomeFiltro)

   61         End If

   62 

   63         Dim obj2 As New BinaryFormatter

   64         Dim st2 As New FileStream(NomeFiltro, FileMode.Create)

   65         obj2.Serialize(st2, hsFiltros)

   66         st2.Close()

Com isso completamos a filtragem básica, então vamos agora para a criação da filtragem mais avançada, relacionando dois tipos de objetos.

A interface deverá ter o seguinte comportamento : O usuário visualiza um conjunto de dados, de um determinado tipo de objeto. Pega então outro tipo de objeto na listbox e arrasta até a dataGrid. Abre-se então a caixa de criação de filtros. Os campos listados com textboxes serão os do tipo de objeto que foi arrastado. Os dados deste tipo de objeto serão filtrados com base nos dados que estavam sendo exibidos na DataGrid (por exemplo, arrasta-se o tipo columns para cima da grid que exibia o tipo TableS_Info, as colunas serão filtradas com base no nome da tabela).

Os campos existentes na dataGrid deverão ser mostrados em uma listbox na tela de construção do filtro. As textbox ficarão readonly. O usuário irá arrastar os campos da listbox para sobre a textbox, indicando quais campos da grid servirão de critério em quais campos do outro tipo de objeto.

Concluida a montagem do critério, será criado um novo item no menu de contexto. Quando o usuário clicar sobre um registro da grid com o botão direito, terá a opção de disparar este novo item. Então, por exemplo, poderá clicar com o botão direito sobre uma tabela e pedir para ver os campos desta tabela. Sendo que toda essa relação será configurável pelo próprio usuário.

Com isso precisaremos criar uma adaptação em nosso frmRestricoes. Ele deverá ter 2 tipos de comportamento : Recebendo apenas uma identificação de campo ele terá o comportamento tradicional como montamos até agora, mas se receber 2 identificações de campo deverá exibir a listbox, definir as textbox como readonly e permitir o drag and drop.

Teremos um 2o Guid de tipo de objeto :

  457     Public _TipoObjetoDrop As System.Guid

  458 

  459     Public Property TipoObjetodrop() As System.Guid

  460         Get

  461             Return (_TipoObjetoDrop)

  462         End Get

  463         Set(ByVal Value As System.Guid)

  464             _TipoObjetoDrop = Value

  465         End Set

  466     End Property

 

O load do form, que faz todo o processamento de criação da interface, sofrerá sutis mudanças, observe :

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

  470         Dim dt As DataTable

  471 

  472         DefinirRestricoes()

  473 

  474         If Not hsNumRestricoes.Contains(TipoObjeto) Then

  475             MsgBox("Este elemento não aceita restricoes")

  476             Me.DialogResult = DialogResult.Cancel

  477         End If

  478 

  479         cn.ConnectionString = Configuracao.Conexao

  480         cn.Open()

  481         dt = cn.GetOleDbSchemaTable(TipoObjeto, Nothing)

  482         cn.Close()

  483 

  484         For icnt As Integer = 0 To hsNumRestricoes(TipoObjeto) - 1

  485             Dim dc As DataColumn

  486             Dim l As New Label

  487             dc = dt.Columns(icnt)

  488 

  489             l.Text = dc.ColumnName

  490             l.Top = y

  491             l.Left = x1

  492             l.Width = 190

  493             Me.Controls.Add(l)

  494 

  495             Dim t As New TextBox

  496             t.Text = ""

  497             t.Name = dc.ColumnName

  498             t.Top = y

  499             t.Left = x2

  500             t.Width = 170

  501             Me.Controls.Add(t)

  502             arText.Add(t)

  503             If UCase(dc.ColumnName) = UCase(nomeColuna) Then

  504                 ControleFoco = t

  505             End If

  506             If Not TipoObjetoDrop.Equals(TipoObjetoDrop.Empty) Then

  507                 AddHandler t.DragOver, AddressOf dragSobre

  508                 AddHandler t.DragDrop, AddressOf dragCaiu

  509                 t.ReadOnly = True

  510                 t.AllowDrop = True

  511             End If

  512             y += t.Height + 15

  513         Next

  514         If Not IsNothing(filtro) AndAlso filtro.Length > 0 Then

  515             For icnt As Integer = 0 To filtro.Length - 1

  516                 arText(icnt).text = filtro(icnt)

  517             Next

  518         End If

  519         Me.Height = y + GroupBox1.Height + 50

  520         Me.Top = 0

  521         Me.Width = 415

  522 

  523         If Not TipoObjetoDrop.Equals(TipoObjetoDrop.Empty) Then

  524             Dim dt2 As DataTable

  525             cn.Open()

  526             dt2 = cn.GetOleDbSchemaTable(TipoObjetoDrop, Nothing)

  527             cn.Close()

  528             Dim arc As New ArrayList

  529             arc.AddRange(dt2.Columns)

  530             lstCampos.DataSource = arc

  531             Me.Width = 736

  532         End If

  533     End Sub

Observe as mudanças :

A cada textbox criada verifica-se se o 2o tipo de objeto foi informado ou não. Se foi, altera-se a configuração da textbox para a 2a forma de atuação : vincula-se a textbox com o tratamento de drag-and-drop e define-se a textbox como readonly.

Ao final, mais uma vez se o 2o tipo de objeto houver sido informado, preenche-se uma listbox com os campos da dataTable deste 2o tipo de objeto.

Veja o código para habilitar o drag and drop :

  536     Private Sub lstCampos_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles lstCampos.MouseDown

  537         Dim pt2 As New Point(e.X, e.Y)

  538         If e.Button = MouseButtons.Right Then

  539             lstCampos.DoDragDrop(lstCampos.Items(lstCampos.IndexFromPoint(pt2)), DragDropEffects.Copy)

  540         End If

  541     End Sub

  542 

  543     Private Sub dragSobre(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DragEventArgs)

  544         e.Effect = DragDropEffects.Copy

  545     End Sub

  546 

  547     Private Sub dragCaiu(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DragEventArgs)

  548         DirectCast(sender, TextBox).Text = DirectCast(e.Data.GetData(GetType(DataColumn)), DataColumn).ColumnName

  549     End Sub

Nada muda na montagem do Array, mudará na forma como é utilizado, mas não em sua montagem.

Vejamos então como este form será disparado. Cada uma destas relações entre dois tipos de objetos irá gerar um item de menu. Então precisaremos de uma classe que represente cada item de menu. Veja como fica esta classe :

  551     <Serializable()> Public Class ItemMenu

  552 

  553         Public Campo As FieldInfo

  554 

  555         Public Filtros() As String

  556 

  557         Public ReadOnly Property NomeItem() As String

  558             Get

  559                 Return ("Detalhes de " & Campo.Name)

  560             End Get

  561         End Property

  562 

  563         Public Sub New(ByVal cmp As FieldInfo, ByVal flt() As String)

  564             Campo = cmp

  565             Filtros = flt

  566         End Sub


Observe que ela possui apenas um FieldInfo, a idéia é que esta classe será guardada de forma a estar ligada ao outro tipo de objeto, em uma relação pai-filho.

Precisaremos então de mais uma hashTable para guardar os itens de menu. Cada tipo de objeto poderá ter itens de menu. Assim sendo esta hashTable terá um elemento para cada tipo de objeto. Mas este elemento poderá conter vários itens de menu, afinal um mesmo objeto poderá ter vários itens de menu. Assim sendo cada elemento desta hashTable deverá ser uma nova hashTable. Vamos chamar essa hashTable de hsDetalhes.

Considerando isso, veja como fica este novo disparo do frmRestricoes :

  572     Private Sub lstObjetos_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles lstObjetos.MouseDown

  573         Dim pt2 As New Point(e.X, e.Y)

  574 

  575         If e.Button = MouseButtons.Right And lstObjetos.IndexFromPoint(pt2) <> -1 Then

  576             Dim r As DragDropEffects

  577             r = lstObjetos.DoDragDrop(lstObjetos.Items(lstObjetos.IndexFromPoint(pt2)), DragDropEffects.Move)

  578             Dim frm As New frmRestricoes

  579             frm.TipoObjeto = DirectCast(lstObjetos.Items(lstObjetos.IndexFromPoint(pt2)), FieldInfo).GetValue(GetType(System.Data.OleDb.OleDbSchemaGuid))

  580             frm.TipoObjetodrop = DirectCast(lstObjetos.SelectedItem, FieldInfo).GetValue(GetType(System.Data.OleDb.OleDbSchemaGuid))

  581             movido = lstObjetos.Items(lstObjetos.IndexFromPoint(pt2))

  582             exibirRestricoesCampos(frm)

  583         End If

  584     End Sub

  585 

  586     Private Sub exibirRestricoesCampos(ByVal frm As frmRestricoes)

  587         frm.Configuracao = Me.configuracao

  588 

  589 

  590         If hsDetalhes.Contains(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name) Then

  591             Dim arm As Hashtable

  592             arm = hsDetalhes(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name)

  593             If arm.Contains(frm.TipoObjetodrop) Then

  594                 frm.ItensFiltro = DirectCast(arm(frm.TipoObjetodrop), ItemMenu).Filtros

  595             End If

  596         End If

  597 

  598         frm.ShowDialog()

  599 

  600         If frm.DialogResult = DialogResult.OK Then

  601             If hsDetalhes.Contains(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name) Then

  602                 Dim arm As Hashtable

  603                 arm = hsDetalhes(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name)

  604                 If arm.Contains(frm.TipoObjetodrop) Then

  605                     arm(frm.TipoObjetodrop) = New ItemMenu(movido, frm.ItensFiltro)

  606                 Else

  607                     arm.Add(frm.TipoObjetodrop, New ItemMenu(movido, frm.ItensFiltro))

  608                 End If

  609                 hsDetalhes(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name) = arm

  610             Else

  611                 Dim arm As New Hashtable

  612                 arm.Add(frm.TipoObjetodrop, New ItemMenu(movido, frm.ItensFiltro))

  613                 hsDetalhes.Add(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name, arm)

  614             End If

  615         End If

  616         LerSchema(lstObjetos.SelectedItem)

  617     End Sub

 

Observe a sequencia de IFs controlando cuidadosamente o preenchimento dos arrayLists.

Mais uma vez precisaremos de código para serializar e deserializar, veja como fica :

 

  619     Private ReadOnly Property NomeDetalhes() As String

  620         Get

  621             Return (Application.StartupPath & "\detalhes" & configuracao.CodigoArquivo & ".bin")

  622         End Get

  623     End Property

SalvarConfig :

   59         If File.Exists(NomeDetalhes) Then

   60             File.Delete(NomeDetalhes)

   61         End If

   62 

   63         Dim obj3 As New BinaryFormatter

   64         Dim st3 As New FileStream(NomeDetalhes, FileMode.Create)

   65         obj2.Serialize(st3, hsDetalhes)

   66         st2.Close()

InicializarForm :

   59         If File.Exists(NomeDetalhes) Then

   60             Dim obj As New BinaryFormatter

   61             Dim st As New FileStream(NomeDetalhes, FileMode.Open)

   62             hsDetalhes = obj.Deserialize(st)

   63             st.Close()

   64         End If

Os itens de menu precisarão ser criados no menu de contexto sempre que este for exibido. Observe como fica :

  626     Private Sub ContextMenu1_Popup(ByVal sender As Object, ByVal e As System.EventArgs) Handles ContextMenu1.Popup

  627         If hsDetalhes.Contains(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name) Then

  628             Dim arm As Hashtable

  629             arm = hsDetalhes(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name)

  630             For icnt As Integer = ContextMenu1.MenuItems.Count - 1 To 2 Step -1

  631                 ContextMenu1.MenuItems.RemoveAt(icnt)

  632             Next

  633             For Each di As DictionaryEntry In arm

  634                 Dim m As New SubMenuItem

  635                 m.Text = DirectCast(di.Value, ItemMenu).NomeItem

  636                 m.menu = di.Value

  637                 AddHandler m.Click, AddressOf AberturaDetalhes

  638                 ContextMenu1.MenuItems.Add(m)

  639             Next

  640         End If

  641     End Sub

Observe que para não duplicar os itens de menu e garantir que estejam sempre atualizados foi necessário eliminar os itens e re-criados a cada vez que o menu é exibido. A criação dinâmica implica em atribuição dinâmica do tratador de evento, o que não é surpresa.

Observe que não usei a classe menuItem do .NET, mas sim uma classe chamada subMenuItem. Isso porque precisava lervar informações adicionais junto ao item de menu, então criei esta classe derivando de menuItem. Veja como ficou :

  643     Public Class SubMenuItem

  644         Inherits MenuItem

  645         Dim _menu As ItemMenu

  646 

  647         Public Property menu() As ItemMenu

  648             Get

  649                 Return _menu

  650             End Get

  651             Set(ByVal Value As ItemMenu)

  652                 _menu = Value

  653             End Set

  654         End Property

  655     End Class

Por fim, veja o tratamento do click no item :

  657     Private Sub AberturaDetalhes(ByVal sender As System.Object, ByVal e As System.EventArgs)

  658         Dim frm As New frmDetalhes

  659         frm.configuracao = Me.configuracao

  660         frm.filtros = DirectCast(sender, SubMenuItem).menu.Filtros

  661         frm.TipoObjeto = DirectCast(sender, SubMenuItem).menu.Campo

  662         frm.DadosFiltro = DirectCast(dg.DataSource, DataTable).Rows(dg.CurrentRowIndex)

  663         frm.ShowDialog()

  664     End Sub

Chamamos neste caso mais um form para que sejam exibidas as informações de detalhe. Vamos criar este novo form, frmDetalhes. Este novo form conterá apenas uma DataGrid.

  666     Public TipoObjeto As FieldInfo

  667     Public filtros() As String

  668     Public DadosFiltro As DataRow

  669     Public configuracao As frmConfig.config

  670 

  671     Dim flt() As String

  672 

  673 

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

  675         Dim dt As DataTable

  676         PrepararFiltros()

  677         CN.ConnectionString = configuracao.Conexao

  678         CN.Open()

  679         dt = CN.GetOleDbSchemaTable(TipoObjeto.GetValue(GetType(System.Data.OleDb.OleDbSchemaGuid)), flt)

  680         CN.Close()

  681         DG.DataSource = dt

  682     End Sub

  683 

  684     Sub PrepararFiltros()

  685         ReDim flt(filtros.Length - 1)

  686         For icnt As Integer = 0 To filtros.Length - 1

  687             If IsNothing(filtros(icnt)) Then

  688                 flt(icnt) = Nothing

  689             Else

  690                 flt(icnt) = DadosFiltro(filtros(icnt)).ToString

  691             End If

  692         Next

  693     End Sub

Repare que este novo form recebe uma dataRow, a datarow em que o cursor está na dataGrid inicial. Utiliza-se desta dataRow para montar os parâmetros que serão usados na obtenção dos novos dados. Assim sendo, por exemplo, ao clicar com o botão direito sobre uma tabela pode-se obter a lista de suas colunas.


Observações

  • Ao invés de fazer com que o frmRestrições tenha 2 comportamentos, poderia ser usada herança para montar um outro form para o 2o comportamento
  • A classe SubMenuItem e ItemMenu muito provavelmente poderiam ser uma única classe
  • Poderia ser utilizado o IsolatedStored para ser feita a serialização das informações, neste caso, se não me engano, as propriedades deveriam devolver Stream
  • As propriedades poderiam fazer uso de uma outra, mais genérica, que devolve-se apenas o caminho no qual os arquivos deverão ser gravados, simplificando futuras manutenções
  • Com certeza esse projeto tem alguns pequenos bugs, tenho certeza que você irá se divertir corrigindo-os

 

Conclusão

Utilizando o ADO.NET temos muitos recursos para obter informações sobre a estrutura da base de dados.

Dennes Torres
MCAD,MCSD,MCSE,MCDBA





Envie seus comentįrios sobre este artigo

Nome :

E-mail :

Comentários :


Avise-me quando houverem novos comentįrios nesta pįgina

Veja abaixo os comentários já enviados :

Nome : Mauro E-Mail : Silvajm@SAO.Bhoeringer-Ingelheim.com
Para mim parecer ser muito útil este artigo, preciso agora de uma forma genérica para retornar o script de um objeto, de uma procedure por exemplo. Se eu usar a procedure SP_HELPTEXT ficarei preso ao SQL Server. Existe algum método que retorna o script, independente do provider?
Nome : 1 E-Mail : -1'
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
-1'
Nome : -1' E-Mail : 1
1
Nome : 1 E-Mail : -1'
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1
Nome : 1 E-Mail : 1
1

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Conheça mais sobre o nosso site :

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::



Quer saber mais?
Faça um curso na Búfalo Informática, Treinamento e Consultoria e
Prepare-se para o Mercado!
Veja o que a Búfalo tem para você.

ļæ½ BĆŗfalo InformĆ”tica, Treinamento e Consultoria - Rua Ɓlvaro Alvim, 37 Sala 920 - CinelĆ¢ndia - Rio de Janeiro / RJ
Tel.: (21)2262-1368 (21) 9240-5134 (21) 9240-7281 e-Mail:
contato@bufaloinfo.com.br