Adding associated double values from database when checkedlistbox item is selected - vb.net

Edited
Hi I have this program where I need to display the sum of the values when read from the database. I tried something like this:
Dim selec As String
Dim con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\rose&mike\Desktop\DbSysDel3\salondbaccess.accdb")
Dim dt2 As New DataTable
selec = ""
con.Open()
For Each incheck In chcklstbx1.CheckedIndices
Dim valName As String
valName = chcklstbx1.Items.Item(incheck).ToString
Dim sqlstr2 As String = "SELECT Service_Fee FROM Service_Types WHERE [Service_Name] = '" & valName & "'"
Dim cmd As New OleDbCommand(sqlstr2, con)
Dim sum As Double = 0
Dim rdr As OleDbDataReader = cmd.ExecuteReader
If rdr.HasRows Then
While rdr.Read
selec += "P" + rdr("Service_Fee").ToString & ControlChars.NewLine
sum = sum + rdr("Service_Fee")
End While
End If
lblFees.Text = selec
lblTotal.Text = sum.ToString
rdr.Close()
Next
con.Close()
But all it does is display the values I want to add one by one when checked.

Here's an example of how this whole thing should be handled:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Using connection As New OleDbConnection("connection string here"),
adapter As New OleDbDataAdapter("SELECT Name, Amount FROM MyTable", connection)
Dim table As New DataTable
adapter.Fill(table)
'Bind the DataTable to the CheckedListBox.
'Normally the DataSource should be set last but, in my experience, that can cause problems with a CheckedListBox.
With CheckedListBox1
.DataSource = table
.DisplayMember = "Name"
.ValueMember = "Amount"
End With
End Using
End Sub
Private Sub CheckedListBox1_ItemCheck(sender As Object, e As ItemCheckEventArgs) Handles CheckedListBox1.ItemCheck
'Get all the currently checked items.
Dim checkedItems = CheckedListBox1.CheckedItems.Cast(Of DataRowView)().ToList()
'Get the item that is being checked or unchecked.
Dim currentItem = DirectCast(CheckedListBox1.Items(e.Index), DataRowView)
If e.NewValue = CheckState.Checked Then
'The current item is being checked so add it to the list.
checkedItems.Add(currentItem)
Else
'The current item is being unchecked so remove it from the list.
checkedItems.Remove(currentItem)
End If
'Sum the Amounts from each checked item.
Dim sum = checkedItems.Sum(Function(drv) CDec(drv("Amount")))
'Use sum here.
'...
End Sub
You get all the data up front, including all the text to display and all the associated numeric values. That data can be bound to the CheckedListBox as a DataTable, keeping it all packaged together.
Each time the user checks or unchecks an item in the list, you perform the calculation based on the data you already have. No need to go back to the database.
The important thing to note about the ItemCheck event is that it is raised BEFORE the item is checked or unchecked. That's why the code needs to ad the current item, or remove it from, the list of checked items.
When you bind a DataTable, each bound item is a DataRowView, i.e. an item from the table's DefaultView. You get all the checked DataRowView items, get their numeric value and sum them. Note that I've used a LINQ query to calculate the sum succinctly but you could use a loop if you wanted to, e.g.
Dim sum As Decimal = 0D
For Each drv In checkedItems
sum += CDec(drv("Amount"))
Next

Related

How to populate master detail combobox in VB?

