Display SQL data in TreeView - vb.net

I am showing data from 1 table from SQL Server 2018 to a TreeView. I can only show the tree up to this point
Private Sub btnPopular_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPopular.Click
Dim conexaoSQLServer As SqlConnection = Nothing
Dim cmd As SqlCommand
Dim da As New SqlDataAdapter
Dim ds As New DataSet
'DBSQLServer = "SELECT RTRIM(D.DESCRIPCION),RTRIM(P.DESCRIPCION) " +
'"FROM PROVINCIAS AS P INNER JOIN DEPARTAMENTO AS D ON P.IDDEPARTAMENTO=D.IDDEPARTAMENTO"
Dim strCon As String = "Data Source = SOPORTE-ERP\SQLEXPRESS; Initial Catalog = prueba; Integrated Security = True"
'define a consulta para obter as tabelas e suas colunas
Dim sqlConsulta As String = "SELECT RTRIM(D.DESCRIPCION),RTRIM(P.DESCRIPCION) " +
"FROM PROVINCIA AS P INNER JOIN DEPARTAMENTO AS D ON P.IDDEPARTAMENTO=D.IDDEPARTAMENTO"
'define os nodes que iremos usar no treeview
Dim NoRaiz As TreeNode = Nothing
Dim NoPrincipal As TreeNode = Nothing
Dim NoFilho As TreeNode = Nothing
Dim nieto As TreeNode = Nothing
'define algumas constanes e variaveis usadas
Dim nomePrincipal As String = String.Empty
Dim nomeFilho As String = String.Empty
Dim BancoDados As String = DBSQLServer
Try
'define e abre a conexão com o SQL Server
conexaoSQLServer = New SqlConnection(strCon)
conexaoSQLServer.Open()
'atribui o comando usado na conexão
cmd = New SqlCommand(sqlConsulta, conexaoSQLServer)
da.SelectCommand = cmd
'preenche o dataset
da.Fill(ds, "DATOS_SISTEMAS")
tvwDados.Nodes.Clear()
'inclui o node raiz
NoRaiz = tvwDados.Nodes.Add("UBIGEO")
Dim contaTabelas As Integer = 0
For Each row As DataRow In ds.Tables("DATOS_SISTEMAS").Rows
If nomePrincipal <> row(0).ToString Then
NoPrincipal = NoRaiz.Nodes.Add(row(0).ToString)
'imageIndex:=1, selectedImageIndex:=1'
nomePrincipal = row(0).ToString
contaTabelas += 1
End If
NoFilho = NoPrincipal.Nodes.Add(key:="", text:=row(1).ToString)
'nieto = NoPrincipal.Nodes.Add(key:="DISTRITOS", text:=row(2).ToString)
'imageIndex:=2, selectedImageIndex:=2
Next
lblTabelas.Text = contaTabelas.ToString & " Tabelas no arquivo"
tvwDados.Nodes(0).EnsureVisible()
Catch ex As Exception
MessageBox.Show("Erro ao realizar a operação com o arquivo : " & ex.Message)
Exit Sub
Finally
'libera os recursos da conexão usada
conexaoSQLServer.Close()
conexaoSQLServer.Dispose()
conexaoSQLServer = Nothing
End Try
End Sub
And it shows me as follows in the picture
now I want another sub-tree or item to be broken down to me where the districts are shown as follows DEPARTMENT / PROVINCE / DISTRICT

