Shuffle the contents of Text Boxes - vb.net

I'm trying to get 10 different inputs in TextBoxes to be displayed in a randomized order.
I split 2 processes to make it easier to understand:
Button puts numbers in random orders from 1-10(works)
Button should replace the numbers with the contents of the TextBoxes(doesn't work)
Here is the GUI
/ Here is the code:
Public tblRandom As New DataTable
Private Sub btnRandom_Click(sender As Object, e As EventArgs) Handles btnGo.Click
Dim r As New Random
Dim tblRandom As New DataTable
tblRandom.Columns.Add("Order")
tblRandom.Constraints.Add("pk", tblRandom.Columns(0), True)
While tblRandom.Rows.Count < 10
Dim newrow As Object = r.Next(10) + 1
Try
tblRandom.Rows.Add(CStr(newrow))
Catch ex As Exception
End Try
End While
dgvRandom.DataSource = tblRandom
End Sub
Private Sub btnReplace_Click(sender As Object, e As EventArgs) Handles btnReplace.Click
For Each row As DataGridViewRow In dgvRandom.Rows
For Each cell As DataGridViewCell In row.Cells
If cell.Value IsNot Nothing Then
Dim i As Integer = 0
While i < 10
i += 1
If cell.Value.ToString = i Then
cell.Value = ActiveControl.Tag(i)
End If
End While
End If
Next
Next
dgvRandom.DataSource = tblRandom
End Sub
End Class

My understanding of your goal here is to take the text in 10 textboxes and shuffled their content. The DataGrid seems to be a temporary location that you're using during the shuffle.
There's a much easier way that avoids all that. Try this code:
Private _random = New Random()
Private Sub BtnRandom_Click(sender As Object, e As EventArgs) Handles btnRandom.Click
Dim tbs As TextBox() = {TextBox1, TextBox2, TextBox3, TextBox4, TextBox5, TextBox6, TextBox7, TextBox8, TextBox9, TextBox10}
Dim text = tbs.Select(Function(tb) tb.Text).OrderBy(Function(t) _random.Next()).ToList()
For Each x In tbs.Zip(text, Function(tb, t) New With {.tb = tb, .t = t})
x.tb.Text = x.t
Next
End Sub
That's it. Job done.
If you still need the DataGrid it would be easy to add a further line just after the x.tb.Text = x.t that adds the content of x.t to the grid.

For the first part you can do:
Public tblRandom As DataTable
Private ReadOnly rand As New Random
Private Sub btnRandom_Click(sender As Object, e As EventArgs) Handles btnRandom.Click
tblRandom?.Dispose()
tblRandom = New DataTable
tblRandom.Columns.Add("Order")
tblRandom.Constraints.Add("pk", tblRandom.Columns(0), True)
Dim nums As New List(Of Integer)
While nums.Count < 10
Dim num = rand.Next(1, 11)
If Not nums.Contains(num) Then
nums.Add(num)
End If
End While
nums.ForEach(Sub(n) tblRandom.Rows.Add(n))
dgvRandom.DataSource = tblRandom
End Sub
As for the second part, you just need to do:
Private Sub btnReplace_Click(sender As Object, e As EventArgs) Handles btnReplace.Click
For Each row As DataRow In tblRandom.Rows
'Assuming the text boxes are hosted by the Form.
Dim txt = Me.Controls.OfType(Of TextBox).
Where(Function(x) x.Tag?.ToString = row(0).ToString).
FirstOrDefault
If txt IsNot Nothing Then
row(0) = txt.Text
End If
Next
tblRandom.AcceptChanges()
End Sub

You are getting the tag from the ActiveControl
cell.Value = ActiveControl.Tag(i)
but as you've just clicked the Replace button, isn't that the active control?

Related

How to check if a value is duplicate and if duplicate need to delete both line in Datatable

I have a Datatable with few Line of Data, my aim is to check whether there is any Duplicate value and if there a duplicate line, i that Duplicate Value & i need to remove both lines that created duplicate.
Input
my expected Output would be
Any Suggestion will be helpful
Thanks in Advance
Thanks & Regards
Harsha
You need some way of getting a unique value for each row which depends only on the data in the row, this is usually known as a hash value.
Then you can use whatever method you like to bin the data and choose only the bins with one item.
For example, with just a DataGridView and a Button on a Form:
Public Class Form1
Dim dt As New DataTable
Function RowHash(dr As DataRow) As Long
'TODO: Make a hash function which will not overflow a Long.
Dim h As Long = 0
For Each itm In dr.ItemArray
h = (31 * h) + itm.GetHashCode()
Next
Return h
End Function
Sub MakeTestData()
dt.Columns.Add("Name")
dt.Columns.Add("P")
For i = 0 To 9
Dim nr = dt.NewRow()
nr("Name") = Chr(65 + (i Mod 6))
nr("P") = i Mod 6
dt.Rows.Add(nr)
Next
DataGridView1.DataSource = dt
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim newDataTable = dt.Clone()
Dim uniqueData = dt.AsEnumerable().
GroupBy(Function(r) RowHash(r)).
Select(Function(s) New With {.n = s.Count, .data = s.First().ItemArray}).
Where(Function(t) t.n = 1)
For Each u In uniqueData
Dim nr = newDataTable.NewRow()
nr.ItemArray = u.data
newDataTable.Rows.Add(nr)
Next
DataGridView1.DataSource = newDataTable
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
MakeTestData()
End Sub
End Class
Before and after clicking the button:

function that runs when dynamically generated control is interacted with

I've been Having trouble with making a sub that runs when my datagridview cell is doubleclicked. It is caused because the datagridview is programmatically created, rather than created by the designer. I have found a help website i will include that appears to be related to the issue.
Public Class seattemplatecreator
Dim alphabet() As Char = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
Private WithEvents dgv_flightTemplate As DataGridView
'help from https://it.toolbox.com/question/event-for-dynamically-created-command-button-043008
Public Sub init(ByVal dgv01 As DataGridView)
dgv_flightTemplate = dgv01
End Sub
Private Sub dgv_flightTemplate_CellMouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles dgv_flightTemplate.CellMouseDoubleClick
MsgBox("workwd")
End Sub
Private Sub btn_createflight_Click(sender As Object, e As EventArgs) Handles btn_createflight.Click
'used https://social.msdn.microsoft.com/Forums/vstudio/en-US/e222f438-f060-4e61-ab28-523d02db91b2/how-to-programmatically-create-datagridview-with-empty-columns-and-rows?forum=vbgeneral
'to help with this part for automatically generating the datagridview
MsgBox(alphabet(0))
Dim dgv_flightTemplate As New DataGridView
Dim c As Integer = txb_columns.Text
Dim r As Integer = txb_rows.Text
For colcount As Integer = 0 To c - 1
Dim nc As New DataGridViewTextBoxColumn
nc.Name = "Seating Column"
dgv_flightTemplate.Columns.Add(nc)
Next
dgv_flightTemplate.Rows.Add(r)
For x = 0 To r - 1
dgv_flightTemplate.Rows(x).HeaderCell.Value = alphabet(x).ToString
Next
Me.Controls.Add(dgv_flightTemplate)
dgv_flightTemplate.Location = New Point(400, 400)
dgv_flightTemplate.AllowUserToAddRows = False
dgv_flightTemplate.AllowUserToDeleteRows = False
dgv_flightTemplate.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders
dgv_flightTemplate.AutoResizeRows()
dgv_flightTemplate.AutoSize = True
End Sub
End Class
https://it.toolbox.com/question/event-for-dynamically-created-command-button-043008
Edit: Olivier Jacot-Descombes response was perfect all that was needed was run the "Init" sub.
Public Class seattemplatecreator
Dim alphabet() As Char = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
Private WithEvents dgv_flightTemplate As DataGridView
'help from https://it.toolbox.com/question/event-for-dynamically-created-command-button-043008
Public Sub init(ByVal dgv01 As DataGridView)
dgv_flightTemplate = dgv01
End Sub
Private Sub dgv_flightTemplate_CellMouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles dgv_flightTemplate.CellMouseDoubleClick
MsgBox("workwd")
End Sub
Private Sub btn_createflight_Click(sender As Object, e As EventArgs) Handles btn_createflight.Click
'used https://social.msdn.microsoft.com/Forums/vstudio/en-US/e222f438-f060-4e61-ab28-523d02db91b2/how-to-programmatically-create-datagridview-with-empty-columns-and-rows?forum=vbgeneral
'to help with this part for automatically generating the datagridview
MsgBox(alphabet(0))
Dim dgv_flightTemplate As New DataGridView
Dim c As Integer = txb_columns.Text
Dim r As Integer = txb_rows.Text
For colcount As Integer = 0 To c - 1
Dim nc As New DataGridViewTextBoxColumn
nc.Name = "Seating Column"
dgv_flightTemplate.Columns.Add(nc)
Next
dgv_flightTemplate.Rows.Add(r)
For x = 0 To r - 1
dgv_flightTemplate.Rows(x).HeaderCell.Value = alphabet(x).ToString
Next
Me.Controls.Add(dgv_flightTemplate)
dgv_flightTemplate.Location = New Point(400, 400)
dgv_flightTemplate.AllowUserToAddRows = False
dgv_flightTemplate.AllowUserToDeleteRows = False
dgv_flightTemplate.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders
dgv_flightTemplate.AutoResizeRows()
dgv_flightTemplate.AutoSize = True
init(dgv_flightTemplate)
End Sub
End Class
Any help would be greatly appreciated
Thanks, Taine
You are not calling Init. But instead, you could as well directly assign to the field
dgv_flightTemplate = New DataGridView 'Note: No Dim here.
But even if you are creating the columns dynamically, you could add a grid with the designer. Just call
dgv_flightTemplate.Columns.Clear()
before adding columns.

Control Event (Changed) In DataTable

Helo:
How to overcome the problem of [RowChanged] event within the [DataTable] in the [DataGridView].
Explanation: You have created a column in the dataTable to find out which rows have been modified, in order to facilitate the knowledge of these rows to update them in the database, but the problem in the event(Row_Changed), can not be explained by more words in the code, see the column of (ISCHANGE) after modifying any row and cover the The Save button you find takes the value True and then False;
Public Class Class1
Public Table As DataTable
Sub New()
Table = New DataTable
Table.Columns.Add("IsChange", System.Type.GetType("System.Boolean"))
Table.Columns.Add("TeacherName", System.Type.GetType("System.String"))
Table.Columns("IsChange").DefaultValue = False
Dim dr As DataRow = Table.Rows.Add
dr("TeacherName") = "X1"
dr = Table.Rows.Add
dr("TeacherName") = "X2"
dr = Table.Rows.Add
dr("TeacherName") = "X3"
dr = Table.Rows.Add
AddHandler Table.RowChanged, New DataRowChangeEventHandler(AddressOf Row_Changed)
End Sub
Sub New(SourceDataTable As DataTable)
'Import DataTable
Table = SourceDataTable
End Sub
Public Sub Save()
GotoChange = False 'disable the event (Row_Changed)
For ALoop As Integer = 0 To Table.Rows.Count - 1
'=======================================
'Here Code Save To DataBase
'
'
'=======================================
Table(ALoop)("IsChange") = False 'RowChanged =False
Next
GotoChange = True 'Enabled the event (Row_Changed)
End Sub
Dim GotoChange As Boolean = True 'This variable is to disable the event (Row_Changed)
Private Sub Row_Changed(ByVal sender As Object, ByVal e As DataRowChangeEventArgs)
If Not GotoChange Then Exit Sub
If e.Action = DataRowAction.Add Or e.Action = DataRowAction.Change Then
If CBool(e.Row("IsChange")) = False Then
MsgBox("IsChange")
e.Row("IsChange") = True 'If [True] this means that the row has been changed
End If
End If
End Sub
End Class
Form Code :
Change Any Row Then Button Save:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim A As New Class1
Me.DataGridView1.DataSource = A.Table
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim A As New Class1(Me.DataGridView1.DataSource)
'Change Value Any Row Then Button Save
'Here Problem: Show Column(IsChang) After Save
'The value First (False) Then (True)
A.save()
End Sub
End Class

vb2008 search through listbox using textbox

hi I have a form that find, or should I say filter, the items in a listbox using a textbox. I have a textbox used for searching and a listbox populated with items from database. Now, say listbox items include apple, banana, berry, cashew, lemon, mango, peanut. If I typed 'b' on the textbox, listbox only show banana and berry..if I typed 'ba' then listbox only show banana but if I typed 'be' then it shows berry and so on. I already got this working (with the code marked as commented in the txtSearch event). My problem is that how can I bring the items in the listbox back when the user strike the backspace? Because, say I have banana and berry in the listbox now, when I erased the text that I typed in the textbox it should list back all the items again so that if I want to search another item it will be filtered again. thanks in advance.
Code Update
Public Class Glossary
Private Sub Glossary_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Call List()
Refreshlist()
End Sub
Private Sub List()
Dim myCmd As New MySqlCommand
Dim myReader As MySqlDataReader
Dim myAdptr As New MySqlDataAdapter
Dim myDataTable As New DataTable
Call Connect()
With Me
STRSQL = "Select word from glossary"
Try
myCmd.Connection = myConn
myCmd.CommandText = STRSQL
myReader = myCmd.ExecuteReader
If (myReader.Read()) Then
myReader.Close()
myAdptr.SelectCommand = myCmd
myAdptr.Fill(myDataTable)
lstword.DisplayMember = "word"
lstword.ValueMember = "word"
If myDataTable.Rows.Count > 0 Then
For i As Integer = 0 To myDataTable.Rows.Count - 1
lstword.Items.Add(myDataTable.Rows(i)("word"))
Next
End If
End If
'lstword.Items.Clear()
'lstword.Items.AddRange(word.Where(Function(word) word.ToString().Contains(txtSearch.Text)).ToArray())
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
myReader = Nothing
myCmd = Nothing
myConn.Close()
Call Disconnect()
End With
End Sub
Dim word As List(Of Object)
Private Sub Refreshlist()
lstword.Items.Clear()
lstword.Items.AddRange(word.Where(Function(word) word.ToString().Contains(txtSearch.Text)).ToArray())
End Sub
Private Sub txtSearch_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtSearch.TextChanged
lstword.Items.Clear()
lstword.Items.AddRange(word.Where(Function(word) word.ToString().Contains(txtSearch.Text)).ToArray())
Refreshlist()
'Call List()
'lstword.BeginUpdate()
'Try
' ' keep track of the "non-searched items" '
' Dim word As New List(Of Object)
' lstword.SelectedIndices.Clear()
' If txtSearch.Text.Length > 0 Then
' For index As Integer = 0 To lstword.Items.Count - 1
' Dim item As String = lstword.Items(index).ToString()
' If item.IndexOf(txtSearch.Text, StringComparison.CurrentCultureIgnoreCase) >= 0 Then
' lstword.SelectedIndices.Add(index)
' Else
' ' this item was not searched for; we will remove it '
' word.Add(index)
' End If
' Next
' ' go backwards to avoid problems with indices being shifted '
' For i As Integer = word.Count - 1 To 0 Step -1
' Dim indexToRemove As Integer = word(i)
' lstword.Items.RemoveAt(indexToRemove)
' Next
' End If
'Finally
' lstword.EndUpdate()
'End Try
End Sub
End Class
The first step is to store the items in off-screen memory. For instance:
Dim words As List(Of Object)
Then when you refresh the list box, only populate it with the items from that in-memory list which match the current criteria:
lstword.Items.Clear()
lstword.Items.AddRange(
words.FindAll(
Function(word) Return word.ToString().Contains(txtSearch.Text)
).ToArray()
)
Or, using LINQ:
lstword.Items.Clear()
lstword.Items.AddRange(
words.Where(
Function(word) word.ToString().Contains(txtSearch.Text)
).ToArray()
)
UPDATE
Since you seem to be having trouble getting it working, and it's hard to say what's wrong with your code without actually seeing it, here's a complete working example:
Public Class Form1
Dim words As New List(Of Object)(New String() {"apple", "banana", "berry", "cashew", "lemon", "mango", "peanut"})
Private Sub RefreshList()
lstword.Items.Clear()
lstword.Items.AddRange(
words.Where(
Function(word) word.ToString().Contains(txtSearch.Text)
).ToArray()
)
End Sub
Private Sub txtSearch_TextChanged(sender As Object, e As EventArgs) Handles txtSearch.TextChanged
RefreshList()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
RefreshList()
End Sub
End Class
UPDATE 2
I tried using your code with my recommended suggestions and it worked fine. Here's the code that worked for me. Try it and let me know if it doesn't work for you:
Public Class Glossary
Private Sub Glossary_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
List()
Refreshlist()
End Sub
Private Sub List()
word.AddRange(New String() {"apple", "banana", "berry", "cashew", "lemon", "mango", "peanut"})
End Sub
Private word As New List(Of Object)()
Private Sub Refreshlist()
lstword.Items.Clear()
lstword.Items.AddRange(word.Where(Function(word) word.ToString().Contains(txtSearch.Text)).ToArray())
End Sub
Private Sub txtSearch_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtSearch.TextChanged
Refreshlist()
End Sub
End Class
If that works, then all you need to do is change the List method to load from the database instead of being a hard-coded list.
Get your initial list off the database, pop it in a List class e.g List.
The use your search text to select all the matching items and put them in the list box. Blank search text you just put all of them in.
You might want to have a look at Foreach and List.FindAll as well.

looping through datagridview

Mine is a windows app. containing forms named BOM nd BOMSelected..
There is datagridview in BOM which contains checkbox column.. When the user selects checkbox, the selected rows should be seen in the datagridview of other form, SelectedBom..
I have coded but don't get it working.. Some error..
Can you please help ??
Your Help is greatly appreciated..
Here is what i have done !!
Public Class SelectedBom
Private Sub SelectedBom_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the 'HemDatabase1DataSet4.partno' table. You can move, or remove it, as needed.
'Me.PartnoTableAdapter.Fill(Me.HemDatabase1DataSet4.partno)
Dim count As Integer = 0
For j As Integer = 0 To BOM.dgv1.RowCount - 1
If BOM.dgv1.Rows(j).Cells(0).Value = True Then
Dim ro As New DataGridViewRow
DataGridView2.Rows.Add(ro)
For i As Integer = 0 To BOM.dgv1.ColumnCount - 1
Me.DataGridView2.Rows(count).Cells(i).Value = BOM.dgv1.Rows(j).Cells(i).Value
Next
count += 1
End If
Next
End Sub
End Class
Try,
For Each row As DataGridViewRow In BOM.dgv1.Rows
Dim obj(row.Cells.Count - 1) As Object
For i = 0 To row.Cells.Count - 1
obj(i) = row.Cells(i).Value
Next
Me.DataGridView2.Rows.Add(obj)
Next
EDIT:
Demo:
Add Button1 and DataGridView1 in BOM form
Public Class BOM
Public Class Sample
Public Property Satus As Boolean
Public Property Name As String
Public Property ID As Integer
End Class
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
SelectedBom.Show()
End Sub
Private Sub BOM_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim myList As New List(Of Sample)
myList.Add(New Sample() With {.ID = 1, .Name = "A"})
myList.Add(New Sample() With {.ID = 2, .Name = "B"})
myList.Add(New Sample() With {.ID = 3, .Name = "C"})
DataGridView1.DataSource = myList
End Sub
End Class
Add DataGridView1 in SelectBOM form
Public Class SelectedBom
Private Sub SelectedBom_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim i As Integer = 0
DataGridView1.AutoGenerateColumns = False
DataGridView1.Columns.Add("Name", "Name")
DataGridView1.Columns.Add("No", "No")
For Each row As DataGridViewRow In BOM.DataGridView1.Rows
If DirectCast(row.Cells(0).Value, Boolean) Then
DataGridView1.Rows.Add(row.Cells(1).Value, row.Cells(2).Value)
End If
Next
End Sub
End Class
Maybe instead of using the for each statement, you should instead use:
for istep as integer = 0 to datagridview.rowcount - 2
With the for each syntax, you can't assign a value to the individual row as you walk down the row.