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
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 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.
i'm having trouble fixing this problem "Syntax error in FROM Clause"
Here's my codes:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
myConnetion = New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\iponz18\Documents\Visual Studio 2012\Projects\CanteenBillingSystem\CanteenBillingSystem\CanteenBillingSystem.accdb")
Dim table As String = "SELECT UserID FROM User WHERE UserID = " + TextBox1.Text + ";"
da = New OleDbDataAdapter(table, myConnetion)
ds = New DataSet
da.Fill(ds, "User")
If (ds.Tables("User").Rows.Count > 0) Then
Form2.Show()
Me.Close()
End If
End Sub
The error is on this line:
da.Fill(ds, "User")
please help me out...
User is a reserved keyword in MS-Access. You need to put it between square brackets
Dim table As String = "SELECT UserID FROM [User] WHERE UserID = ...
Said that, your query has other problems. A better approach is this one:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim table As String = "SELECT UserID FROM User WHERE UserID = #uid"
Using conn = New OleDbConnection(.....)
Using da = New OleDbDataAdapter(table, conn )
da.SelectCommand.Parameters.Add("#uid", OleDbType.VarWChar).Value = textBox1.Text
ds = New DataSet
da.Fill(ds, "User")
If (ds.Tables("User").Rows.Count > 0) Then
Form2.Show()
End If
End Using
End Using
End Sub
I have changed your string concatenated query text to a parameterized query. This approach is more safe because it avoids Sql Injection and remove the need to escape properly your strings.
Also another very important advice that I feel to give is to avoid at all costs global variables to keep objects like a connection or an adapter around. This will give you a lot of trouble when something unexpected happens and your connection is leaved in unpredictable state.
Finally, if you just need to check if you have a UserID in your table, then there is no need to build an OleDbDataAdapter, a DataSet and fill it. You could just use ExecuteScalar method of the OleDbCommand
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim table As String = "SELECT UserID FROM User WHERE UserID = #uid"
Using conn = New OleDbConnection(.....)
Using cmd = New OleDbCommand(table, conn )
cmd.Parameters.Add("#uid", OleDbType.VarWChar).Value = textBox1.Text
conn.Open()
Dim result = cmd.ExecuteScalar()
if result IsNot Nothing then
Form2.Show()
End If
End Using
End Using
End Sub
I have a form which contains a ComboBox linked to an access database. I am asking the combobox to display the Incident ID, Supplier and Supply date of all records in the database table
The code is as follows
Private Sub frm_5_UpdateIncidentSelect_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Links dropdown menu to incident table in database
Dim dt As New DataTable
Dim query As String = "select [incident ID],[stock supplier],[supply date] from incident"
Using connection As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Database.accdb")
Using command As New OleDbCommand(query, connection)
Using adapter As New OleDbDataAdapter(command)
connection.Open()
adapter.Fill(dt)
connection.Close()
End Using
End Using
End Using
Dim MyDataRow As DataRow = dt.Rows(0)
Dim x As Integer
x = dt.Rows.Count
For y = 0 To x
If y < x Then
MyDataRow = dt.Rows(y)
ComboBox1.Items.Add(CStr(MyDataRow("Incident ID")))
ComboBox1.Items.Add(CStr(MyDataRow("stock supplier")))
ComboBox1.Items.Add(CStr(MyDataRow("supply date")))
End If
Next
End Sub
The issue I am having is that the data is being returned over 3 lines as follows
12
Supplier1
01/01/2015
13
Supplier2
07/01/2015
Ideally I need this information returned on one as follows
12 Supplier 1 01/01/2015
13 Supplier 2 07/01/2015
I cannot for the life of me figure this out, I am not great with VB I am afraid. Can anyone tell me where I am going wrong?
Here's an example of what Plutonix was talking about:
Public Class frm_5_UpdateIncidentSelect
Private Class ComboBoxData
Public IncidentID As String
Public StockSupplier As String
Public SupplyDate As String
Public Sub New(ByVal data As DataRow)
Me.IncidentID = data("Incident ID").ToString
Me.StockSupplier = data("stock supplier").ToString
Me.SupplyDate = data("supply date").ToString
End Sub
Public Overrides Function ToString() As String
Return Me.IncidentID.PadRight(5, " ") & " Supplier " & Me.StockSupplier.PadRight(5, " ") & " " & Me.SupplyDate
End Function
End Class
Private Sub frm_5_UpdateIncidentSelect_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Links dropdown menu to incident table in database
Dim dt As New DataTable
Dim query As String = "select [incident ID],[stock supplier],[supply date] from incident"
Using connection As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Database.accdb")
Using command As New OleDbCommand(query, connection)
Using adapter As New OleDbDataAdapter(command)
connection.Open()
adapter.Fill(dt)
connection.Close()
End Using
End Using
End Using
For Each Data As DataRow In dt.Rows
ComboBox1.Items.Add(New ComboBoxData(Data))
Next
End Sub
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
If ComboBox1.SelectedIndex <> -1 Then
Dim data As ComboBoxData = DirectCast(ComboBox1.SelectedItem, ComboBoxData)
Debug.Print(data.IncidentID)
Debug.Print(data.StockSupplier)
Debug.Print(data.SupplyDate)
End If
End Sub
End Class
It's been over 5 years, but I hope it can help someone.
The solution is actually way, way easier.
I had the same problem while coding a simple Programm for internal use.
Try this:
For i As Integer = 0 To dataSet.Tables(0).Rows.Count - 1
ComboBox1.Items.Add(dataSet.Tables(0).Rows(i)(0) + " | " +
dataSet.Tables(0).Rows(i)(1) + " | " + dataSet.Tables(0).Rows(i)(2))
Next
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