In essence, this is a data querying question.
I use these conceptual tables for my answer. I hope they reflect your actual table structure.
CREATE TABLE DEPARTAMENTO (
IDDEPARTAMENTO CHAR(2) NOT NULL,
DESCRIPCION VARCHAR(50) NOT NULL,
CONSTRAINT PK_DEPARTAMENTO PRIMARY KEY (IDDEPARTAMENTO),
CONSTRAINT UQ_DEPARTAMENTO UNIQUE (DESCRIPCION)
);
CREATE TABLE PROVINCIA (
IDDEPARTAMENTO CHAR(2) NOT NULL,
IDPROVINCIA CHAR(2) NOT NULL,
DESCRIPCION VARCHAR(50) NOT NULL,
CONSTRAINT PK_PROVINCIA PRIMARY KEY (IDDEPARTAMENTO, IDPROVINCIA),
CONSTRAINT UQ_PROVINCIA UNIQUE (IDDEPARTAMENTO, DESCRIPCION),
CONSTRAINT FK_PROVINCIA_DEPARTAMENTO FOREIGN KEY (IDDEPARTAMENTO) REFERENCES DEPARTAMENTO (IDDEPARTAMENTO)
);
CREATE TABLE UBIGEO (
IDUBIGEO CHAR(6) NOT NULL,
IDDEPARTAMENTO CHAR(2) NOT NULL,
IDPROVINCIA CHAR(2) NOT NULL,
DESCRIPCION VARCHAR(50) NOT NULL,
CONSTRAINT PK_UBIGEO PRIMARY KEY (IDUBIGEO),
CONSTRAINT UQ_UBIGEO UNIQUE (IDDEPARTAMENTO, IDPROVINCIA, DESCRIPCION),
CONSTRAINT FK_UBIGEO_PROVINCIA FOREIGN KEY (IDDEPARTAMENTO, IDPROVINCIA) REFERENCES PROVINCIA (IDDEPARTAMENTO, IDPROVINCIA)
);
Next, I would look at the data that is actually required for populating a treeview. Every node refers to a parent node (or a parent node contains several child nodes). I would like to reflect that in the data that I retrieve from the database.
Each of the above three tables holds node data. Instead of using JOINs, I would use UNIONs here, so that each row in the result data represents a single node. Each row should contain at least three fields:
nodeText will contain the node text that is displayed in the treeview's nodes
nodeKey will contain a unique textual key for the node
nodeParentKey will contain the key of the parent node (or an empty string if it has no parent)
So the query that I would execute to retrieve the node data would look something like this:
'define a consulta para obter as tabelas e suas colunas
Dim sqlConsulta As String = "
SELECT
D.DESCRIPCION AS nodeText,
'DEPA' + D.IDDEPARTAMENTO AS nodeKey,
'' AS nodeParentKey
FROM
DEPARTAMENTO AS D
UNION ALL
SELECT
P.DESCRIPCION AS nodeText,
'PROV' + P.IDDEPARTAMENTO + P.IDPROVINCIA AS nodeKey,
'DEPA' + P.IDDEPARTAMENTO AS nodeParentKey
FROM
PROVINCIA AS P
UNION ALL
SELECT
U.DESCRIPCION AS nodeText,
'DIST' + U.IDUBIGEO AS nodeKey,
'PROV' + U.IDDEPARTAMENTO + U.IDPROVINCIA AS nodeParentKey
FROM
UBIGEO AS U
"
I would rewrite the loop that processes your retrieved data and actually convert it to two loops.
The first loop processes the retrieved data and creates the TreeNode objects. But instead of immediately putting them in a Nodes collection in the tvwDados treeview, I would put them in a helper dictionary. Additionally, I would store the node's key in another helper dictionary as well. These helper dictionaries will be used in the second loop.
The second loop actually populates the tvwDados treeview from the nodes dictionary and add the node either to the tvwDados.Nodes collection or a parent node's Nodes collection, which depends if a parent key can be found in the nodeParents dictionary.
My data processing code would look something like this:
'Helper dictionaries
Dim nodes As New Dictionary(Of String, TreeNode) 'Holds the nodes based on their key values
Dim nodeParents As New Dictionary(Of String, String) 'Holds the parent keys of child nodes
'Create nodes from data
For Each row As DataRow In ds.Tables("DATOS_SISTEMAS").Rows
Dim nodeText As String = row.Field(Of String)("nodeText")
Dim nodeKey As String = row.Field(Of String)("nodeKey")
Dim nodeParentKey As String = row.Field(Of String)("nodeParentKey")
nodes.Add(nodeKey, New TreeNode(nodeText))
If Not String.IsNullOrEmpty(nodeParentKey) Then
nodeParents.Add(nodeKey, nodeParentKey)
End If
Next
'Add nodes to treeview (and resolve parents)
For Each kvp In nodes
Dim node As TreeNode = kvp.Value
Dim nodeKey As String = kvp.Key
Dim nodeParentKey As String = Nothing
If nodeParents.TryGetValue(nodeKey, nodeParentKey) Then
'Child node
Dim parentNode As TreeNode = nodes(nodeParentKey)
parentNode.Nodes.Add(node)
Else
'Root node
tvwDados.Nodes.Add(node)
End If
Next
I am not sure about the purpose of your contraTabelas integer and your NoRaiz, NoPrincipal and NoFilho tree nodes. For the sake of clarity I omitted them from my code. I assume that you will be able to include them in the processing logic yourself if you still need them.
Edit:
If you want to do special things when double clicking a district node, you can pass additional data in a node's Tag property.
In the above code, change this line:
nodes.Add(nodeKey, New TreeNode(nodeText))
to this:
Dim node As New TreeNode(nodeText)
node.Tag = nodeKey
nodes.Add(nodeKey, node)
Next, you can implement the tree view's NodeMouseDoubleClick event handler. It could look something like this:
Private Sub tvwDados_NodeMouseDoubleClick(sender As Object, e As TreeNodeMouseClickEventArgs) Handles tvwDados.NodeMouseDoubleClick
Dim nodeKey As String = TryCast(e.Node.Tag, String)
If nodeKey IsNot Nothing AndAlso nodeKey.StartsWith("DIST") Then
'You have double clicked a district node
Dim IDDISTRITO As Integer = Integer.Parse(nodeKey.Substring(4))
'Do something with the district id here
'...
End If
End Sub
If you need help with additional subjects, just feel free to post an entirely new question here on StackOverflow.
Edit:
I have updated my answer based on new information. I have made changes to the conceptual table structure and the SQL query in the sqlConsulta variable. As far as I can determine, it should not produce duplicate key errors anymore.
I think that the duplicate key errors came from the province nodes. So for the province nodes, I included both the department ID and the province ID in the node key.

