Make ListView FindItemWithText search anywhere in text - vb.net

Dim query as String = "Card" ' Just an example, I would call this with a function
Dim itemF As ListViewItem = ListView1.FindItemWithText(query, False, 0, True)
If (itemF IsNot Nothing) Then
itemF.Selected = True
Else
Alert("Nothing was found")
End If
So I am trying to use this code to add a search functionality. The problem is, this works if I'm trying to go from the beginning to the end. But if I wanted to search Card and find W_Card_Brt_Better, I won't get expected results. Though if I search, W_Card, the item will be selected.

ListView.FindItemWithText always searches using BeginsWith pattern - this is by design, as specified in the docs:
ListView.FindItemWithText Method (String) # MSDN
Finds the first ListViewItem that begins with the specified text value.
If you are trying to do anything more custom than that, I suggest you search against the underlying data source instead. You should generally avoid performing any kind of business logic/validation from the UI control directly.
In the business object, you can do a simple for loop, or build a dictionary, to make it search faster, and encapsulate this search functionality within a method or property, which you will call from the UI layer.

FindItemWithText finds the first ListViewItem ... that begins with the specified text value. If you are looking for Items/SubItems which simply CONTAINS the text you have to iterate and search yourself.
To make it like FindItemWithText, specify a start point and Bool for the SubItem search:
Function FindItemContainingText(lv As ListView, searchSubs As Boolean,
StartIndex As Integer,
searchTarget As String) As ListViewItem
If (StartIndex >= lv.Items.Count) Or (StartIndex < 0) Then
Return Nothing
End If
Dim FindMe As String = searchTarget.ToLowerInvariant
Dim subStopper As Integer = 0
For i As Integer = StartIndex To lv.Items.Count - 1
If searchSubs Then
subStopper = lv.Items(i).SubItems.Count - 1
Else
' Item.Text/Label can be read from SubItem(0)
' just look at it if not searching subI's
subStopper = 0
End If
For n As Integer = 0 To subStopper
If lv.Items(i).SubItems(n).Text.ToLowerInvariant.Contains(FindMe) Then
' return the LVI that contains the text
Return lv.Items(i)
End If
Next n
Next
' note!
Return Nothing
End Function

Related

I keep getting "true" statement for the first match of email and the rest all are "False"

I am currently doing a program that will detect Email in a text file, i am able to bring out the email, but couldnt validate the email. The validation code is something look like this:
Dim truelist As New ArrayList
For i As Integer = 0 To ListBox2.Items.Count - 1
truelist.Add(ListBox2.Items(i))
Next
For Each item In truelist
Dim result As Boolean
Dim rgx As New Regex("^[_a-z0-9-]+(.[_a-z0-9-]+)#[a-z0-9-]+(.[a-z0-9-]+)(.[a-z]{2,4})$")
If rgx.IsMatch(item) Then
MessageBox.Show(item, "true")
result = True
Else
MessageBox.Show(item, "false")
result = False
End If
Next
and my sample input is :
ys_host#hotmail.com
.kjdsd.#hotmail.com
.as.das.#hotmail.com
~!U#)(!#U#hotmail.com
idgohwoijgw12942149!#hotmail.com
ys_host#hotmail.com
Even though my 1st input the same with last input, but i will get false return from the last input
Your regex does not account for any space, and evaluates the characters from the start of the input (^) to the end ($). As such, any leading or trailing spaces in the input strings would fail to be matched.
Trim() is the VB command that removes spaces at the end or beginning of strings. LTrim and RTrim are specific commands that remove spaces at either end. Source: https://msdn.microsoft.com/en-us/library/microsoft.visualbasic.strings.trim(v=vs.110).aspx; while https://learn.microsoft.com/en-us/dotnet/standard/base-types/trimming indicates that String.Trim() works on whitespaces in general.
You could Trim() the inputs to your ListBox and this should solve the problem. The safer and better way in this context is to Trim() the inputs to the Regex - data validation at the most effective time in the code. The sample code (based on your code) would be:
Dim truelist As New ArrayList
For i As Integer = 0 To ListBox2.Items.Count - 1
truelist.Add(Trim(ListBox2.Items(i))) '*** Changed this line, added "Trim"
Next
For Each item In truelist
Dim result As Boolean
Dim rgx As New Regex("^[_a-z0-9-]+(.[_a-z0-9-]+)#[a-z0-9-]+(.[a-z0-9-]+)(.[a-z]{2,4})$")
If rgx.IsMatch(item) Then
MessageBox.Show(item, "true")
result = True
Else
MessageBox.Show(item, "false")
result = False
End If
Next
The alternative syntax is truelist.Add(ListBox2.Items(i).Trim()).
As an aside, this code is ripe for setting up in a function - enabling re-use. Here is an example of the refactoring:
'[Main function elements ... Include Sub/Function header etc.]
Dim truelist As New ArrayList
For i As Integer = 0 To ListBox2.Items.Count - 1
truelist.Add(Trim(ListBox2.Items(i))) '*** Changed this line, added "Trim"
Next
For Each item In truelist
Dim result As Boolean
If IsValidEmailAddress(item) Then
' Do valid stuff
Else
' Do not-valid stuff
End If
Next
'[Main Sub/Function tail, include End Sub/Function as relevant]
Function IsValidEmailAddress(value as String) as Boolean
Dim rgx As New Regex("^[_a-z0-9-]+(.[_a-z0-9-]+)#[a-z0-9-]+(.[a-z0-9-]+)(.[a-z]{2,4})$")
return rgx.IsMatch(item.Trim()) ' Note use of Trim here for encapsulated data validation.
' encapsulated data validation may or may not be what you want.
End Function

Visual Basic Iterative enabling of Textbox's

