DataTable RowFilter is unchecking checkboxes in CheckedListBox - vb.net

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

Related

ctrl F in DataGridView

I'm trying to implement an equivalent to a ctrl+F function in my project. I want it to work like in an excel where the cursor points at the matching string in any cell. It can also work as a filter that only show the rows with the matching string.
EDIT 1 : Here is what i tried when using #Gabriel Stancu 's method :
Private Sub btnSearch_Click(sender As Object, e As EventArgs) Handles btnSearch.Click
'Dim search As New OleDbCommand
'Dim da As OleDbDataAdapter
Dim ds As New DataSet
Dim dt As New DataTable
Dim dgv As DataGridView
Dim searchedValue As String
frmPrinc.dgv.DataSource = dt.DefaultView
searchedValue = txtBoxName.Text
MsgBox(searchedValue) 'Displayed
For Each row As DataGridViewRow In frmPrinc.dgv.Rows
MsgBox("yes") 'Not displayed
For Each cell As DataGridViewCell In row.Cells
MsgBox("yes2") 'Not displayed
If cell.Value IsNot DBNull.Value Then
MsgBox("yes3") 'Not displayed
If cell.Value.ToString().Equals(searchedValue) Then
cell.Selected = True
MsgBox(cell.Selected)
Exit Sub
End If
End If
Next
Next
End Sub
this just cleared my DataGridView display
EDIT 2 : Here's what i'm currently working on using #jmcilhinney 's method :
Private Sub btnSearch_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnSearch.Click
Dim myBindingSource As New BindingSource
Dim myDataTable As New DataTable
Dim myDataGridView As New DataGridView
myBindingSource.DataSource = myDataTable
myDataGridView.DataSource = myBindingSource
Dim myValue As String
myValue = txtBoxName.Text
MsgBox(myValue)
myDataTable.Columns.Add(New DataColumn("NAME", GetType(String)))
myBindingSource.DataSource = myDataTable
myBindingSource.Position = myBindingSource.Find("NAME", myValue) 'Running fails on this line
'myBindingSource.Filter = $"MyColumn LIKE '%{myValue}% OR MyOtherColumn LIKE '%{myValue}%'"
Dim sourceRows = myBindingSource.Cast(Of DataRowView)().
Where(Function(drv) CStr(drv("NOM")).Contains(myValue)).
ToArray()
For Each gridRow As DataGridViewRow In myDataGridView.Rows
gridRow.Selected = sourceRows.Contains(DirectCast(gridRow.DataBoundItem, DataRowView))
Next
End Sub
This above is still work in progress but i'm a bit stuck getting a
System.ArgumentException : 'DataMember property' NAME 'could not be found in the DataSource.'
If you already have a populated grid then the code to search or filter has nothing at all to do with your database. What you should be doing is populating a DataTable, using either a data adapter or a data reader, binding that to a BindingSource and then binding that to your DataGridView, e.g.
myBindingSource.DataSource = myDataTable
myDataGridView.DataSource = myBindingSource
Searching or filtering is then using the BindingSource. To select the first record with a specific value in a specific column:
myBindingSource.Position = myBindingSource.Find("MyColumn", myValue)
To filter out rows that don't contain a partial value in any column:
myBindingSource.Filter = $"MyColumn LIKE '%{myValue}% OR MyOtherColumn LIKE '%{myValue}%'"
To select each row in the grid that matches those conditions without actually filtering:
Dim sourceRows = myBindingSource.Cast(Of DataRowView)().
Where(Function(drv) CStr(drv("MyColumn")).Contains(myValue)).
ToArray()
For Each gridRow As DataGridViewRow In myDataGridView.Rows
gridRow.Selected = sourceRows.Contains(DirectCast(gridRow.DataBoundItem, DataRowView))
Next
Okay thanks to #Gabriel Stancu this worked for me :
Private Sub btnSearch_Click(sender As Object, e As EventArgs) Handles btnSearch.Click
Dim da As OleDbDataAdapter
Dim ds As New DataSet
Dim dt As New DataTable
Dim dgv As New DataGridView
ds.Tables.Add(dt)
da = New OleDbDataAdapter("*", getConnexion) ' * here are my columns name
Dim cmdBuilder As New OleDbCommandBuilder(da)
cmdBuilder.QuotePrefix = "["
cmdBuilder.QuoteSuffix = "]"
da.Fill(dt) 'here is what i forgot
dgv.DataSource = dt.DefaultView
Dim searchedValue As String
frmPrinc.dgv.DataSource = dt.DefaultView
searchedValue = txtBoxName.Text
'MsgBox(searchedValue) 'breakpoint to make sure the input string was correct
For Each row As DataGridViewRow In frmPrinc.dgv.Rows
For Each cell As DataGridViewCell In row.Cells
If cell.Value IsNot DBNull.Value And cell.Value IsNot Nothing Then 'you also need to check for the "Nothing" value
If cell.Value.ToString().Equals(searchedValue) Then
frmPrinc.dgv.SelectionMode = DataGridViewSelectionMode.CellSelect 'add this to only select a cell, not the entire row
cell.Selected = True
Exit Sub
End If
End If
Next
Next
End Sub
As jmcilhinney specified in the comments, your code has nothing to do with what you want to achieve. A first approach that comes to my mind (might not be the most efficient) would be to go row by row and column by column. Something like:
Private Sub SearchCellValue(ByVal searchedValue As String)
For Each row As DataGridViewRow In dgvSample.Rows
For Each cell As DataGridViewCell In row.Cells
If cell.Value IsNot DBNull.Value And cell.Value IsNot Nothing Then 'you also need to check for the "Nothing" value
If cell.Value.ToString().Equals(searchedValue) Then
dgvSample.SelectionMode = DataGridViewSelectionMode.CellSelect 'add this to only select a cell, not the entire row
cell.Selected = True
Exit Sub
End If
End If
Next
Next
End Sub
You could also search in the DataTable which holds the data for the DataGridView, get the corresponding indexes (row and column) and then select the corresponding cell in the DataGridView (as it is not quite the best practice to implement code logic directly on the UI which should be separated). Anyway, both approaches seem faster than looking directly in the database, as your approach suggests you intend to do.
You could also make this a Boolean Function and return a value (True if you found the value, False otherwise). This is just a sample code to guide you. Before jumping into complex stuff, make sure you read some documentation or at least follow some tutorials step by step.