the structure of my database is the same as what you are proposing, executing the query with a small change shows me as follows.
SELECT D.DESCRIPCION AS nodetext,CAST(D.IDDEPARTAMENTO AS VARCHAR) AS nodeKey,'' AS nodeParentKey
FROM DEPARTAMENTO AS D UNION ALL
SELECT P.DESCRIPCION AS nodeText,CAST(P.IDPROVINCIA AS VARCHAR) AS nodeKey,CAST(P.IDDEPARTAMENTO AS VARCHAR) AS nodeParentKey
FROM PROVINCIA AS P UNION ALL
SELECT U.DESCRIPCION AS nodeText,CAST(U.IDUBIGEO AS VARCHAR) AS nodeKey,CAST(U.IDPROVINCIA AS VARCHAR) AS nodeParentKey
FROM ubigeo AS U SELECT * FROM ubigeo
UBIGEO IS A DISTRICT
AND IT SHOWS SO
when executing the code that you published it shows me this error message
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim conexaoSQLServer As SqlConnection = Nothing
Dim cmd As SqlCommand
Dim da As New SqlDataAdapter
Dim ds As New DataSet
DBSQLServer = "prueba"
Dim strCon As String = "Data Source = DESKTOP-2IM88ST\SQLEXPRESS; Initial Catalog = " & DBSQLServer & "; Integrated Security = True"
'define a consulta para obter as tabelas e suas colunas
Dim sqlConsulta As String = "SELECT D.DESCRIPCION AS nodeText,CAST(D.IDDEPARTAMENTO AS VARCHAR) AS nodeKey,'' AS nodeParentKey FROM DEPARTAMENTO AS D UNION ALL " +
"SELECT P.DESCRIPCION AS nodeText,CAST(P.IDPROVINCIA AS VARCHAR) AS nodeKey,CAST(P.IDDEPARTAMENTO AS VARCHAR) AS nodeParentKey FROM PROVINCIA AS P UNION ALL " +
"SELECT U.DESCRIPCION AS nodeText,CAST(U.IDUBIGEO AS VARCHAR) AS nodeKey,CAST(U.IDPROVINCIA AS VARCHAR) AS nodeParentKey FROM ubigeo AS U"
Try
'define e abre a conexão com o SQL Server
conexaoSQLServer = New SqlConnection(strCon)
conexaoSQLServer.Open()
'atribui o comando usado na conexão
cmd = New SqlCommand(sqlConsulta, conexaoSQLServer)
da.SelectCommand = cmd
'preenche o dataset
da.Fill(ds, "DATOS_SISTEMAS")
'Helper dictionaries
Dim nodes As New Dictionary(Of String, TreeNode) 'Holds the nodes based on their key values
Dim nodeParents As New Dictionary(Of String, String) 'Holds the parent keys of child nodes
'Create nodes from data
TreeView1.Nodes.Clear()
For Each row As DataRow In ds.Tables("DATOS_SISTEMAS").Rows
Dim nodeText As String = row.Field(Of String)("nodeText")
Dim nodeKey As String = row.Field(Of String)("nodeKey")
Dim nodeParentKey As String = row.Field(Of String)("nodeParentKey")
nodes.Add(nodeKey, New TreeNode(nodeText))
If Not String.IsNullOrEmpty(nodeParentKey) Then
nodeParents.Add(nodeKey, nodeParentKey)
End If
Next
'Add nodes to treeview (and resolve parents)
For Each kvp In nodes
Dim node As TreeNode = kvp.Value
Dim nodeKey As String = kvp.Key
Dim nodeParentKey As String = Nothing
If nodeParents.TryGetValue(nodeKey, nodeParentKey) Then
'Child node
Dim parentNode As TreeNode = nodes(nodeParentKey)
parentNode.Nodes.Add(node)
Else
'Root node
TreeView1.Nodes.Add(node)
End If
Next
Catch ex As Exception
MessageBox.Show("Erro ao realizar a operação com o arquivo : " & ex.Message)
Exit Sub
Finally
'libera os recursos da conexão usada
conexaoSQLServer.Close()
conexaoSQLServer.Dispose()
conexaoSQLServer = Nothing
End Try
End Sub
End Class
error when performing this operation: an element with the same key has already been added

Bart Hofland,
the query to verify, it shows me this information in the new database.
this is the ubigeo or district table

Related

with already added an item with the same key TREEVIEW vb.net and sql server

