I have a program that asks the user a series of questions that are collected from a database and stored in a datatable.
I have a system that chooses two random numbers, one to determine which question and one to determine what order the answers are displayed. I want questions to remain in the datatable until the user gets it correct, and afterwards that question cannot come up again.
My datatable is called DT, and there's a line of code:
DT.Rows.RemoveAt(QNumber)
Which sounds like it should remove the row selected. However I have a question regarding this.
If for example QNumber was 2, and so row 2 was deleted. Would this then move everything from row 3 in to row 2, and then everything from row 4 to row 3 and so on, or would this just make row 2 blank, and so break my code?
Since you didn't post any context are some test snippets:
Button1_Click loads a table
Button2_Click "processes" each row and optionally deletes the row.
In your case "process" would mean: display the question and loop until you get an answer.
In your case Button2 code would determine which record to load, probably using .Select() on the datatable that returns the "question" to ask, and eventually delete.
Dim da As SqlDataAdapter
Dim ds As DataSet
Dim dt As DataTable
Dim con As New System.Data.SqlClient.SqlConnection()
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
Using con As New System.Data.SqlClient.SqlConnection()
con.ConnectionString = "Data Source=APCD03;Initial Catalog=OIStest;Integrated Security=True"
Dim rdr As Data.SqlClient.SqlDataReader
con.Open()
Dim cmd As New SqlCommand("SELECT [DBLinked] as PK ,[TEBackupDate] FROM [OISTest].[dbo].[_DBLink]", con)
rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
dt = New DataTable
dt.Load(rdr)
rdr.Close()
End Using
Catch ex As Exception
MsgBox(ex, ex.Message)
End Try
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
For Each row As DataRow In dt.Rows
If Not row.RowState = DataRowState.Deleted Then
If MsgBox("Delete this row, PK: " & row("PK"), vbYesNo) = MsgBoxResult.Yes Then
row.Delete()
End If
End If
Next
End Sub
Button3 demos selecting a specific row to process and delete:
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim rows() As DataRow = dt.Select("PK='test row'")
If rows.Length = 0 Then
MsgBox("row was deleted already")
Else
MsgBox(rows(0)(0)) ' display PK
If MsgBox("Delete " & rows(0)(0), vbYesNo) = MsgBoxResult.Yes Then
rows(0).Delete()
End If
End If
End Sub
Note that rows(0) is the first record of the result from the select - not the record index in the overall table.
Related
I was just wondering how I would display a random entity in an access column.
Imports System.Data.OleDb
Public Class ReviseFlashcards
Dim connection As New OleDb.OleDbConnection(
"provider=microsoft.ACE.OLEDB.12.0;Data Source=flashcard login.accdb")
Dim dt As New DataTable
Dim dataadapter As OleDb.OleDbDataAdapter
'contains the current row number
Dim rownumber As Integer = 0
'Data table to contain all the records
Private Sub ReviseFlashcards_Load(sender As Object, e As EventArgs) _
Handles MyBase.Load
Dim sqlstring As String = "select * from flashcards"
connection.Open()
dataadapter = New OleDb.OleDbDataAdapter(sqlstring, connection)
dt.Clear()
dataadapter.Fill(dt)
txtFront.Text = dt.Rows(0)(2)
txtBack.Text = dt.Rows(0)(3)
connection.Close()
End Sub
Private Sub btnDisplay_Click(sender As Object, e As EventArgs) _
Handles btnDisplay.Click
End Sub
End Class
The current code displays the first row in the database which is shown below. I was wondering if there was a way to display a random row by clicking a button with the front and back matching each other.
The access database
Add a random generator to the form. Make shure to declare it as Shared, so that this generator will be created only once. This ensures that it generates unique random number sequencess (see: Why isn't this Random number generation code working?).
Private Shared rand As New Random()
Then create a row index by respecting the actual row count:
Dim index = rand.Next(dt.Rows.Count) ' generates index in the range 0 .. Count - 1
txtFront.Text = dt.Rows(index)(2).ToString()
txtBack.Text = dt.Rows(index)(3).ToString()
I'm quite new to programming so excuse my lack of knowledge.
I'm trying to create a quiz for my project and am having trouble with it. Currently, I'm trying to make the quiz run until the number of questions is the same as the desired amount the user inputs in a Combobox from the previous form. Whilst this does partially work, the problem is that it is really slow and freezes when you click the button the second time.
Imports System.Data.OleDb
Public Class ArithmeticQuestions
Dim NoQ = ArQOP.NoQ
Dim dr As OleDbDataReader
Dim cm As New OleDbCommand
Dim cn As New OleDbConnection
Dim n As Integer = 1
Private Sub ArithmeticQuestions_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Label1.Hide()
RadioButton1.Hide()
RadioButton2.Hide()
RadioButton3.Hide()
RadioButton4.Hide()
SkipQues.Hide()
NxtQues.Hide()
End Sub
Sub Questions()
cn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=login.accdb"
cn.Open()
cm.CommandText = "SELECT Questions FROM MCQ WHERE QuestionNumber ='" & n & "'"
cm.Connection = cn
dr = cm.ExecuteReader
dr.Read()
Label1.Text = dr.Item("Questions")
cn.Close()
End Sub
Private Sub NxtQues_Click(sender As Object, e As EventArgs) Handles NxtQues.Click
n = n + 1
Do
Call Questions()
Loop Until n = NoQ
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles StartQuiz.Click
Label1.Show()
RadioButton1.Show()
RadioButton2.Show()
RadioButton3.Show()
RadioButton4.Show()
SkipQues.Show()
NxtQues.Show()
StartQuiz.Hide()
Call Questions()
End Sub
End Class
Any help would be appreciated.
I am guessing that NoQ is the number of questions the user requested.
Keep you database objects local to the method where they are used so you can control that they are closed and disposed. Using...End Using blocks take care of this for you even if there is an error. You can pass the connection string directly to the constructor of the connection and the command text and connection directly to the constructor of the command.
Always use Parameters. I had to guess at the OleDbType. Check your database for the correct type and adjust the code accordingly. It appears that you are retrieving a single piece of data so you can use .ExecuteScalar to get the first column of the first row of the result set.
In the button code I just incremented the question number and checked if against the maximum number of questions.
Dim NoQ = ArQOP.NoQ
Dim n As Integer
Private Sub Questions()
Using cn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=login.accdb"),
cmd As New OleDbCommand("SELECT Questions FROM MCQ WHERE QuestionNumber = #QuestionNumber;", cn)
cmd.Parameters.Add("#QuestionNumber", OleDbType.Integer).Value = n
cn.Open()
Label1.Text = cmd.ExecuteScalar.ToString
End Using
End Sub
Private Sub NxtQues_Click(sender As Object, e As EventArgs) Handles NxtQues.Click
n += 1
If n <= NoQ Then
Questions()
End If
End Sub
I'm doing a project for my Database Management subject. I cannot figure out how to add an amount to a previously added amount. For now, I'm only able to update the amount. Here's the code. I'm sorry if I cannot explain it well.
I have 2 forms. My first form allows me to enter a last name and retrieve the data to my list view.
My second form lets me retrieve the data I entered in my first form and it will show up on a separate list view with a "Last Name | Amount" tab.
I have two textboxes. One for last name set to readonly to disable editing, and another for the amount I want to enter.
After entering an amount, let's say 20, it will update on the listview and my database as 20.
The problem is that when I enter a new amount for the same last name, let's say 30, the 30 will replace the 20 but it should be 50 because 20+30 = 50.
I understand the logic and I have tried adding another textbox for addition but I simply do not know the codes for it.
Imports System.Data.OleDb
Public Class Form2
Dim conString As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\Israel De Leon\Documents\testing.accdb;"
Dim con As OleDbConnection = New OleDbConnection(conString) 'Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\Database2.accdb
Dim cmd As OleDbCommand
Dim adapter As OleDbDataAdapter
Dim dt As DataTable = New DataTable()
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'SET LISTVIEW PROPERTIES
ListView1.View = View.Details
ListView1.FullRowSelect = True
'Construct Columns
ListView1.Columns.Add("Last Name", 100)
ListView1.Columns.Add("Amount", 100)
End Sub
Private Sub UpdateLV(lname As String)
'Updates last name and amount entered into the database
Dim sql As String = "UPDATE Table1 SET LastName='" + TextBox1.Text + "',Amount='" + TextBox2.Text + "' WHERE LastName='" + lname + "'"
cmd = New OleDbCommand(sql, con)
'OPEN CON, EXECUTE, UPDATE, CLOSE
Try
con.Open()
adapter = New OleDbDataAdapter(cmd)
adapter.UpdateCommand = con.CreateCommand()
adapter.UpdateCommand.CommandText = sql
If (adapter.UpdateCommand.ExecuteNonQuery() > 0) Then
MsgBox("Successfully Updated")
End If
con.Close()
Retrieve()
ClearBox()
Catch ex As Exception
MsgBox(ex.Message)
con.Close()
End Try
End Sub
Private Sub Retrieve()
ListView1.Items.Clear()
'SQL STM
Dim sql As String = "SELECT * FROM Table1 "
cmd = New OleDbCommand(sql, con)
'OPEN CON, RETRIEVE, FILL LISTVIEW
Try
con.Open()
adapter = New OleDbDataAdapter(cmd)
adapter.Fill(dt)
'LOOP THROUGH DT
For Each row In dt.Rows
Populate(row(0), row(1)) 'Index of database row
Next
'CLEAR DATATABLE
dt.Rows.Clear()
con.Close()
Catch ex As Exception
MsgBox(ex.Message)
con.Close()
End Try
End Sub
Private Sub Populate(lname As String, aamount As String)
'ROW ARRAY
Dim row As String() = New String() {lname, aamount}
Dim item As ListViewItem = New ListViewItem(row)
'ADD TO ROWS COLLECTION
ListView1.Items.Add(item)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Retrieve()
End Sub
Private Sub ListView1_MouseClick(sender As Object, e As MouseEventArgs) Handles ListView1.MouseClick
Dim llname As String = ListView1.SelectedItems(0).SubItems(0).Text
Dim amounts As String = ListView1.SelectedItems(0).SubItems(1).Text
TextBox1.Text = llname
TextBox2.Text = amounts
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim amounts As String = ListView1.SelectedItems(0).SubItems(0).Text
UpdateLV(amounts)
End Sub
Private Sub ClearBox()
TextBox1.Text = ""
TextBox2.Text = ""
End Sub
End Class
Mathematical operation should not be done using strings. This is a real basic principle that many VB.NET programmers don't think enough thanks to the forgiveness allowed by Option Strict Off in the VB.NET project settings.
If you are just starting a new project in VB.NET don't use this setting but switch it ASAP to On. This will give you an halt when you try to use strings as they were numbers and force you to do the appropriate conversion and checking on the values provided.
So your code that updates the amount rewritten
Private Sub UpdateLV(lname As String)
' Get the amount as a number (decimal for currency is the best)
Dim addAmt As Decimal
if Not decimal.TryParse(textbox2.Text, addAmt) Then
MessageBox.Show("Insert a valid amount please")
return
End If
' Sanity check
if addAmt <= 0 Then
MessageBox.Show("Amount should be > 0")
return
End If
'Updates last name and amount entered into the database
Dim sql As String = "UPDATE Table1 SET LastName=#name
,Amount=Amount+#amt
WHERE LastName=#oldname"
cmd = New OleDbCommand(sql, con)
Try
con.Open()
' Using an adapter here is wrong. You use directly the command
cmd.Parameters.Add("#name", OleDbType.VarWChar).Value = textBox1.Text
cmd.Parameters.Add("#amt", OleDbType.Decimal).Value = addAmt
cmd.Parameters.Add("#oldname", OleDbType.VarWChar).Value = lName
If (cmd.ExecuteNonQuery() > 0) Then
MsgBox("Successfully Updated")
End If
con.Close()
Retrieve()
ClearBox()
Catch ex As Exception
MsgBox(ex.Message)
con.Close()
End Try
End Sub
Something else is not clear in your code. What is the purpose of changing also the LastName here? Finally do not keep a global connection object. Instead create it when you need it and destroy it afterward with Using statement. It will be better for your memory footprint and for your database
I have a ComboboxCell in first cell of Datagridview. This combobox has Datasource from my DB. When I select Item from It, all other cells in a row gets populated by a record from my DB. Now I want to prevent duplicate row entries, based on Comboboxcell selected item. After that I want to clear that Comboboxcell, and keep code running. I managed to do almost everything, but problem is that after msgbox is displayed, code stops working - so when I select ComboboxCell Item again, nothing happens, even in a new row. Here is my whole code:
Private Sub My_DGV_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles My_DGV.CellValueChanged
If My_DGV.Columns(e.ColumnIndex).Name = "Column1" Then
'Prevent duplicates
For i As Integer = 0 To My_DGV.RowCount - 2
For j As Integer = i + 1 To My_DGV.RowCount - 2
If My_DGV.Rows(i).Cells(0).Value = My_DGV.Rows(j).Cells(0).Value Then
MsgBox("You allready selected this item. Duplicates are not allowed.", MsgBoxStyle.Information, "Warning")
My_DGV.Rows(j).Cells(0).Value = " "
Dim cbx As ComboBox = DGV_APO.EditingControl
cbx.SelectedIndex = -1
Exit Sub
End If
Next
Next
OracleconnOpen()
Using cmd As New OracleCommand()
Dim SQL As String = "Select NAMES,SURNAMES,STATE FROM My_Table"
Dim Concat_SQL As String = " Where "
SQL = String.Concat(SQL, Concat_SQL, " ID_NUMBER = :id")
cmd.Parameters.Add(New OracleParameter("id", My_DGV.CurrentRow.Cells(0).Value))
cmd.Connection = OracleconnOpen()
cmd.CommandText = SQL
cmd.CommandType = CommandType.Text
Dim dr As OracleDataReader = cmd.ExecuteReader()
Dim dt As New DataTable
dt.Load(dr)
My_DGV.CurrentRow.Cells(1).Value = dt.Rows(0)("NAMES").ToString()
My_DGV.CurrentRow.Cells(2).Value = dt.Rows(0)("SURNAMES").ToString()
My_DGV.CurrentRow.Cells(3).Value = dt.Rows(0)("STATE").ToString()
End Using
OracleconnClose()
End If
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'SELECT from my DB
Dim SQL As String = "SELECT ID_NUMBER from My_Table"
Dim dtb As New DataTable()
Try
OracleconnOpen() 'Open my connection
Using dad As New OracleDataAdapter(SQL, OracleconnOpen)
dad.Fill(dtb)
End Using
Column1.DisplayMember = "ID_NUMBER"
Column1.DataSource = dtb
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
OracleconnClose() 'Close my connection
End Try
End Sub
Private Sub My_DGV_CurrentCellDirtyStateChanged(sender As Object, e As EventArgs) Handles My_DGV.CurrentCellDirtyStateChanged
If My_DGV.IsCurrentCellDirty Then My_DGV.CommitEdit(DataGridViewDataErrorContexts.Commit)
End Sub
Private Sub My_DGV_DataError(sender As Object, e As DataGridViewDataErrorEventArgs) Handles DGV_APO.DataError
e.ThrowException = False
End Sub
So, what am I doing wrong ? Any help appreciated.
You have an Exit Sub call within your loop that will always execute so nothing after the exit sub will ever be run when you change this value. If you want to exit the loop and continue running the code after a duplicate is found, that should be done within the If statement. This code should stop working after the first row is compared even if it is not a match.
I am displaying data from SQLite table into a DataGridView as following -
Private Sub Subjects_Manager_Load(sender As Object, e As EventArgs) Handles MyBase.Load
con = New SQLiteConnection("Data Source = c:\demo\test.db;Version=3;")
con.Open()
sql = "SELECT * FROM med_subjects"
da = New SQLiteDataAdapter(sql, con)
da.Fill(ds, "SubjectsList")
DataGridView1.DataSource = ds.Tables("SubjectsList").DefaultView
With DataGridView1
.RowHeadersVisible = False
.Columns(0).HeaderCell.Value = "Subject Id"
.Columns(1).HeaderCell.Value = "Subject Name"
End With
DataGridView1.Sort(DataGridView1.Columns(0), System.ComponentModel.ListSortDirection.Ascending)
con.close()
End Sub
I want to save changes done in DataGridView (either Updation of Row/s or Insertion of Row/s) back to SQLite table. But I couldn't find a way to do so.
Edit 1 :
I know Insert/Update Queries of SQLite, but what I don't know is how & where to keep them so that they can be triggered in responses to changes made in DataGridView. e.g.
' I am using this variable for demonstration, in reality InsertSubjectSqlString will be equal to changes done in DataGridView
Dim InsertSubjectSqlString As String = "Insert into med_subjects (Subject_Name) Values ('_Miscellaneous')"
Dim SqliteInsertRow As SQLiteCommand
SqliteInsertRow = con.CreateCommand
SqliteInsertRow.CommandText = InsertSubjectSqlString
SqliteInsertRow.ExecuteNonQuery()
But I don't know, where should I put it?
Edit 2:
After seeing comments and answers, I came to know that there is No direct way to Insert/Update Sqlite database from DataGridView. So I was curious, if there is any event like RowSelected which would
trigger on selecting a row and get that row's data
then taking the row's data into multiple text boxes and lastly
triggering Insert/Update queries taking values from these textboxes
by a button
I know it's highly hypothetical with NO sample codes, but it's because I am asking for Event name.
Call this on LeaveRow event or CellEndEdit
Private Sub DataGridView1_RowLeave(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.RowLeave
Dim i as integer
Try
for i = 0 to datagrid.rows.count-1
myUpdate(datagrid.item(0,i).tostring() , datagrid.item(1,i).tostring())
next
Catch ex As Exception
MsgBox(Err.Description)
End Try
End Sub
note , you can also give the column name of your grid in place of column index , like this datagrid.item("fname",i).tostring()
Here we will save in database:
Sub myUpdate(byval fname as string , byval lname as string)
Try
dim con as new sqlconnection("you connection string")
dim cmd as new sqlcommand
con.open()
cmd.connection= con
cmd.commandtext="insert into table (fname,lname) values (#fname,#lname)"
cmd.Parameters.AddWithValue("#fname", fname)
cmd.Parameters.AddWithValue("#lname", lname)
cmd.ExecuteNonQuery()
Con.close()
Catch ex As Exception
MsgBox(Err.Description)
End Try
End sub
I hope this will help you to solve !
There are many ways to manipulate data .
CristiC
I dont think you will find some magic way that the DataGridView and DataTable are going to persist things automatically to the backend SQLite database. As you are hinting at I think you will have to rely on events.
I think the event you are missing is answered here stackoverflow cell value changed event
Ok I think is better to use CellEndEdit, because LeaveRow is triggered only if you click on other row.
Look on this example.
you must use: da.Update(ds.Tables("SubjectsList").DefaultView)
Imports System.Data.SqlClient
Public Class Form1
Const connectionstring As String = "server=(local);database=TestDB;uid=sa;pwd=sa;"
Private SQL As String = "select * from customer"
Private con As New SqlConnection(connectionstring)
Private dt As New DataTable
Private adapter As New SqlDataAdapter(SQL, con)
Private commandbuilder As New SqlCommandBuilder(adapter)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
adapter.Fill(dt)
DataGridView1.DataSource = dt
End Sub
Private Sub DataGridView1_RowLeave(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.RowLeave
adapter.Update(dt)
End Sub
End Class
Please use this code hopefully it will work
Imports System.Data.SQLite
Public Class Form
Dim con As New SQLite.SQLiteConnection
Dim da As New SQLite.SQLiteDataAdapter
Dim dt As New DataTable
Dim cmdbl As New SQLite.SQLiteCommandBuilder
Dim dlgResult As DialogResult
Private Sub btnUpdate_Click(sender As Object, e As EventArgs) Handles btnUpdate.Click
dlgResult = MessageBox.Show("Do you want to save the changes you made?", "Confirmation!", MessageBoxButtons.YesNo)
If dlgResult = DialogResult.Yes Then
Try
con.Open()
cmdbl = New SQLiteCommandBuilder(da)
da.Update(dt)
MsgBox("Updated successfully!")
con.Close()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End If
End Sub
End Class