I have two Comboboxes with Master Detail Relationship Table Bank and Branch
My VB code behind:-
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Using con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\New\Test.accdb")
Using cmd As New OleDbCommand("Select Bank, ID from Bank", con)
Dim da As New OleDbDataAdapter(cmd)
Dim dt As New DataTable
da.Fill(dt)
ComboBox1.DataSource = dt
ComboBox1.DisplayMember = "Bank"
ComboBox1.ValueMember = "ID"
ComboBox1.Text = "Select"
End Using
End Using
End Sub
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
Dim bankId = ComboBox1.SelectedValue.ToString
Using con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\New\Test.accdb")
Using branch_cmd As New OleDbCommand("Select Branch from Branch where Bank_id ='" & bankId & "'", con)
Dim da As New OleDbDataAdapter(branch_cmd)
Dim dt As New DataTable
da.Fill(dt)
ComboBox2.DataSource = dt
ComboBox2.DisplayMember = "Bank"
ComboBox2.ValueMember = "ID"
End Using
End Using
End Sub
End Class
I want to populate in second combo box based on first combobox selected value, but the code got error on
ComboBox1_SelectedIndexChanged function:
And, from debugging the branch_cmd sql is:
Select Branch from Branch where Bank_id ='System.Data.DataRowView'
EDIT:
I was about to add a note and then I realised that this note is actually the solution to your actual problem. You are setting the DataSource first and then the DisplayMember and ValueMember afterwards. That is wrong and the reason for your issue. When you set the DataSource you have done the binding, so everything happens then and there. The first item in the list is selected so your SelectedIndexChanged handler is executed. You haven't set the DisplayMember or ValueMember yet, so the SelectedValue won't return the appropriate value. ALWAYS set the DataSource last, as I have done in my example below.
ORIGINAL:
Unless you have a large amount of data, you should just get all the data from both tables upfront, bind the data and then let the binding take care of the filtering automatically. You do that using BindingSources and binding the child to a DataRelation rather than a DataTable. Behold!
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim parentTable = GetParentTable()
Dim childTable = GetChildTable()
Dim data As New DataSet
'Create a foreign key relation between the tables.
data.Tables.Add(parentTable)
data.Tables.Add(childTable)
data.Relations.Add("ParentChild", parentTable.Columns("ParentId"), childTable.Columns("ParentId"))
'Bind the parent BindingSource to the parent table.
parentBindingSource.DataMember = "Parent"
parentBindingSource.DataSource = data
'Bind the child BindingSource to the relation.
childBindingSource.DataMember = "ParentChild"
childBindingSource.DataSource = parentBindingSource
parentComboBox.DisplayMember = "ParentName"
parentComboBox.ValueMember = "ParentId"
parentComboBox.DataSource = parentBindingSource
childComboBox.DisplayMember = "ChildName"
childComboBox.ValueMember = "ChildId"
childComboBox.DataSource = childBindingSource
End Sub
Private Function GetParentTable() As DataTable
Dim table As New DataTable("Parent")
table.PrimaryKey = {table.Columns.Add("ParentId", GetType(Integer))}
table.Columns.Add("ParentName", GetType(String))
table.Rows.Add(1, "Parent 1")
table.Rows.Add(2, "Parent 2")
table.Rows.Add(3, "Parent 3")
Return table
End Function
Private Function GetChildTable() As DataTable
Dim table As New DataTable("Child")
table.PrimaryKey = {table.Columns.Add("ChildId", GetType(Integer))}
table.Columns.Add("ChildName", GetType(String))
table.Columns.Add("ParentId", GetType(Integer))
table.Rows.Add(1, "Child 1.1", 1)
table.Rows.Add(2, "Child 1.2", 1)
table.Rows.Add(3, "Child 1.3", 1)
table.Rows.Add(4, "Child 2.1", 2)
table.Rows.Add(5, "Child 2.2", 2)
table.Rows.Add(6, "Child 2.3", 2)
table.Rows.Add(7, "Child 3.1", 3)
table.Rows.Add(8, "Child 3.2", 3)
table.Rows.Add(9, "Child 3.3", 3)
Return table
End Function
If you do that, selecting a parent will automatically filter the children displayed for selection.
In case it's not obvious, you would get the parent and child tables by querying a database rather than building them manually, as I have done in my example.
As it stands now I can see two errors. First, do not concatenate strings to build sql command texts. Use always a parameterized query, specifying exactly what is the datatype of the parameter that you are passing. Second error is in the DisplayMember and ValueMember for the second combo. You don't have here a Bank_Id or a Bank name but the Branch name
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
' Always check if you have a valid selection to avoid NRE.
if ComboBox1.SelectedValue Is Nothing Then
Return
End if
' If bankid is an integer then convert to an integer
Dim bankId as Integer = Convert.ToInt32(ComboBox1.SelectedValue)
Using con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\New\Test.accdb")
Using branch_cmd As New OleDbCommand("Select Branch from Branch where Bank_id =#id", con)
cmd.Parameters.Add("#id", OleDbType.Integer).Value = bankId
Dim da As New OleDbDataAdapter(branch_cmd)
Dim dt As New DataTable
da.Fill(dt)
ComboBox2.DataSource = dt
ComboBox2.DisplayMember = "Branch"
ComboBox2.ValueMember = "Branch"
End Using
End Using
End Sub
According to your comment below you get a System.Data.DataRowView as the element contained in the SelectedValue. This should not happen with the code shown in your question, so perhaps, there is something different that creates the problem. (For example, if the datatable fields names don't match with the ValueMember/DisplayMember properties)
In any case, from a DataRowView, you should be able to get the integer in this way
Dim drv = DirectCast(ComboBox1.SelectedValue, DataRowView)
if drv IsNot Nothing then
Dim bankid = Convert.ToInt32(drv("ID"))
...
End if

DataTable RowFilter is unchecking checkboxes in CheckedListBox

I have a search function set up on a textbox. When text is typed into the box, the checkedlistbox visible items are filtered. When you check one of the visible boxes, then erase the search text, the checked item loses its check. Is there any way around this?
Search box:
Private Sub txtSearchBidder_TextChanged(sender As Object, e As EventArgs) Handles txtSearchBidder.TextChanged
If Not String.IsNullOrWhiteSpace(txtSearchBidder.Text) Then
dtBidder.DefaultView.RowFilter = "Bidders LIKE '%" & txtSearchBidder.Text & "%'"
End If
End Sub
CheckedListBox:
Dim dtBidder as new DataTable
dtBidder.Columns.Add("Bidders")
For Each bidder As String In cmd1.Parameters("#Bidders").Value.ToString().Split(",")
dtBidder.Rows.Add(bidder)
Next
Dim source1 As New BindingSource()
source1.DataSource = dtBidder
chkListBidders.BindingContext = New BindingContext()
chkListBidders.DataSource = source1
chkListBidders.DisplayMember = "Bidders"
chkListBidders.ValueMember = "Bidders"
As suggested, there's nothing in the control or the binding that will do this for you, so you need to do it yourself, e.g.
Dim checkedItems = CheckedListBox1.CheckedItems.Cast(Of DataRowView)().ToArray()
'Perform filtering here.
For i = 0 To CheckedListBox1.Items.Count - 1
CheckedListBox1.SetItemChecked(i, checkedItems.Contains(DirectCast(CheckedListBox1.Items(i), DataRowView)))
Next

Combobox - display 2 Datatable columns

I created multiple-column drop-down list for a combobox from a DataTable, and now I want to display both columns in it too. So far only 1 column is displayed (with DisplayMember property). So basically I want Autocomplete with both columns displayed in combobox. I would be satisfied with displaying 2nd column in Textbox next to combobox too, but It must work as Autocomplete (when selected index changes, display value changes too). I need this because both Datable values (Name & Surname) will be added in another DB table together, and for user to see both values in same place.
EDITED:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim SQL As String = "SELECT ID,Name ||' ' || Surname as FullName from MyTable"
Dim dtb As New DataTable()
dtb.Columns.Add("Name", System.Type.GetType("System.String"))
dtb.Columns.Add("Surname", System.Type.GetType("System.String"))
Using con As OracleConnection = New OracleConnection("Data Source=MyDB;User Id=Lucky;Password=MyPassword;")
Try
con.Open()
Using dad As New OracleDataAdapter(SQL, con)
dad.Fill(dtb)
End Using
Combobox1.DataSource = dtb
Combobox1.DisplayMember = "FullName"
Combobox1.ValueMember= "ID"
con.Close()
Catch ex As Exception
'MessageBox.Show(ex.Message)
Finally
con.Dispose()
End Try
End Using
End Sub
And drawing line between combobox columns:
Private Sub Combobox1_DrawItem(sender As Object, e As DrawItemEventArgs) Handles Combobox1.DrawItem
e.DrawBackground()
Dim drv As DataRowView = CType(Combobox1.Items(e.Index), DataRowView)
Dim id As String = drv("Name").ToString()
Dim name As String = drv("Surname").ToString()
Dim r1 As Rectangle = e.Bounds
r1.Width = r1.Width / 2
Using sb As SolidBrush = New SolidBrush(e.ForeColor)
e.Graphics.DrawString(id, e.Font, sb, r1)
End Using
Using p As Pen = New Pen(Color.AliceBlue)
e.Graphics.DrawLine(p, r1.Right, 0, r1.Right, r1.Bottom)
End Using
Dim r2 As Rectangle = e.Bounds
r2.X = e.Bounds.Width / 2
r2.Width = r2.Width / 2
Using sb As SolidBrush = New SolidBrush(e.ForeColor)
e.Graphics.DrawString(name, e.Font, sb, r2)
End Using
End Sub
Any suggestions ? Thanks in advance !!
Checkout this answer: https://stackoverflow.com/a/5570901/6550457
It suggests:
comboBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
comboBox1.AutoCompleteSource = AutoCompleteSource.ListItems;
Only way to achieve all is to add another combobox and bind him to same datatable. This way when you select item from combobox you see both values in them. Autocomplete works too. Doing everything in same combobox is not good, displayed text is too wide, doesn't look good.

Populating Listbox with Data from DataGridView

I've got 2 forms. Form A has a listbox and a Combobox. Form B has a DataGridView. In my A, my combobox is meant to represent groups for a task. e.g. Inbox, Important etc. So whenever I select an item from the comobobox e.g. I select the "Inbox" Item the DataGridViewer will sort all the rows which contain "Inbox" in one of the columns. This all works fine. I can view the sorted data.
To Load my data into the DataGridView I use:
Dim ds As DataSet
Dim dataset1 As New DataSet("datasetTasks")
Dim table1 As New DataTable("tableTask")
Private Sub Main_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ds = CreateDataset()
frm_Tasks.DataGridView1.DataSource = ds.Tables("tableTask")
LoadFromXMLfile("C:\Users\Beta4K\Documents\Tasks.FILE")
For Each dr As DataRow In ds.Tables(0).Rows
ListBox1.Items.Add(dr("TaskName").ToString())
Next
End Sub
Private Sub LoadFromXMLfile(filename As String)
If System.IO.File.Exists(filename) Then
Dim xmlSerializer As XmlSerializer = New XmlSerializer(ds.GetType)
Dim readStream As FileStream = New FileStream(filename, FileMode.Open)
ds = CType(xmlSerializer.Deserialize(readStream), DataSet)
readStream.Close()
frm_Tasks.DataGridView1.DataSource = ds.Tables("tableTask")
Else
MsgBox("file not found! add data and press save button first.", MsgBoxStyle.Exclamation, "")
End If
End Sub
Private Function CreateDataset() As DataSet
table1.Columns.Add("TaskID")
table1.Columns.Add("TaskName")
table1.Columns.Add("TaskMessage")
table1.Columns.Add("TaskDate")
table1.Columns.Add("TaskTime")
table1.Columns.Add("TaskGroup")
dataset1.Tables.Add(table1)
Return dataset1
End Function
Here's the code for my Combobox:
ListBox1.Items.Clear()
Dim dv As New DataView(ds.Tables("tableTask"))
dv.RowFilter = "TaskGroup = '" + ComboBox1.SelectedItem + "'"
frm_Tasks.DataGridView1.DataSource = dv.ToTable("tableTask")
For Each dr As DataRow In ds.Tables(0).Rows
ListBox1.Items.Add(dr("TaskName").ToString())
Next
What this is meant to do is that it clears the listbox, and then reloads the items into the listbox from reading all the data in the DataGridViewer. Since it's already sorted all it has to do is add the items but it doesn't. Instead it just adds all the items regardless of the filter.
Can someone help me.
You loop over the datatable while you need to loop over the dataview
For Each dr As DataRowView In dv
ListBox1.Items.Add(dr("TaskName").ToString())
Next

How to associate a timer to my function..?

I am facing another issue. I want to associate a timer to my function.
Actually what i am doing is that i am tryin to store certain values in a database. A function is such that it calculates 5 values and stores them in a database. While storing them in the database it displays them in a textbox on the form. Now i want to display them one by one which it does. But it does it so fast that i can only see the last value that it enters in the database inside the textbox.
Can i make it show all the values slowly one by one.
Private Sub dbInsert(ByVal strfile As String, ByVal hashoffile As String, ByVal p As Integer)
'parameter to the above function (ByVal strfile As String)
DbConnection()
cmd = "select * from hashtable"
da = New OleDb.OleDbDataAdapter(cmd, con)
da.Fill(ds, "values")
maxrows = ds.Tables("values").Rows.Count
TextBox1.Text = p + 1
TextBox2.Text = hashoffile
TextBox3.Text = strfile
Dim cb As New OleDb.OleDbCommandBuilder(da)
Dim dsnewrow As DataRow
dsnewrow = ds.Tables("values").NewRow()
dsnewrow.Item("p_id") = TextBox1.Text
dsnewrow.Item("process_name") = TextBox2.Text
dsnewrow.Item("hash_value") = TextBox3.Text
ds.Tables("values").Rows().Add(dsnewrow)
da.Fill(ds)
da.Update(ds, "values")
'MsgBox("new Item added to database")
con.Close()
'System.Threading.Thread.Sleep(1000)
End Sub
Add a Timer Control to your form
Move your code (retrieving data and displaying data) to Timer Controls tick event
Set Timer controls Interval Property as you required, to make it slow displaying values.
Call TIMERCONTROLNAME.Enabled = True to start timer.