I have this error, and I'm not sure what's causing it:
an element with the same key has already been added
I am showing data in a treeview as follows: DEPARTMENT / PROVINCE / DISTRICT attachment code made in visual basic:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim conexaoSQLServer As SqlConnection = Nothing
Dim cmd As SqlCommand
Dim da As New SqlDataAdapter
Dim ds As New DataSet
Dim strCon As String = "Data Source = SOPORTE-ERP\SQLEXPRESS; Initial Catalog = prueba; Integrated Security = True"
'define a consulta para obter as tabelas e suas colunas
Dim sqlConsulta As String = "SELECT DESCRIPCION AS nodeText,'DEPA' + CAST(IDDEPARTAMENTO AS VARCHAR) AS nodeKey,''AS nodeParentKey FROM DEPARTAMENTO " +
"UNION ALL SELECT DESCRIPCION AS nodeText,'PROV' + CAST(IDPROVINCIA AS VARCHAR) AS nodeKey,'DEPA' + CAST(IDDEPARTAMENTO AS VARCHAR) AS nodeParentKey " +
"FROM PROVINCIAS UNION SELECT DESCRIPCION AS nodeText,'DIST' + CAST(IDUBIGEO AS VARCHAR) AS nodeKey," +
"'PROV' + CAST(IDPROVINCIA AS VARCHAR) AS nodeParentKey FROM UBIGEO"
Try
'define e abre a conexão com o SQL Server
conexaoSQLServer = New SqlConnection(strCon)
conexaoSQLServer.Open()
'atribui o comando usado na conexão
cmd = New SqlCommand(sqlConsulta, conexaoSQLServer)
da.SelectCommand = cmd
'preenche o dataset
da.Fill(ds, "DATOS_SISTEMAS")
'Helper dictionaries
Dim nodes2 As New Dictionary(Of String, TreeNode) 'Holds the nodes based on their key values
Dim nodeParents As New Dictionary(Of String, String) 'Holds the parent keys of child nodes
'Create nodes from data
For Each row As DataRow In ds.Tables("DATOS_SISTEMAS").Rows
Dim nodeText2 As String = row.Field(Of String)("nodeText")
Dim nodeKey2 As String = row.Field(Of String)("nodeKey")
Dim nodeParentKey2 As String = row.Field(Of String)("nodeParentKey")
nodes2.Add(nodeKey2, New TreeNode(nodeText2))
If Not String.IsNullOrEmpty(nodeParentKey2) Then
nodeParents.Add(nodeKey2, nodeParentKey2)
End If
Next
'Add nodes to treeview (and resolve parents)
For Each kvp In nodes2
Dim node1 As TreeNode = kvp.Value
Dim nodeKeys1 As String = kvp.Key
Dim nodeParentKeys1 As String = Nothing
If nodeParents.TryGetValue(nodeKeys1, nodeParentKeys1) Then
'Child node
Dim parentNode As TreeNode = nodes2(nodeParentKeys1)
parentNode.Nodes.Add(node1)
Else
'Root node
TreeView1.Nodes.Add(node1)
End If
Next
Catch ex As Exception
MessageBox.Show("error when performing this operation: " & ex.Message)
Exit Sub
Finally
'libera os recursos da conexão usada
conexaoSQLServer.Close()
conexaoSQLServer.Dispose()
conexaoSQLServer = Nothing
End Try
End Sub
I validated that the ID fields of each table are unique and autoincrementing. It seems to be an error with the dictionary. What am I doing wrong?
I suspect missing a Tree1.Nodes.Clear() is the main problem, but there may be other things as well, the question is missing some important info (data!), and the code is written in an outdated style. The result is I had to modernize as I went to find even this much, and I may still be missing something. See below, and take note of the commends I added:
'Separate DB access from event code!
Friend Module DB
Private connectString As String = "Data Source = SOPORTE-ERP\SQLEXPRESS; Initial Catalog = prueba; Integrated Security = True"
Public Function GetTreeNodeData() As DataTable
'multi-line string literals work now
Dim sql As String = "
SELECT *
FROM (
SELECT DESCRIPCION AS nodeText,'DEPA' + CAST(IDDEPARTAMENTO AS VARCHAR) AS nodeKey,''AS nodeParentKey
FROM DEPARTAMENTO
UNION ALL
SELECT DESCRIPCION AS nodeText,'PROV' + CAST(IDPROVINCIA AS VARCHAR) AS nodeKey,'DEPA' + CAST(IDDEPARTAMENTO AS VARCHAR) AS nodeParentKey
FROM PROVINCIAS
UNION
SELECT DESCRIPCION AS nodeText,'DIST' + CAST(IDUBIGEO AS VARCHAR) AS nodeKey,'PROV' + CAST(IDPROVINCIA AS VARCHAR) AS nodeParentKey
FROM UBIGEO
) t
ORDER BY t.NodeParentKey, t.NodeKey, t.NodeText"
Dim ds As New DataSet
Using cn As New SqlConnection(connectString), _
cmd As New SqlCommand(sql, cn), _
da As New SqlDataAdapter(cmd)
'preenche o dataset
da.Fill(ds, "DATOS_SISTEMAS") '.Fill() opens and closes automatically
End Using 'End Using closes the connection, **even if an exception is thrown!**
Return ds.Tables("DATOS_SISTEMAS")
End Function
End Module
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'Limit the scope of each try/catch, so the error message can be more meaningful/helpful
Dim nodeRecords As DataTable
Try
'Create nodes from data
nodeRecords = DB.GetTreeNodeData()
Catch ex As Exception
MessageBox.Show($"Error performing database operation:{vbCrLf}{ex.Message}")
Exit Sub
End Try
Dim nodes As New Dictionary(Of String, TreeNode)
Try
For Each row As DataRow In nodeRecords.Rows
Dim nodeKey As String = row.Field(Of String)("nodeKey")
Dim nodeText As String = row.Field(Of String)("nodeText")
Dim parentKey As String = row.Field(Of String)("nodeParentKey")
If Not nodes.ContainsKey(parentKey) Then
nodes.Add(parentKey, new TreeNode())
End If
If nodes.ContainsKey(nodeKey) Then
nodes(nodeKey).Text = nodeText
nodes(parentKey).Nodes.Add(nodes(nodeKey)) 'we know the parent exists, because we just created it if it was missing
Else
Dim node As New TreeNode(nodeText)
nodes(parentKey).Nodes.Add(node)
nodes.Add(nodeKey, node)
End If
Next row
Catch ex As Exception
MessageBox.Show($"Error building tree:{vbCrLf}{ex.Message}")
Exit Sub
End Try
TreeView1.SuspendLayout() 'suspend/resume layout avoids flickering as control is updated
TreeView1.Nodes.Clear() 'I suspect missing this is your main problem
For Each kvp As KeyValuePair(Of String, TreeNode) In nodes.Where(Function (n) n.Parent Is Nothing)
TreeView1.Nodes.Add(node1)
Next kvp
TreeView1.ResumeLayout()
End Sub

