I am trying to edit customers using my C# project to learn how to implement this code in VB.net. The project was successful in adding customers, but when it got to editing customers, things got a bit more complicated because I needed to get the id of the row selected and pass it to a string. I have the MainForm.vb which could open CustomerForm.vb when I click on 'Edit'. When CustomerForm.vb opens, it failed to show the customer's info. I am not sure if FillDataTable() function is necassary, I would like to know if there is an improvement to this.
CustomerForm.vb
Imports System.Data.SqlClient
Public Class CustomerForm
Private objConnection As SqlConnection
Public objDataTable As DataTable = New DataTable()
Public objSqlCommand As SqlCommand = New SqlCommand()
Public editMode As Boolean = False
Public id As String = ""
Public FName As String = ""
Public LName As String = ""
Public PhNumber As String = ""
Public ReadOnly Property Connection As SqlConnection
Get
Return Me.objConnection
End Get
End Property
Dim CS As String = ("server=CASHAMERICA;Trusted_Connection=yes;database=ProductsDatabase2; connection timeout=30")
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
Try
If editMode = False Then
If txtFirstName.Text = "" Then
MessageBox.Show("First Name Is required")
ElseIf txtLastName.Text = "" Then
MessageBox.Show("Last Name Is required")
Else
Dim con As New SqlConnection(CS)
con.Open()
Dim cmd As New SqlCommand("INSERT INTO Customers(FIrstName,LastName,PhoneNumber) VALUES(#FName, #LName, #PhNumber)", con)
cmd.Parameters.AddWithValue("#FName", txtFirstName.Text)
cmd.Parameters.AddWithValue("#LName", txtLastName.Text)
cmd.Parameters.AddWithValue("#PhNumber", txtPhoneNumber.Text)
cmd.ExecuteNonQuery()
MessageBox.Show("Customer has been added!")
con.Close()
End If
ElseIf editMode = True Then
If txtFirstName.Text = "" Then
MessageBox.Show("First Name Is required")
ElseIf txtLastName.Text = "" Then
MessageBox.Show("Last Name Is required")
Else
Dim con As New SqlConnection(CS)
con.Open()
Dim cmd As New SqlCommand("UPDATE Customers SET FIrstName = #FName, LastName = #LName, PhoneNumber = #PhNumber WHERE Id = #Id", con)
cmd.Parameters.AddWithValue("#Id", id)
cmd.Parameters.AddWithValue("#FName", txtFirstName.Text)
cmd.Parameters.AddWithValue("#LName", txtLastName.Text)
cmd.Parameters.AddWithValue("#PhNumber", txtPhoneNumber.Text)
cmd.ExecuteNonQuery()
MessageBox.Show("Customer has been edited.")
con.Close()
End If
End If
Catch ex As Exception
MessageBox.Show(Me, ex.Message, "Save Failed", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
Me.Close()
End Sub
Private Sub CustomerForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If editMode = False Then
GetId()
Else
textId.Text = id
txtFirstName.Text = FName
txtLastName.Text = LName
txtPhoneNumber.Text = PhNumber
End If
End Sub
Private Sub GetId()
Dim con As New SqlConnection(CS)
con.Open()
Dim cmd As New SqlCommand("SELECT MAX(id)+1 FROM Customers")
textId.Text = FillDataTable().Rows(0)(0).ToString()
If textId.Text = "" Then
textId.Text = "1"
End If
End Sub
Public Property CommandText As String
Get
Return CommandText
End Get
Set(ByVal value As String)
CommandText = value
End Set
End Property
Public Sub OpenDBConnection()
objConnection = New SqlConnection(CS)
objConnection.Open()
End Sub
Public Sub CreateCommandObject()
objSqlCommand = objConnection.CreateCommand()
objSqlCommand.CommandText = CommandText
objSqlCommand.CommandType = CommandType.Text
End Sub
Public Function FillDataTable() As DataTable
objDataTable = New DataTable()
objSqlCommand.CommandText = CommandText
objSqlCommand.Connection = objConnection
objSqlCommand.CommandType = CommandType.Text
objDataTable.Load(objSqlCommand.ExecuteReader())
objConnection.Close()
Return objDataTable
End Function
Public Sub CloseConnection()
If objConnection IsNot Nothing Then objConnection.Close()
End Sub
End Class
MainForm.vb
Imports System.Windows.Forms.LinkLabel
Public Class MainForm
Private Sub ExitToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ExitToolStripMenuItem.Click
Me.Close()
End Sub
Private Sub AddToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles AddToolStripMenuItem.Click
CustomerForm.ShowDialog()
End Sub
Private Sub AddToolStripMenuItem1_Click(sender As Object, e As EventArgs) Handles AddToolStripMenuItem1.Click
ProductForm.ShowDialog()
End Sub
Private Sub ViewProductsToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ViewProductsToolStripMenuItem.Click
ManageProductsForm.ShowDialog()
End Sub
Private Sub AboutToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles AboutToolStripMenuItem.Click
AboutForm.Show()
End Sub
Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the 'ProductsDatabase2DataSet1.Customers' table. You can move, or remove it, as needed.
Me.CustomersTableAdapter.Fill(Me.ProductsDatabase2DataSet1.Customers)
End Sub
Private Sub gridCustomers_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles gridCustomers.MouseUp
Try
Dim f As CustomerForm = New CustomerForm()
f.editMode = True
Dim rowIndex As Integer = gridCustomers.CurrentCell.RowIndex
f.id = gridCustomers.Rows(rowIndex).Cells("colId").Value.ToString()
f.FName = gridCustomers.Rows(rowIndex).Cells("colFirstName").Value.ToString()
f.LName = gridCustomers.Rows(rowIndex).Cells("colLastName").Value.ToString()
f.PhNumber = gridCustomers.Rows(rowIndex).Cells("colPhoneNumber").Value.ToString()
f.Show()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub gridCustomers_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles gridCustomers.CellContentClick
Dim f As CustomerForm = New CustomerForm()
f.ShowDialog(Me)
f.editMode = True
End Sub
End Class
Error Message:
Customer Info not displayed:
As mentioned, it seems like you have several things potentially going on.
The first thing I would change is your public sql objects. You don't need them to be public. It also increases the chances for issues if you're using the same objects in multiple places.
SqlCommand, SqlConnection, and SqlDataAdapters can be immediately disposed of (in most cases) as soon as they are used.
One easy method to handling all of that is to throw everything inside of a USING block.
Dim sqlResult As New Datatable()
Using cn As New SqlConnection("Connection String here"),
cmd As New SqlCommand(sql, cn),
adapt As New SqlDataAdapter(cmd)
cn.Open()
'Handle stored procedure vs select options
If sql.Contains("SELECT") Then
cmd.CommandType = CommandType.Text
Else
cmd.CommandType = CommandType.StoredProcedure
End If
'If parameters passed, else comment out
cmd.Parameters.AddWithValue("#parameterName", parameterValue)
sqlResult = New DataTable(cmd.CommandText)
adapt.Fill(sqlResult)
End Using
Now all your objects are disposed of as soon as your datatable is filled.
*This example uses a datatable, but it is the same idea for anything interacting with sql objects.
If implemented, you may find out that some objects weren't being disposed of or initialized properly.
Another method for testing where things are going wrong is to query the states of the SQL objects before or around the error to see if something isn't set correctly or is uninitialized.
Related
I'm pretty sure this code used to allow me to login using the SQL statement below, but now it doesn't and just clears the text boxes.
There is also an error message that I would like displayed when the username and password do not match that in the database. This is, like the other one, a label that the visibility would need changing. I had this code:
Try
UserType = GetUserType(txtUsername.Text.Trim, txtPass.Text.Trim)
Catch ex As Exception
lblErrorMatch.Visible = True
End Try
However, this just clears the text boxes when the details do not match.
Imports System.Data.OleDb
Public Class frmLogin
Private DBCmd As New OleDbCommand
Private ConStr As String = "Provider=Microsoft.ACE.OLEDB.12.0;" &
"Data Source=|DataDirectory|\NewHotel.mdb;"
Private Sub btnLogIn_Click(sender As Object, e As EventArgs) Handles btnLogIn.Click
Dim UserType As Object
'Distinguishing beetween different users
If UserType Is Nothing Then
lblErrorEmpty.Visible = True
ElseIf UserType.ToString = "MANAGER" Then
frmHomeManager.Show()
ElseIf UserType.ToString = "RECEPTIONIST" Then
frmHomeReceptionist.Show()
End If
'Username and password not matching error message
'Empty textboxes error message
If txtUsername.Text = "" OrElse txtPass.Text = "" Then lblErrorEmpty.Visible = True : Exit Sub
txtUsername.Clear()
txtPass.Clear()
chkShowPass.Checked = False
End Sub
Private Function GetUserType(Username As String, Pass As String) As Object
Dim UserType As Object
'Pull username and password from the database to check against the entered login details
Using DBCon As New OleDb.OleDbConnection(ConStr),
DBCmd As New OleDbCommand("SELECT UserType FROM tblEmployeeLogin WHERE [Username] = #Username AND [Pass] = #Pass", DBCon)
DBCmd.Parameters.Add("#Username", OleDbType.VarChar).Value = Username
DBCmd.Parameters.Add("#Pass", OleDbType.VarChar).Value = Pass
DBCon.Open()
UserType = DBCmd.ExecuteScalar.ToString
End Using
Return UserType
End Function
Private Sub chkShowPass_CheckedChanged(sender As Object, e As EventArgs) Handles chkShowPass.CheckedChanged
'Show password when checkbox is checked
If chkShowPass.Checked = True Then
txtPass.PasswordChar = Nothing
ElseIf chkShowPass.Checked = False Then
txtPass.PasswordChar = "*"
End If
End Sub
Private Sub txtUsername_TextChanged(sender As Object, e As EventArgs) Handles txtUsername.TextChanged, txtPass.TextChanged
'Make error message disappear after text is enetered into either of the text boxes
lblErrorEmpty.Visible = False
lblErrorMatch.Visible = False
End Sub
Private Sub lnkClear_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs) Handles lnkClear.LinkClicked
txtUsername.Clear()
txtPass.Clear()
chkShowPass.Checked = False
End Sub
Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click
'Close the form down which stops the application
Me.Close()
End Sub
End Class
Thank you for your time :)
I recently made a login like what you're trying to do. Hopefully you understand this code and it helps:
Dim CMD As OleDbCommand = New OleDbCommand("SELECT * FROM Staff WHERE Login = '" & ID & "'", Con)
Dim user As String
Try
Con.Open()
Dim sdr As OleDbDataReader = CMD.ExecuteReader()
If (sdr.Read() = True) Then
user = sdr("Login")
MessageBox.Show("Login Successful!")
ActiveUser.IsLoggedIn = True
Else
MessageBox.Show("Invalid username or password!")
End If
Catch ex As Exception
MsgBox(ex.Message)
Finally
Con.Close()
End Try
Im doing a school project. and I was testing a login form for my app. I'm trying separately from my login form and a profile pic form. I have successfully managed to save the image to the access database but I have had quite a few problems trying to display it on a textbox on my form.
This is the whole app code:
Imports System.Data.OleDb
Imports System.IO
Public Class Form2
Dim con As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\geral\source\repos\BD de imagenes\BD de imagenes\DBImagenes.mdb")
Dim cmd As New OleDbCommand
Dim sql As String
Dim da As New OleDb.OleDbDataAdapter
Dim result As Integer
Private Sub saveimage(sql As String)
Try
Dim arrimage() As Byte
Dim mstream As New System.IO.MemoryStream
PictureBox1.Image.Save(mstream, System.Drawing.Imaging.ImageFormat.Png)
arrimage = mstream.GetBuffer()
Dim Filesize As UInt32
Filesize = mstream.Length
mstream.Close()
con.Open()
cmd = New OleDbCommand
With cmd
.Connection = con
.CommandText = sql
.Parameters.AddWithValue("#Imagen", arrimage)
.Parameters.Add("#Nombre", OleDbType.VarChar).Value = TextBox1.Text
.ExecuteNonQuery()
End With
Catch ex As Exception
MsgBox(ex.Message)
Finally
con.Close()
End Try
End Sub
'End Try
Public conex As New OleDbConnection()
Public Sub conexion()
conex.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\geral\source\repos\BD de imagenes\BD de imagenes\DBImagenes.mdb"
conex.Open()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles BTNGuardar.Click
sql = "Insert into TBImg (Imagen, Nombre) Values (#Imagen, #Nombre)"
'sql = "Insert into TBImg (Imagen) Values (#Imagen)"
saveimage(sql)
MsgBox("Image has been saved in the database")
End Sub
Private Sub BtnExaminar_Click(sender As Object, e As EventArgs) Handles BtnExaminar.Click
OpenFileDialog1.Filter = "Imagenes JPG|*.jpg|Imagenes PNG|*.png"
OpenFileDialog1.RestoreDirectory = True
If OpenFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
PictureBox1.Image = Image.FromFile(OpenFileDialog1.FileName)
End If
End Sub
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
End Sub
Private Sub PictureBox1_Click(sender As Object, e As EventArgs) Handles PictureBox1.Click
Try
With OpenFileDialog1
'CHECK THE SELECTED FILE IF IT EXIST OTHERWISE THE DIALOG BOX WILL DISPLAY A WARNING.
.CheckFileExists = True
'CHECK THE SELECTED PATH IF IT EXIST OTHERWISE THE DIALOG BOX WILL DISPLAY A WARNING.
.CheckPathExists = True
'GET AND SET THE DEFAULT EXTENSION
.DefaultExt = "jpg"
'RETURN THE FILE LINKED TO THE LNK FILE
.DereferenceLinks = True
'SET THE FILE NAME TO EMPTY
.FileName = ""
'FILTERING THE FILES
.Filter = "(*.jpg)|*.jpg|(*.png)|*.png|(*.jpg)|*.jpg|All files|*.*"
'SET THIS FOR ONE FILE SELECTION ONLY.
.Multiselect = False
'SET THIS TO PUT THE CURRENT FOLDER BACK TO WHERE IT HAS STARTED.
.RestoreDirectory = True
'SET THE TITLE OF THE DIALOG BOX.
.Title = "Select a file to open"
'ACCEPT ONLY THE VALID WIN32 FILE NAMES.
.ValidateNames = True
If .ShowDialog = DialogResult.OK Then
Try
PictureBox1.Image = Image.FromFile(OpenFileDialog1.FileName)
Catch fileException As Exception
Throw fileException
End Try
End If
End With
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Exclamation, Me.Text)
End Try
End Sub
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
conexion()
PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage
End Sub
Private Sub Label1_Click(sender As Object, e As EventArgs) Handles Label1.Click
End Sub
Private Sub BtnBuscar_Click(sender As Object, e As EventArgs) Handles BtnBuscar.Click
Dim arrimage() As Byte
Dim conn As New OleDb.OleDbConnection
Dim Myconnection As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\geral\source\repos\BD de imagenes\BD de imagenes\DBImagenes.mdb"
conn.ConnectionString = Myconnection
conn.Open()
sql = "Select * from TBImg where Nombre=" & (TBBuscar.Text)
Dim cmd As New OleDbCommand
With cmd
.Connection = conex
.CommandText = sql
End With
Dim publictable As New DataTable
Try
da.SelectCommand = cmd
da.Fill(publictable)
TextBox1.Text = publictable.Rows(1).Item("Nombre").ToString
arrimage = publictable.Rows(1).Item("Imagen")
Dim mstream As New System.IO.MemoryStream(arrimage)
PictureBox1.Image = Image.FromStream(mstream)
Catch ex As Exception
MsgBox(ex.Message)
Finally
da.Dispose()
conn.Close()
End Try
End Sub
End Class
the relevant part is at Private Sub BtnBuscar_Click.
I'm trying to search in a textbox for the name that I saved the image with. but I haven't had success all I get is the error of the title.
this is how my database looks like the images are saved as an ole object
This is the error I get
I was following this tutorial https://www.youtube.com/watch?v=zFdjp39mfhQ
but he didn't quite explain how to use the:
TextBox1.Text = publictable.Rows(0).Item(1)
arrimage = publictable.Rows(0).Item(1)'
don't know if it's the cause of the issue.
instructions. The reason why my code looks different is that I was trying to stuff to see if I could make it work.
I have tried to search for answers and people suggest that I may have put the table name wrong or the column but I copied the name exactly how it is in the table with ctrl + c and ctrl + v.
what I want is that when I type the name in the column name of the database that it brings the designated picture stored as ole object onto my desired picture box on my form app.
Needless to say, I'm not that experienced with vb.net and SQL, Acces. I'm just following tutorials for being able to complete the project.
Do not declare connections or commands or datareaders at the class level. They all need to have their Dispose methods called. Using blocks will have the declare, closing and disposing even if there is an error. Streams also need Using blocks.
Defaults for an OpenFiledialog
Multiselect is False
CheckFileExists is True
CheckPathExists is True
DereferenceLinks is True
ValidateNames is True
FileName is ""
Unless you are getting paid by the line, it is unnecessary to reset these values to their defaults.
I have alerted your Filter to exclude All Files. You also had jpg appearing twice.
I declared a variable to hold the file extension, PictureFormat, of the image file so you could provide the proper parameter for ImageFormat.
When you retrieve the image field from the database it comes as an Object. To get the Byte() a DirectCast should work.
Private PictureFormat As String
Private Sub PictureBox1_Click(sender As Object, e As EventArgs) Handles PictureBox1.Click
FillPictureBoxFromFile()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles BTNGuardar.Click
Try
saveimage()
Catch ex As Exception
MessageBox.Show(ex.Message)
Exit Sub
End Try
MsgBox("Image has been saved in the database")
End Sub
Private cnStr As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\geral\source\repos\BD de imagenes\BD de imagenes\DBImagenes.mdb"
Private Sub saveimage()
Dim arrimage() As Byte
Using mstream As New System.IO.MemoryStream
If PictureFormat.ToLower = ".png" Then
PictureBox1.Image.Save(mstream, System.Drawing.Imaging.ImageFormat.Png)
ElseIf PictureFormat.ToLower = ".jpg" Then
PictureBox1.Image.Save(mstream, System.Drawing.Imaging.ImageFormat.Jpeg)
End If
arrimage = mstream.GetBuffer()
Dim Filesize As Long
Filesize = mstream.Length
End Using
Using con As New OleDbConnection(cnStr),
cmd As New OleDbCommand("Insert into TBImg (Imagen, Nombre) Values (#Imagen, #Nombre)", con)
With cmd
.Parameters.Add("#Imagen", OleDbType.Binary).Value = arrimage
.Parameters.Add("#Nombre", OleDbType.VarChar).Value = TextBox1.Text
con.Open()
.ExecuteNonQuery()
End With
End Using
End Sub
Private Sub BtnExaminar_Click(sender As Object, e As EventArgs) Handles BtnExaminar.Click
FillPictureBoxFromFile()
End Sub
Private Sub BtnBuscar_Click(sender As Object, e As EventArgs) Handles BtnBuscar.Click
Dim dt As DataTable
Try
dt = GetDataByName(TBBuscar.Text)
Catch ex As Exception
MessageBox.Show(ex.Message)
Exit Sub
End Try
TextBox1.Text = dt(0)("Nombre").ToString
Dim arrimage = DirectCast(dt(0)("Imagen"), Byte())
Dim mstream As New System.IO.MemoryStream(arrimage)
PictureBox1.Image = Image.FromStream(mstream)
End Sub
Private Function GetDataByName(name As String) As DataTable
Dim dt As New DataTable
Using conn As New OleDb.OleDbConnection(cnStr),
cmd As New OleDbCommand("Select * from TBImg where Nombre= #Buscar", conn)
cmd.Parameters.Add("#Buscar", OleDbType.VarChar).Value = TBBuscar.Text
conn.Open()
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
Return dt
End Function
Private Sub FillPictureBoxFromFile()
With OpenFileDialog1
.Filter = "(*.jpg)|*.jpg|(*.png)|*.png"
.RestoreDirectory = True
.Title = "Select a file to open"
If .ShowDialog = DialogResult.OK Then
PictureBox1.Image = Image.FromFile(OpenFileDialog1.FileName)
End If
PictureFormat = Path.GetExtension(OpenFileDialog1.FileName)
End With
End Sub
The following code is supposed to display information from a database but there is an error (the title of this question) on the DBCmd.ExecuteNonQuery() line of code.
Does anyone know how I can resolve this problem?
• I am using VB.NET
• I am using an Access database
The code is:
Imports System.Data.OleDb
Public Class frmCheckAvailablity
Private DBCon As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;" &
"Data Source=|DataDirectory|\NewHotel.mdb;")
Private Access As New DBControl
Dim QRY As String
Private DBCmd As OleDbCommand
Dim DBDR As OleDbDataReader
Public DBDA As New OleDbDataAdapter("SELECT RoomType FROM tblRoomBookings", DBCon)
Public DT As New DataTable
Public DS As New DataSet
Public DR As DataRow
Private Function NotEmpty(text As String) As Boolean
Return Not String.IsNullOrEmpty(text)
End Function
Private Sub frmCheckAvailability_Shown(sender As Object, e As EventArgs) Handles Me.Shown
'RUN QUERY
Access.ExecQuery("SELECT * FROM tblRoomBookings ORDER BY BookingID ASC")
If NotEmpty(Access.Exception) Then MsgBox(Access.Exception) : Exit Sub
End Sub
Private Sub frmCheckAvailability_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the 'NewHotelDataSet.tblRoomBookings' table. You can move, or remove it, as needed.
Me.TblRoomBookingsTableAdapter.Fill(Me.NewHotelDataSet.tblRoomBookings)
If DBCon.State = ConnectionState.Closed Then DBCon.Open() : Exit Sub
End Sub
Private Sub Search()
DBDA.Fill(DT)
txtSearch.AutoCompleteCustomSource.Clear()
For Each DBDR In DT.Rows
txtSearch.AutoCompleteCustomSource.Add(DBDR.Item(0).ToString)
Next
txtSearch.AutoCompleteMode = AutoCompleteMode.SuggestAppend
txtSearch.AutoCompleteSource = AutoCompleteSource.CustomSource
End Sub
Private Sub SearchCustomers(RoomType As String)
'ADD PARAMETERS & RUN QUERY
Access.AddParam("#RoomType", "%" & RoomType & "%")
Access.ExecQuery("SELECT * FROM tblRoomBookings WHERE RoomType LIKE #RoomType")
'REPORT & ABORT ON ERRORS
If NotEmpty(Access.Exception) Then MsgBox(Access.Exception) : Exit Sub
End Sub
Private Sub txtSearch_TextChanged(sender As Object, e As EventArgs) Handles txtSearch.TextChanged
QRY = "SELECT FullName FROM tblRoomBookings WHERE RoomType'" & txtSearch.Text & "'"
DBCmd = New OleDbCommand(QRY, DBCon)
DBCmd.ExecuteNonQuery()
DBDR = DBCmd.ExecuteReader
If DBDR.Read Then
txtRoomType.Text = DBDR("RoomType")
txtFirstNight.Text = DBDR("FirstNight")
txtLastNight.Text = DBDR("LastNight")
txtNoNights.Text = DBDR("NoNights")
End If
End Sub
The only place in the code that I see DBcmd.ExecuteNonQuery is in search text changed event. Do really want to run this code every time the users types a letter?
Do not create a new connection at the class (Form) level. Every time the connection is used it needs to be disposed so it can be returned to the connection pool. Using...End Using blocks handle this for you even if there is an error.
Don't call .ExecuteNonQuery. This is not a non query; it begins with Select.
You can't execute a command without an Open connection.
Never concatenate strings for sql statments. Always use parameters.
The connection is open while the reader is active. Don't update the user interface while the connection is open.
Load a DataTable and return that to the user interface code where you update the user interface.
Private ConStr As String = "Your connection string"
Private Function GetSearchResults(Search As String) As DataTable
Dim dt As New DataTable
Dim QRY = "SELECT FullName FROM tblRoomBookings WHERE RoomType = #Search"
Using DBcon As New OleDbConnection(ConStr),
DBCmd As New OleDbCommand(QRY, DBcon)
DBCmd.Parameters.Add("#Search", OleDbType.VarChar).Value = Search
DBcon.Open()
Using reader = DBCmd.ExecuteReader
dt.Load(reader)
End Using
End Using
Return dt
End Function
Private Sub txtSearch_TextChanged(sender As Object, e As EventArgs) Handles txtSearch.TextChanged
Dim dtSearch = GetSearchResults(txtSearch.Text)
If dtSearch.Rows.Count > 0 Then
txtRoomType.Text = dtSearch(0)("RoomType").ToString
txtFirstNight.Text = dtSearch(0)("FirstNight").ToString
txtLastNight.Text = dtSearch(0)("LastNight").ToString
txtNoNights.Text = dtSearch(0)("NoNights").ToString
End If
End Sub
I am currently working in windows form applications using vb.net 2013. I have two forms, we can call them form1 and form 2 for now. Form1 has a datagridview with a checkbox column that the end user will click to open form2 as a dialog form. Once form2 opens I want it to automatically load and fill two text boxes that have information from corresponding columns from the original DGV. In short, the DGV on form1 has 3 columns; JobNumber, LineNumber, and the checkbox. The end user will click the checkbox to bring up form2 and I want the Jobnumber and Linenumber to automaticaly fill into two textboxes on form2. Here is my code from form1.
'assembly dialog result form
dr = f.ShowDialog
If dr = Windows.Forms.DialogResult.OK Then
'dim y as job string
Dim Y As String = DataGridOrdered.Rows(e.RowIndex).Cells(3).Value
'open connection
Using conn1 As New SqlConnection(connstring)
conn1.Open()
Using comm1 As SqlCommand = New SqlCommand("UPDATE Production.dbo.tblFCOrdered SET Complete = 1, RackIn = 1 WHERE JobNumber = '" & Y & "'", conn1)
comm1.ExecuteNonQuery()
conn1.Close()
End Using
End Using
Call DGVOrderedRefresh()
ElseIf dr = Windows.Forms.DialogResult.Yes Then
'dim M as job string
Dim M As String = DataGridOrdered.Rows(e.RowIndex).Cells(3).Value
'open connection
Using conn1 As New SqlConnection(connstring)
conn1.Open()
Using comm1 As SqlCommand = New SqlCommand("UPDATE Production.dbo.tblFCOrdered SET Complete = 1, RackIn = 1 WHERE JobNumber = '" & M & "'", conn1)
comm1.ExecuteNonQuery()
conn1.Close()
End Using
End Using
Call DGVOrderedRefresh()
ElseIf dr = Windows.Forms.DialogResult.Cancel Then
'refresh datagridview ordered
Call DGVOrderedRefresh()
End If
ElseIf e.ColumnIndex = 0 Then
'fail safe to make sure the header is not clicked
If e.RowIndex = -1 Then
Exit Sub
End If
The code for my dialog is as follows
Imports System.Data
Imports System.Data.SqlClient
Public Class BuildName
' Dim connstring As String = "DATA SOURCE = BNSigma\CORE; integrated security = true"
Dim connstring As String = "DATA SOURCE = BNSigma\TEST; integrated security = true"
Private Sub Label3_Click(sender As Object, e As EventArgs) Handles Label3.Click
End Sub
Private Sub BuildName_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim ds As New DataTable
Try
'load name combo box
Using conn1 As New SqlConnection(connstring)
conn1.Open()
Using comm1 As SqlCommand = New SqlCommand("SELECT Name FROM Production.dbo.FCNames", conn1)
Dim adapater As New SqlDataAdapter
adapater.SelectCommand = comm1
adapater.Fill(ds)
adapater.Dispose()
conn1.Close()
CBName.DataSource = ds
CBName.DisplayMember = "Name"
CBName.ValueMember = "Name"
End Using
End Using
Catch ex As Exception
MsgBox("Error loading name List, please contact a mfg. Engr.!")
MsgBox(ex.ToString)
End Try
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Update built by name
Dim v As Object = TBFloor.Text
Dim G As Object = TBLine.Text
Dim O As Object = TBJobNumber.Text
Try
Using conn1 As New SqlConnection(connstring)
conn1.Open()
Using comm1 As SqlCommand = New SqlCommand("UPDATE Production.dbo.tblFCOrdered SET BuiltBy = #Name Where JobNumber = '" & O & "'", conn1)
comm1.Parameters.AddWithValue("#Name", CBName.Text)
comm1.ExecuteNonQuery()
conn1.Close()
End Using
End Using
Catch ex As Exception
MsgBox("Error updating Ordered table, please contact a MFG. ENGR.!")
MsgBox(ex.ToString)
End Try
End Sub
End Class
UPDATED CODE FOR VALTER
Form1 code
Public row_Index As Integer = 0
Private Sub DataGridOrdered_CurrentCellDirtyStateChanged(sender As Object, e As EventArgs) Handles DataGridOrdered.CurrentCellDirtyStateChanged
If DataGridOrdered.IsCurrentCellDirty Then
DataGridOrdered.CommitEdit(DataGridViewDataErrorContexts.Commit)
End If
End Sub
Private Sub DataGridOrdered_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridOrdered.CellValueChanged
If DataGridOrdered.Columns(e.ColumnIndex).Name = 9 Then
Dim checkCell As DataGridViewCheckBoxCell = CType(DataGridOrdered.Rows(e.RowIndex).Cells(9), DataGridViewCheckBoxCell)
If CType(checkCell.Value, [Boolean]) = True Then
row_Index = e.RowIndex
BuildName.ShowDialog(Me)
End If
End If
End Sub
Form 2 code
Private Sub BuildName_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim value1 As Object = FormOrdered.DataGridOrdered.Rows(FormOrdered.row_Index).Cells(3).Value
Dim value2 As Object = FormOrdered.DataGridOrdered.Rows(FormOrdered.row_Index).Cells(4).Value
TBJobNumber.Text = CType(value1, String)
TBFloor.Text = CType(value2, String)
In your form1 add:
Public Row_Index As Integer = 0 //use a simple variable
Sub datagridOrdered_CurrentCellDirtyStateChanged( _
ByVal sender As Object, ByVal e As EventArgs) _
Handles datagridOrdered.CurrentCellDirtyStateChanged
If datagridOrdered.IsCurrentCellDirty Then
datagridOrdered.CommitEdit(DataGridViewDataErrorContexts.Commit)
End If
End Sub
Private Sub datagridOrdered_CellValueChanged(sender As System.Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles datagridOrdered.CellValueChanged
If datagridOrdered.Columns(e.ColumnIndex).Name = "Assemble" Then //<- here
Dim checkCell As DataGridViewCheckBoxCell = _
CType(datagridOrdered.Rows(e.RowIndex).Cells(2), _ //<- here
DataGridViewCheckBoxCell)
If CType(checkCell.Value, [Boolean]) = True Then
//RowIndex = e.RowIndex you dont need this
Row_Index = e.RowIndex
BuildName.ShowDialog(Me)
End If
End If
End Sub
//Nor this
//Private _count As Integer
//Public Property RowIndex() As Integer
//Get
//Return _count
//End Get
//Set(value As Integer)
//_count = value
//End Set
//End Property
And in dialog load event use Form1.Row_Index instead:
Private Sub BuildName_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim value1 As Object = FormOrdered.datagridOrdered.Rows(Form1.Row_Index).Cells(0).Value //<- here
Dim value2 As Object = FormOrdered.datagridOrdered.Rows(Form1.Row_Index).Cells(1).Value //<- here
TBJobNumber.Text = CType(value1, String)
TBFloor.Text = CType(value2, String)
...
...
End Sub
or add a module and add the Row_Index there. You can use it then as is
Private Sub BuildName_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim value1 As Object = FormOrdered.DataGridView1.Rows(Row_Index).Cells(0).Value
Dim value2 As Object = FormOrdered.DataGridView1.Rows(Row_Index).Cells(1).Value
TBJobNumber.Text = CType(value1, String)
TBFloor.Text = CType(value2, String)
...
...
End Sub
I am writing a small postIt programm which should enable the user to send little notes to other computers using a database.
I have a main-Form where I can write messages and see users online, a sheet-form which should display messages and a sql database which has a table for users and messages.
The idea is that I am writing messages into the Messagestable and my programm uses SqlDependency to get the new messages and to show them in sheets. So far so good.
The problem is when I add a new Message to my table the SqlDependency fires an Event and my mainForm creates a new sheet which freezes after sheet.Show() is called. The mainForm keeps on running but my sheets always do not respond.
Here's my code:
DBListener:
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlClient
Imports System.Threading
Public Class DBListener
Private changeCount As Integer = 0
Private Const tableName As String = "IMSMessages"
Private Const statusMessage As String = _
"{0} changes have occurred."
Private exitRequested As Boolean = False
Private waitInProgress As Boolean = False
Private recieverHost As String
Private imsMain As IMSMain
Public Sub New(main As IMSMain, recieverHost As String)
Me.imsMain = main
Me.recieverHost = recieverHost
initCommand(recieverHost)
SqlDependency.Start(connectionString)
GetMsg(recieverHost)
End Sub
Private connectionString As String = "Data Source=NB_RANDY\SQLEXPRESS;Initial Catalog=eurom_test;Integrated Security=SSPI;"
Private sqlConn As SqlConnection = New SqlConnection(connectionString)
Private commandMesg As SqlCommand = Nothing
Private commandUsers As SqlCommand = Nothing
Private Sub initCommand(recieverHost As String)
commandMesg = New SqlCommand
commandMesg.CommandText = "SELECT MessageID,SenderHost,RecieverHost,isRead,isRecieved,Stamp from dbo.IMSMessages " &
"WHERE RecieverHost=#RecieverHost" &
" AND isRecieved = 0"
commandMesg.Parameters.Add("#RecieverHost", SqlDbType.VarChar).Value = recieverHost
commandMesg.Connection = sqlConn
commandUsers = New SqlCommand
commandUsers.CommandText = "Select HostName From dbo.IMSUser"
End Sub
Private Sub OnChangeMsg(ByVal sender As System.Object, ByVal e As System.Data.SqlClient.SqlNotificationEventArgs)
Dim dep As SqlDependency = DirectCast(sender, SqlDependency)
RemoveHandler dep.OnChange, AddressOf OnChangeMsg
GetMsg(recieverHost)
End Sub
Private Sub OnChangeUser(ByVal sender As System.Object, ByVal e As System.Data.SqlClient.SqlNotificationEventArgs)
Dim dep As SqlDependency = DirectCast(sender, SqlDependency)
RemoveHandler dep.OnChange, AddressOf OnChangeUser
GetUsers()
End Sub
Public Sub GetMsg(recieverHost As String)
If Not sqlConn.State = ConnectionState.Open Then
sqlConn.Open()
End If
commandMesg.Notification = Nothing
Dim dep As SqlDependency = New SqlDependency(commandMesg)
AddHandler dep.OnChange, New OnChangeEventHandler(AddressOf OnChangeMsg)
Dim table As DataTable = New DataTable
Dim adapter As SqlDataAdapter = New SqlDataAdapter(commandMesg)
adapter.Fill(table)
imsMain.recieveNewMessages(table)
End Sub
Public Sub GetUsers()
If Not sqlConn.State = ConnectionState.Open Then
sqlConn.Open()
End If
commandMesg.Notification = Nothing
Dim dep As SqlDependency = New SqlDependency(commandUsers)
AddHandler dep.OnChange, New OnChangeEventHandler(AddressOf OnChangeUser)
Dim table As DataTable = New DataTable
imsMain.updateOnlineUserList()
End Sub
End Class
IMSMain-Form:
Imports System.Data.SqlClient
Imports System.Threading
Public Class IMSMain
Private user As IMSUser
Private listener As DBListener
Private sqlConn As SqlConnection
Public Sub New()
InitializeComponent()
sqlConn = New SqlConnection("Data Source=NB_RANDY\SQLEXPRESS;Initial Catalog=eurom_test;Integrated Security=SSPI;")
Me.user = New IMSUser(My.Computer.Name)
user.register()
updateOnlineUserList()
listener = New DBListener(Me, user.HostName)
End Sub
Private Sub onSend(sender As Object, e As EventArgs) Handles bt_send.Click
Dim command As SqlCommand = New SqlCommand
Dim msgText = tb_text.Text
Dim reciever As IMSUser = lb_Online.SelectedItem
Dim insert_string As String = "Insert INTO dbo.IMSMessages(Text,RecieverHost,SenderHost,isRead,isRecieved,Stamp) Values(#Text,#RecieverHost,#SenderHost,#isRead,#isRecieved,#Stamp)"
Dim adapter As SqlDataAdapter = New SqlDataAdapter
Dim table As DataTable = New DataTable()
Try
If Not sqlConn.State = ConnectionState.Open Then
sqlConn.Open()
End If
command.Connection = sqlConn
command.CommandText = insert_string
adapter.SelectCommand = command
adapter.Fill(table)
command.CommandText = insert_string
command.Parameters.Add("#Text", SqlDbType.VarChar).Value = msgText
command.Parameters.Add("#RecieverHost", SqlDbType.NChar).Value = reciever.HostName
command.Parameters.Add("#SenderHost", SqlDbType.NChar).Value = user.HostName
command.Parameters.Add("#isRecieved", SqlDbType.Bit).Value = 0
command.Parameters.Add("#isRead", SqlDbType.Bit).Value = 0
command.Parameters.Add("#Stamp", SqlDbType.SmallDateTime).Value = DateTime.Now
command.ExecuteNonQuery()
Catch ex As Exception
Console.WriteLine("onSend: internal database exception" + ex.Message)
End Try
End Sub
Private Sub processMessageId(refMessageID As Integer)
Dim command As SqlCommand = New SqlCommand
Dim msgID_string As String = "SELECT * from dbo.IMSMessages " &
"WHERE MessageID=#MessageID AND isRecieved = 0" &
" ORDER BY Stamp"
Dim isRecievedUpdate_string As String = "Update dbo.IMSMessages " &
"SET isRecieved=1" &
" WHERE MessageID=#MessageID"
Dim adapter As SqlDataAdapter = New SqlDataAdapter
Dim table As DataTable = New DataTable()
Try
If Not sqlConn.State = ConnectionState.Open Then
sqlConn.Open()
End If
command = New SqlCommand
command.CommandText = msgID_string
command.Parameters.Add("#MessageID", SqlDbType.Int).Value = refMessageID
command.Connection = sqlConn
adapter.SelectCommand = command
adapter.Fill(table)
command = New SqlCommand
command.Connection = sqlConn
command.CommandText = isRecievedUpdate_string
command.Parameters.Add("#MessageID", SqlDbType.Int).Value = refMessageID
command.ExecuteNonQuery()
Catch ex As Exception
Console.WriteLine("processMessageID: internal database exception" + ex.Message)
End Try
Try
Dim row As DataRow = table.Rows(0)
Dim senderHost As String = row("SenderHost")
Dim sender As IMSUser = New IMSUser(senderHost)
Dim currentSheet As IMSSheet
Dim stringText As String = row("Text")
Dim stamp As Date = row("Stamp")
currentSheet = New IMSSheet(sender, stringText, stamp)
currentSheet.Show()
Catch ex As Exception
Console.WriteLine("processMessageID: error while showing sheet")
End Try
End Sub
Public Sub recieveNewMessages(newMessageTable As DataTable)
For Each row As DataRow In newMessageTable.Rows
Dim id As Integer = row("MessageID")
processMessageId(id)
Next
End Sub
Public Sub updateOnlineUserList()
lb_Online.Items.Clear()
Dim adapter As SqlDataAdapter = New SqlDataAdapter
Dim table As DataTable = New DataTable()
Dim command As SqlCommand = New SqlCommand
Dim onlineUser_string = "Select * FROM dbo.IMSUser"
command.CommandText = onlineUser_string
Try
If Not sqlConn.State = ConnectionState.Open Then
sqlConn.Open()
End If
command.Connection = sqlConn
adapter.SelectCommand = command
adapter.Fill(table)
Catch ex As Exception
Console.WriteLine("internal database exception" + ex.Message)
End Try
For Each row As DataRow In table.Rows
Dim tmp As IMSUser = New IMSUser(row("HostName"))
If Not user.Equals(tmp) Then
lb_Online.Items.Add(tmp)
End If
Next
End Sub
End Class
IMSSheet-Form:
Public Class IMSSheet
Dim IsDraggingForm As Boolean = False
Private MousePos As New System.Drawing.Point(0, 0)
Public Sub New(session As IMSUser, text As String, stamp As Date)
Ini
tializeComponent()
Me.tb_sender.Text = session.HostName
addText(text, stamp)
End Sub
Private Sub addText(msg As String, stamp As DateTime)
Dim currentText As String = tb_text.Text
currentText = currentText + stamp.ToString + Environment.NewLine + msg + Environment.NewLine
tb_text.Text = currentText
End Sub
Private Sub IMSSheet_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseDown
If e.Button = MouseButtons.Left Then
IsDraggingForm = True
MousePos = e.Location
End If
End Sub
Private Sub IMSSheet_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseUp
If e.Button = MouseButtons.Left Then IsDraggingForm = False
End Sub
Private Sub IMSSheet_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseMove
If IsDraggingForm Then
Dim temp As Point = New Point(Me.Location + (e.Location - MousePos))
Me.Location = temp
temp = Nothing
End If
End Sub
Private Sub onClose(sender As Object, e As EventArgs) Handles bt_Close.Click
Me.Close()
End Sub
Private Sub bt_minimize_Click(sender As Object, e As EventArgs) Handles bt_minimize.Click
Me.WindowState = FormWindowState.Minimized
End Sub
Private Sub bt_Close_MouseHover(sender As Object, e As EventArgs) Handles bt_Close.MouseHover
End Sub
Private Sub bt_minimize_MouseHover(sender As Object, e As EventArgs) Handles bt_minimize.MouseHover
End Sub
End Class
Interesting is that if I have already some new Messages in my Messagestable before I run the programm they are going to be displayed correctly.
If I display the sheets using Form.ShowDialog() it works as well but its not how I want it to work.
As I am out of ideas I hope you can help me.
You are running into issues because the change event of SqlDependency occurs on a different thread that your UI thread. From the SqlDepending documentation:
The OnChange event may be generated on a different thread from the
thread that initiated command execution.
When calling sheet.Show() on a non UI thread, Show() will lockup because there is no message loop to handle the UI events. Use Invoke() on ImsMain to create and show the ImsSheet on your UI thread.