Adding associated double values from database when checkedlistbox item is selected

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

DataGridView is not showing first row after removing other rows

I am having a problem that I have been debugging for some days.
Some context, I have an application where I store the data (in the following code a list of class Expediente) in xml files using serialization. For displaying the data I deserialize it and then I create a datatable which I use as the datasource for the datagrid. I added a datagridcheckbox column to check the ones to delete.
In the datagrid I have a toolstrip button that when clicked it removes the checked -or checked in edit mode thus the line CType(dgvr.Cells(0).GetEditedFormattedValue(dgvr.Index, DataGridViewDataErrorContexts.Commit), Boolean) = True- elements from the datasource and persists the datasource to an xml.
The problem is that when deserializing the xml and loading it again to a datatable and binding the datagrid datasource to it, it removes the first row. If I close the application and open it, the first row appears.
I have debuged and in the datatable the correct numbers of rows appear. Moreover, I have narrowed the problem to the binding navigator, when it sets its binding source to the datable there the first row of the datagrid disappears.
I paste a simplified version of the code, I can provide more if needed.
Note that if in LoadGridExpedientes() I comment the lines
navExpedientes.BindingSource = Nothing
navExpedientes.BindingSource = bs
The first row doesn't disappear but obviously the counter of the binding navigator does not update.
Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim chk As New DataGridViewCheckBoxColumn()
grdExpedientes.Columns.Add(chk)
chk.HeaderText = ""
chk.Name = "chk"
'fills a datatable from an xml, it works ok, it fills it with the correct amount of rows
Dim dt As DataTable = Sistema.getInstance.getDataExpedienteForGrid()
Dim bs As New BindingSource
bs.DataSource = dt
grdExpedientes.DataSource = bs
navExpedientes.BindingSource = bs
For i As Integer = 0 To grdExpedientes.Rows.Count - 2
grdExpedientes.Rows(i).Cells(0).Value = True
Next
End Sub
Private Sub LoadGridExpedientes()
grdExpedientes.DataSource = Nothing
navExpedientes.BindingSource = Nothing
grdExpedientes.Columns.Clear()
grdExpedientes.Rows.Clear()
'This is to know if there is already the checkbox column
If Not (grdExpedientes.Columns.Count > 0 AndAlso grdExpedientes.Columns(0).Name = "chk") Then
Dim chk As New DataGridViewCheckBoxColumn()
grdExpedientes.Columns.Add(chk)
chk.HeaderText = ""
chk.Name = "chk"
End If
Dim dt As DataTable = Sistema.getInstance.getDataExpedienteForGrid()
Dim bs As New BindingSource
bs.DataSource = dt
grdExpedientes.DataSource = bs
navExpedientes.BindingSource = bs
End Sub
Private Sub navExpedientesDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles navExpedientesDelete.Click
For i As Integer = 0 To grdExpedientes.Rows.Count - 2
Dim dgvr As DataGridViewRow = grdExpedientes.Rows(i)
If CType(dgvr.Cells(0).Value, Boolean) = True Or _
CType(dgvr.Cells(0).GetEditedFormattedValue(dgvr.Index, DataGridViewDataErrorContexts.Commit), Boolean) = True Then
Dim draux As DataGridViewRow = dgvr
Dim expABorrar As Expediente = CType((From elem As Expediente In Sistema.listExpedientes
Where elem.Expediente = CType(draux.Cells("Expediente (Ficha)").Value, String)
Select elem).FirstOrDefault, Expediente)
Sistema.listExpedientes.Remove(expABorrar)
End If
Next
If System.IO.File.Exists(Sistema.pathListExpedientes) Then
System.IO.File.Delete(Sistema.pathListExpedientes)
End If
Dim sw As System.IO.TextWriter = New System.IO.StreamWriter(Sistema.rutaListaExpedientes, 0)
Serializer(Of Expediente).Serialize(Sistema.listExpedientes, sw, New List(Of Type) From {GetType(Movimiento)})
sw.Close()
LoadGridExpedientes()
End Sub
The problem was that I was using the delete toolstrip button that comes with the binding navigator, it seems that when clicked it has 'embedded' that it deletes the row that was selected that by default is the first one.
I created a new toolstrip button in the binding navigator and the problem was solved. I do not know why not updating the binding navigator binding source also avoided the first row from being removed.