Issues on loop for retrieve the dynamic sql parameter values from dynamic textboxes

all. I am a new VB.NET beginner. I am facing the issue of how to pass the dynamic SQL parameter values from the dynamic textboxes to search the data. I had added control of dynamic textboxes and labels and would like to search the data on the database table based on the dynamic textboxes value inputted from the user. Currently, I can only able to search the data from 1 dynamic textbox value only.
I want all the values from the dynamic textboxes that the user had been entered can be retrieved, and search the data based on the SQL parameter variable name and value entered by the user.
Can someone give me some solutions on how to solve this problem? I had stuck on this problem for a few days. Thank you for all the help!
What I had tried:
Private Sub FilterData()
Dim count As Integer = 0
'filterdata for radiobutton
Try
For Each TextBox As TextBox In grp2.Controls.OfType(Of TextBox)()
For Each Label As Label In grp2.Controls.OfType(Of Label)()
Using connection As New SqlConnection("connectionString")
'user key in the SQL statement
sql = TextBox1.Text
Dim sql2 As String
sql2 = sql
Dim sp1 As String() = sql.Split(New String() {"where"}, StringSplitOptions.None)
sql = sp1(0) & " where " & Label.Text & " = #parameter or " & Label.Text & " =#parameter"
If (TextBox.Text <> "") Then
count += 1
For j As Integer = 0 To count - 1
Using cmd As New SqlCommand(sql, connection)
cmd.Parameters.AddWithValue("#parameter", TextBox.Text)
'cmd.Parameters.Add("#parameter", SqlDbType.NVarChar, 20).Value = TextBox.Text
connection.Open()
Dim dt As New DataTable()
Dim reader As SqlDataReader
reader = cmd.ExecuteReader()
dt.Load(reader)
DataGridView1.DataSource = dt
End Using
Next
Else
GetData()
End If
'cmd.Dispose()
connection.Close()
End Using
Next
Next
Catch ex As Exception
'MsgBox(ex.Message)
End Try
End Sub
Firstly, if you have a 1:1 correspondence between Labels and TextBoxes then you should not be using two nested For Each loops, because that is going to pair up each and every Label with each and every TextBox. What you should be doing is creating arrays and then using a single For loop to access the pairs of controls:
Dim labels = grp2.Controls.OfType(Of Label)().ToArray()
Dim textBoxes = grp2.Controls.OfType(Of TextBox)().ToArray()
For i = 0 To labels.getUpperBound(0)
Dim label = labels(i)
Dim textBox = textBoxes(i)
'...
Next
As for build the SQL and adding the parameters, I would tend to do it something like this:
Dim labels = grp2.Controls.OfType(Of Label)().ToArray()
Dim textBoxes = grp2.Controls.OfType(Of TextBox)().ToArray()
Dim criteria As New List(Of String)
Dim command As New SqlCommand
For i = 0 To labels.getUpperBound(0)
Dim label = labels(i)
Dim textBox = textBoxes(i)
Dim parameterName = "#" & label.Text.Replace(" ", "_")
criteria.Add($"[{label.Text}] = {parameterName}")
command.Parameters.AddWithValue(parameterName, textBox.Text)
Next
Dim sql = "SELECT * FROM MyTable"
If criteria.Any() Then
sql &= " WHERE " & String.Join(" OR ", criteria)
End If
command.CommandText = sql
I think that you should begin to separate UI and data logic here is an example of implementation:
First you have a table in database:
CREATE TABLE Customer (
Id INT IDENTITY (1, 1) PRIMARY KEY,
FirstName VARCHAR (255) NOT NULL,
LastName VARCHAR (255) NOT NULL,
Phone VARCHAR (25),
Email VARCHAR (255) NOT NULL,
Street VARCHAR (255),
City VARCHAR (50),
State VARCHAR (25),
ZipCode VARCHAR (5)
);
Then you create the underlying entity in VB. Net:
Public Class Customer
Public Property Id As Integer
Public Property FirstName As String
Public Property LastName As String
Public Property Phone As String
Public Property Email As String
Public Property Street As String
Public Property City As String
Public Property State As String
Public Property ZipCode As String
End Class
Data loader
Now you need a data access component that loads records to a list of this above entity here a nice implementation:
Imports System.Data.SqlClient
Public Class CustomerDataAccess
Public Property ConStr As String
Public Sub New(ByVal constr As String)
constr = constr
End Sub
Public Function GetCustomersByCriterias(constraints As Object) As List(Of Customer)
Dim query As String = "SELECT Id, FirstName, LastName, Phone, Email, Street, City, State, ZipCode
FROM [dbo].[Customer] "
Dim result = New List(Of Customer)()
Using con = New SqlConnection(ConStr)
Using cmd = con.CreateCommand()
cmd.CommandType = CommandType.Text
cmd.CommandText = query
'' here the magic to add dynamic criteria coming from constraints
cmd.ApplyConstraints(Of Customer)(constraints)
con.Open()
LoadCustomerData(cmd, result)
End Using
End Using
Return result
End Function
Private Sub LoadCustomerData(ByVal cmd As SqlCommand, ByVal result As List(Of Customer))
Using rdr = cmd.ExecuteReader()
Dim idIdx As Integer = rdr.GetOrdinal("Id")
Dim firstNameIdx As Integer = rdr.GetOrdinal("FirstName")
Dim lastNameIdx As Integer = rdr.GetOrdinal("LastName")
Dim phoneIdx As Integer = rdr.GetOrdinal("Phone")
Dim emailIdx As Integer = rdr.GetOrdinal("Email")
Dim streetIdx As Integer = rdr.GetOrdinal("Street")
Dim cityIdx As Integer = rdr.GetOrdinal("City")
Dim stateIdx As Integer = rdr.GetOrdinal("State")
Dim zipCodeIdx As Integer = rdr.GetOrdinal("ZipCode")
While rdr.Read()
Dim item = New Customer()
item.Id = rdr.GetValueOrDefault(Of Integer)(idIdx)
item.FirstName = rdr.GetValueOrDefault(Of String)(firstNameIdx)
item.LastName = rdr.GetValueOrDefault(Of String)(lastNameIdx)
item.Phone = rdr.GetValueOrDefault(Of String)(phoneIdx)
item.Email = rdr.GetValueOrDefault(Of String)(emailIdx)
item.Street = rdr.GetValueOrDefault(Of String)(streetIdx)
item.City = rdr.GetValueOrDefault(Of String)(cityIdx)
item.State = rdr.GetValueOrDefault(Of String)(stateIdx)
item.ZipCode = rdr.GetValueOrDefault(Of String)(zipCodeIdx)
result.Add(item)
End While
End Using
End Sub
End Class
Extensions methods
Below are extensions methods referenced above that do the magic you are looking for:
DataReader extensions to make it easy to read values from SalDataReader with Dbnull exfeptional case and casting
Module DataReaderExtenions
<Extension()>
Function GetValueOrDefault(Of T)(row As IDataRecord, fieldName As String) As T
Dim ordinal = row.GetOrdinal(fieldName)
Return row.GetValueOrDefault(Of T)(ordinal)
End Function
<Extension()>
Function GetValueOrDefault(Of T)(row As IDataRecord, ordinal As Integer) As T
Return (If(row.IsDBNull(ordinal), Nothing, row.GetValue(ordinal)))
End Function
<Extension()>
Function GetValueOrDefaultSqlite(Of T)(row As IDataRecord, fieldName As String) As T
Dim ordinal = row.GetOrdinal(fieldName)
Return row.GetValueOrDefault(Of T)(ordinal)
End Function
<Extension()>
Function GetValueOrDefaultSqlite(Of T)(row As IDataRecord, ordinal As Integer) As T
Return (If(row.IsDBNull(ordinal), Nothing, Convert.ChangeType(row.GetValue(ordinal), GetType(T))))
End Function
End Module
Command extensions that lets you extract criteria from an anonymous object values:
Imports System.Reflection
Imports System.Runtime.CompilerServices
Module CommandExtensions
<Extension()>
Function AddParameter(command As IDbCommand, name As String, value As Object) As IDataParameter
If command Is Nothing Then Throw New ArgumentNullException("command")
If name Is Nothing Then Throw New ArgumentNullException("name")
Dim p = command.CreateParameter()
p.ParameterName = name
p.Value = If(value, DBNull.Value)
command.Parameters.Add(p)
Return p
End Function
<Extension()>
Function ToDictionary(data As Object) As Dictionary(Of String, Object)
If TypeOf data Is String OrElse data.[GetType]().IsPrimitive Then Return New Dictionary(Of String, Object)()
Return (From [property] In data.[GetType]().GetProperties(BindingFlags.[Public] Or BindingFlags.Instance)
Where [property].CanRead
Select [property]).ToDictionary(Function([property]) [property].Name, Function([property]) [property].GetValue(data, Nothing))
End Function
<Extension()>
Sub ApplyConstraints(Of TEntity)(cmd As IDbCommand, constraints As Object)
If constraints Is Nothing Then Return
Dim dictionary = constraints.ToDictionary()
Dim whereClause = " WHERE "
For Each kvp In dictionary
Dim columnName = kvp.Key
Dim propertyName = kvp.Key
Dim prefix = "#"c
Dim value = kvp.Value
whereClause += $"{columnName} **like** {prefix}{propertyName} AND "
cmd.AddParameter(propertyName, value)
Next
If String.IsNullOrEmpty(whereClause) Then Return
cmd.CommandText += whereClause.Remove(whereClause.Length - 5, 5)
End Sub
End Module
Example:
After coded all these stuff now you can do the following:
Dim DataGridView1 As DataGridView = New DataGridView()
Dim ConStr As String = ConfigurationManager.ConnectionStrings("MyApp").ConnectionString
Dim dal As CustomerDataAccess = New CustomerDataAccess(ConStr)
Dim criterias = New With {.FirstName = "%James%", .LastName = "%Nadin%"}
DataGridView1.DataSource = dal.GetCustomersByCriterias(criterias)
Despite all this code you are still need to bind your textbox (after naming them correctly) to a SearchEntity and use this entity to provide criterias
I hope this material can help you tackle your issue and incite you to improve your architecture & dev skills