Si I'm working on an assignment where I have 10 RadioButtons indicating how many contesters I have, and depending on what I pick between 1 to 10, I need that many of my corresponding TextBoxes to be enabled so I could fill it with names!
Is there a way for me to make a For loop between 1 and the number I picked from the RadioButton and say something like
For i = 0 to Size
{
TextBox&i.Enabled = True
}
Since my TextBoxs are called TextBox1 to TextBox10
I know you can add strings together using &, but how can I do that for an object name?
As of right now I literally have the dumbest way of doing it, which is a click event inside each RadioButton that manually enables the correct number of TextBoxes...
Thank you in advance!
You can iterate over all controls like this:
For Each ctr In Me.Controls
Dim indx As String = ctr.Name
If TypeOf (ctr) Is Textbox Then
' Now compare the name with TextBox&i and do smth
End If
Next
It's not possible to just concatenate a string and use it as an object variable reference like that, but you can search the form's controls by their name property (which is a string) and do it that way. Here's an example:
Private Sub EnableTextBoxes(ByVal Size As Integer)
For i As Integer = 1 To Size
Dim matches() As Control = Me.Controls.Find("Textbox" & i.ToString, True)
If matches IsNot Nothing AndAlso matches.Length = 1 Then matches(0).Enabled = True
Next
End Sub

Using the contains() method

Hello I am trying trying to use the contains method to see if a list-block (which exclusively consists of filepaths) 'contains' a certain string. It is supposed to remove the respective entry (in a for loop) if the string is contained in the filepath.
Dim index As Integer = findNumFiles("Archer", 6) '//returns number of files in archer directory
Dim iString As String = "Archer"
For i = 0 To index
If Lookup.Items(index).contains(iString) Then
removeFromList(Lookup, index) '//passes "Lookup" as a ListBlock name and removes the index number
End If
Next
a sample filepath would be
"Z:\Movies and TV\Archer\Season 6\1.mp4"
edit: I forgot to mention that it does not work. I tested the command with a list entry simply named "archer" and if the iString is = to "archer", that list entry is removed. It seems the fact that I'm attempting to use the contains method on a filepath is the problem but I'm not sure.
Thanks for any insight!
Use Instr function to check if the string present or not,
I am not sure about what "Lookup.Items" .
Use 'i'in for loop instead of index
Hope below code will help you
Sub test1()
Dim index As Integer
index = findNumFiles("Archer", 6)
'//returns number of files in archer directory
Dim iString As String
iString = "Archer"
For i = 0 To index
If InStr(1,Lookup.Items(i), iString, 1) > 0 Then
' removeFromList(Lookup, index) '//passes "Lookup" as a ListBlock name and removes the index number
End If
Next
End Sub

Access a form's control by name

not sure whether the title of this post is accurate.
I'm trying to access windows form controls and their properties by "composing" their name within a loop, but I can't seem to find the related documentation. Using VB.net. Basically, say I have the following:
Dim myDt As New DataTable
Dim row As DataRow = myDt.NewRow()
row.Item("col01") = Me.label01.Text
row.Item("col02") = Me.label02.Text
'...
row.Item("colN") = Me.labelN.Text
I'd like to write a for loop instead of N separate instructions.
While it's simple enough to express the left-hand side of the assignments, I'm stumped when it comes to the right-hand side:
For i As Integer = 1 to N
row.Item(String.format("col{0:00}", i)) = ???
' ??? <- write "label" & i (zero-padded, like col) and use that string to access Me's control that has such name
Next
As an extra, I'd like to be able to pass the final ".Text" property as a string as well, for in some cases I need the value of the "Text" property, in other cases the value of the "Value" property; generally speaking, the property I'm interested in might be a function of i.
Cheers.
You could use the ControlsCollection.Find method with the searchAllChildren option set to true
For i As Integer = 1 to N
Dim ctrl = Me.Controls.Find(string.Format("label{0:00}", i), True)
if ctrl IsNot Nothing AndAlso ctrl.Length > 0 Then
row.Item(String.format("col{0:00}", i)) = ctrl(0).Text
End If
Next
An example on how to approach the problem using reflection to set a property that you identify using a string
Dim myLabel As Label = new Label()
Dim prop as PropertyInfo = myLabel.GetType().GetProperty("Text")
prop.SetValue(myLabel, "A Label.Text set with Reflection classes", Nothing)
Dim newText = prop.GetValue(myLabel)
Console.WriteLine(newText)

DataRow.SetColumnError(Int32, String) ignores Int32 value and always uses zero

Okay, this is doing my head in. I'm calling SetColumnError() on a DataRow object that has 20 columns, but no matter which ColumnIndex I use it sets the error text on column 0. The MSDN documentation makes it plainly clear that the error text is supposed to be set on the column that the ColumnIndex provides.
I'm trying to set error text on columns 1 and 2 (I normally use constants when doing this, I have just used the integers in this example code for simplicity). Why does the error text appear on column 0 and what should I be doing to get the text to show on columns 1 and 2? I'm not receiving IndexOutOfRangeException.
Here is the code I'm having trouble with.
Public Sub ValidateRows()
For Each dgvRow As DataGridViewRow In Me.DataGridView1.Rows
If dgvRow.DataBoundItem IsNot Nothing Then
Dim rowView As DataRowView = dgvRow.DataBoundItem
Dim rowData As MyDataSet.DocumentRow = rowView.Row
rowData.ClearErrors()
If rowData.Revision = rowData.Revision_old Then
rowData.SetColumnError(1, "You must change the revision")
End If
If rowData.InternalRevision = rowData.InternalRevision_old Then
rowData.SetColumnError(2, "You must change the internal revision")
End If
End If
Next
End Sub
I'm not sure, but I think the number of the column must be of the type "DataColumn".