How To Clear CheckedListBox From Previous Selection - VB.NET

Okay so I have a checkedlistbox that when a user selects a item it will print out that item into word. That works perfect. However, I want to give the user the option to not select anything at all, but when the user does not select an item in the checkboxlist, it still prints out the previous selected item into MS word.
Below is my code:
Private Sub ExportContactOkButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExportContactOkButton.Click
Dim i As Integer
Dim array_Counter_Contacts As Integer
Dim array_size_Contacts As Integer
array_Counter_Contacts = 0
For i = 0 To ExportContactCheckedListBox.Items.Count() - 1
If ExportContactCheckedListBox.GetItemCheckState(i) = CheckState.Checked Then
array_size_Contacts = UBound(SelectedContacts)
ReDim Preserve SelectedContacts(array_size_Contacts + 1)
SelectedContacts(array_Counter_Contacts) = ExportContactCheckedListBox.Items(i)
If Search.debugging = True Then
MsgBox(ExportContactCheckedListBox.Items(i))
End If
array_Counter_Contacts += 1
ExportContactCheckedListBox.Items(i) = ExportContactCheckedListBox.Items(i).ToString.Replace("'", "''")
If array_Counter_Contacts = 1 Then
ContactNames = "" & ExportContactCheckedListBox.Items(i) & ""
Else
ContactNames = String.Concat(ContactNames & "', '" & ExportContactCheckedListBox.Items(i) & "")
End If
End If
Next
If Search.debugging = True Then
MsgBox(ContactNames)
End If
sConnection.Close()
Dispose()
Me.Close()
End Sub
I have tried remove, clear, and I even tried this
Dim i As Integer
For i = 0 To ExportContactCheckedListBox.CheckedIndices.Count - 1
ExportContactCheckedListBox.SetItemChecked(ExportContactCheckedListBox.CheckedIndices(0), False)
Next i
But nothing is working. Can anyone help me? All I want is to be able to have the checkedlistbox forget or clear the checked item after the "OK" button is pressed and the text has already been printed into word.
Use a List(Of String) to store the selection and, of course remember, to reinitialize the list when you hit the ExportContactOkButton
' Declared at the form level....
Dim SelectedContacts as List(Of String)
.....
Private Sub ExportContactOkButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExportContactOkButton.Click
Dim i As Integer
SelectedContacts = new List(Of String)()
For i = 0 To ExportContactCheckedListBox.Items.Count() - 1
If ExportContactCheckedListBox.GetItemCheckState(i) = CheckState.Checked Then
SelectedContacts.Add(ExportContactCheckedListBox.Items(i))
.....
End If
Next
End Sub
In this way, every time you hit the ExportContactOKButton you reinitialize your list of contacts, loop through the checked items, add the checked one to your list.
A List(Of String) is better because you don't need to know in advance how many items your user selects in the CheckedListBox and continuosly resize the array. You simply add new items to it. And you could always use it like an array
Dim Dim array_Counter_Contacts = SelectedContacts.Count
For i = 0 to array_Counter
Console.WriteLine(SelectedContacts(i))
Next

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