add listbox values to access database windows form application

I have a calculator with two textboxes where the user puts a number in each one. They then click a plus, minus, divide, or multiply button and it does that function to the numbers. These numbers are saved to a listbox called listbox1. When the user clicks to display the results, the listbox is populated with all their saved values, and the application is SUPPOSED to save the listbox items to an access database. it is not working. Below is my code, where numFirst is the name of a category in the database table:
Private Sub btnDisplay_Click(sender As Object, e As EventArgs) Handles
btnDisplay.Click
ListBox1.Items.Clear()
For arrayindex As Integer = 0 To intarrayCount - 1
ListBox1.Items.Add(Input(arrayindex))
ListBox1.Text = Convert.ToString(ListBox1.Items.Item(arrayindex))
Next arrayindex
Dim query As String = "SELECT * FROM wk6"
Dim connectionString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\helse_000\Desktop\APU\VB Advanced\week4\ENTD461_wk4_Andrew_Helsel\ENTD461_wk2_Andrew_Helsel\calculator.mdb"
Dim command As OleDbCommand = New OleDbCommand
Dim var1 As String = Convert.ToString(ListBox1.Items.Item(0))
command.CommandText = "INSERT into wk6 (numFirst) VALUES (" + var1 + ")"
Figured it out, removed the select all query string and made my textbox fields into parameters after modifying their results a bit to fit the format I needed for the table.
For i As Integer = 0 To ListBox1.Items.Count - 1
Dim firstString As String = Convert.ToString(ListBox1.Items.Item(i))
Dim leftPart As String = firstString.Split(Convert.ToChar
(strButtonSelected))(0)
Dim rightPart As String = firstString.Split(Convert.ToChar("="))(1)
Dim a As Integer = firstString.IndexOf(strButtonSelected)
Dim b As Integer = firstString.IndexOf("=")
Dim secNum = firstString.Substring(a + 1, b - 4)
secNum = secNum.Trim
Dim conn As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=C:\Users\helse_000\Desktop\APU\VB Advanced\week4\ENTD461_wk4_Andrew_Helsel\ENTD461_wk2_Andrew_Helsel\calculator.mdb")
Dim command As OleDbCommand = New OleDbCommand
Dim cmdstring As String = "INSERT INTO wk6 (numFirst, numSecond, Operator, equals, Answer)" + " VALUES (#firstName,#lastName,#Operator,#equals,#answer)"
command = New OleDbCommand(cmdstring, conn)
command.Parameters.AddWithValue("#firstName", leftPart)
command.Parameters.AddWithValue("#lastName", secNum)
command.Parameters.AddWithValue("#Operator", strButtonSelected)
command.Parameters.AddWithValue("#equals", "=")
command.Parameters.AddWithValue("#answer", rightPart)
conn.Open()
command.ExecuteNonQuery()
conn.Close()

Populating a treeview with two tables using vb.net

Hello I have this two tables:
Then I want this result using treeview:
id department category
1 MIS System
1 MIS Networking
1 MIS Programmer
2 Audit Operations
2 Audit DS
3 HRD PA
3 HRD PayBen
3
HRD PS
3 HRD PLD
4 Acounting Sup
4 Acounting FMCG
5 Procurement NULL
or like this
MIS
-System
-Networking
-Programmer
AUDIT
-Operations
-DS
HRD
-PA
-PayBen
-PS
-PLD
Acounting
-Sup
-FMCG
Can someone please guide me, thank you. I'm having trouble finding any solution on the internet and I'm new to the vb.net language.
In my code below, i give you the logic. You just need to query your tables now.
Tell me if you need explanations.
TreeView1.Nodes.Clear()
'Create a first node and select it
Dim root As TreeNode = TreeView1.Nodes.Add("Test")
TreeView1.SelectedNode = root
'Create two types of node
Dim department As New TreeNode()
Dim category As New TreeNode()
'Daos (if you create a Department class and subDepartment class)
Dim _daoD As New Departments(_cnx)
Dim _daoSD As New subDepartments(_cnx)
'Lists (depending on classes too)
Dim listD As List(Of Department)
Dim listSD As List(Of subDepatment)
For Each dep As Department In listD
'Add a Tree node for a new department
department = New TreeNode(dep.department)
department.Tag = dep.id
root.Nodes.Add(department)
TreeView1.SelectedNode = departement
For Each subDep As subDepartment In listSubDep
'Add a TreeNode for new categories
categ = New TreeNode(subDep.category)
categ.Tag = subDep.id
Nodes.Add(categ)
Next
Next
Then, you can create 4 classes (the first one with properties, and the second one to query in the table)
Public Class Department
'properties
Public Property id As Integer
Get
Return _ID
End Get
Set(ByVal Value As Integer)
_ID = Value
End Set
End Property
'etc
End Class
Public Class Departments
Dim _cnx As OracleConnection (if you use Oracle)
Public Sub New(ByVal pcnx As OracleConnection)
_cnx = pcnx
End Sub
'Your queries
End Class
Here is my solutio. i hope this will help someone who have this kind of problem.
Dim myConnString As String = "Data Source=name;Initial Catalog=database;Integrated Security=True"
Dim daMyName As New SqlDataAdapter
Dim dsMyName As New DataSet
Dim recCount As Integer
Dim mySelectQuery As String = "SELECT DEPARTMENT.department, subDept.category from subdept right join department on .department.id = subDept.parent"
Dim myConnection As New SqlConnection(myConnString)
Dim myCommand As New SqlCommand(mySelectQuery, myConnection)
myConnection.Open()
daMyName.SelectCommand = myCommand
daMyName.Fill(dsMyName)
Dim tbl As DataTable = dsMyName.Tables(0)
recCount = dsMyName.Tables(0).Rows.Count
tvw1.Nodes.Clear() 'Clear any nodes so we don't load a tree into a tree
Dim nd As TreeNode
nd = tvw1.Nodes.Add("DEPARTMENT")
Dim parentRow As DataRow
Dim rowNdx As Integer
rowNdx = 0
daMyName.Fill(tbl)
Dim Jan As String
For Each parentRow In tbl.Rows
If rowNdx = recCount Then
Exit For
End If
Jan = dsMyName.Tables(0).Rows(rowNdx)("Department").ToString() 'set the next name
Dim Dnode As TreeNode
Dnode = tvw1.Nodes.Add(dsMyName.Tables(0).Rows(rowNdx)("department").ToString())
Dim cnode As TreeNode
cnode = New TreeNode
Do While dsMyName.Tables(0).Rows(rowNdx)("department").ToString() = Jan And rowNdx < recCount
'if it changes we need to kick out of Do While
cnode = Dnode.Nodes.Add(dsMyName.Tables(0).Rows(rowNdx)("category").ToString())
rowNdx = rowNdx + 1
If rowNdx = recCount Then
Exit Do
End If
Loop
Next parentRow
myConnection.Close()

tie primary key from table to checkedlistbox items to prevent issues with table changes

I have a checkedlistbox that is populated from the records in a table in my database. I'm able to create the check boxes dynamically on form load, and also save the user's selections on close. The problem is if a record is deleted in the database, then the users selections change because there's nothing about the checkedlistbox items that tie them to the primary key on the table. I'm storing the user's selections as an arraylist collection type user setting.
How can I store both the name and the primary key so that when the database changes, it doesn't effect a user's selections?
Here's my code:
Public Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim conn As New ADODB.Connection()
Dim database As String = "mydatabase"
conn.CommandTimeout = 50400 '14 hours
conn.Open("Provider=myprovider;Data Source=myserver;Initial Catalog=" & database & ";Integrated Security=SSPI")
conn.CursorLocation = ADODB.CursorLocationEnum.adUseClient
Dim rs As New ADODB.Recordset()
Dim Sql As String
Sql = "SELECT ProcessID, ProcessName FROM mytable WHERE is_active = 1"
rs.Open(Sql, conn)
Dim cnt As Integer
If rs.BOF = True And rs.EOF = True Then
cnt = 0
Else
rs.MoveFirst()
cnt = rs.RecordCount
End If
Dim i As Integer
i = 0
While i < cnt
CheckedListBox1.Items.Add(rs.Fields(1).Value)
i = i + 1
rs.MoveNext()
End While
'get user settings
Me.email.Checked = My.Settings.email
Me.pop.Checked = My.Settings.pop
Dim index As Integer
For Each item As String In My.Settings.selectedlistbox
index = CheckedListBox1.Items.IndexOf(item)
If index = -1 Then
CheckedListBox1.SetItemChecked(item, True)
Else
CheckedListBox1.SetItemChecked(item, False)
End If
Next
End Sub
Use a simple class and override the ToString function:
Private Class ProcessItem
Public Property ProcessID As Integer = 0
Public Property ProcessName As String = String.Empty
Public Overrides Function ToString() As String
Return Me.ProcessName
End Function
End Class
Then you can add your information:
CheckedListBox1.Items.Add(New ProcessItem With { _
.ProcessID = rs.Fields(0).Value, _
.ProcessName = rs.Fields(1).Value})