How do I fix a "ReadOnly" in vb.net when using arrays - vb.net

It gives me an error message when i try to assign the Words array values from the text file. It tells me that words is"ReadOnly"
This is located in the sub "LoadWords"
Module Module1
Const MaxNoWords As Integer = 10000
Const MaxTries As Integer = 6
Sub Main()
Menu()
End Sub
Sub Menu()
Dim words(MaxNoWords) As String
Dim MenuChoice As Char
Console.WriteLine("Hangman v2")
Do
MenuChoice = GetMenuChoice(MenuChoice)
If MenuChoice = "a" Then
LoadWords(words(MaxNoWords))
ElseIf MenuChoice = "b" Then
PlayHangman()
End If
Loop Until MenuChoice = "x"
Console.ReadLine()
End Sub
Function GetMenuChoice(MenuChoice As Char)
Console.WriteLine(" A - Load Words" & vbNewLine & " B - Play" & vbNewLine & " X - Quit")
MenuChoice = Console.ReadLine
Return MenuChoice
End Function
Sub LoadWords(ByRef words As String)
Dim index As Integer = 0
Console.WriteLine("Loading Words")
FileOpen(1, "U:\A Level Computing\words.txt", OpenMode.Input)
Do
words(index) = LineInput(1)
Loop Until EOF(1)
FileClose(1)
Console.ReadLine()
End Sub
Sub PlayHangman()
Console.WriteLine("Playing Hangman DO NOT Disturb")
Console.ReadLine()
End Sub
End Module

You can change your code like this ...
dim mod_words(MaxNoWords) as string
Sub Main()
Menu()
End Sub
Sub Menu()
' removed line
Dim MenuChoice As Char
and later
Do
mod_words(index) = LineInput(1)
Loop Until EOF(1)
Further I would suggest to use FreeFile (get free file handle) into a var, so you are not fixed to "1".
And somehow I do not understand, why yo try to add only at the last item in your array ...
Could you please explain, what exactly you try to achieve?

It looks like you want to load each word from the file (each on its own line) into the array called words.
If so, simply change:
Sub LoadWords(ByRef words As String)
To:
Sub LoadWords(ByRef words() As String)
Notice the addition of parenthesis, which indicates an Array, not a single string, is being passed.
*Also, ByRef is not necessary here, you could change to ByVal.

Explanations and comments in line.
Imports System.IO
Module Module1
Const MaxTries As Integer = 6
'Moved words to Module level so it can be seen from
'all method in the Module
'Changed from array to List(Of String)
'so we don't have to worry about the size
Private words As New List(Of String)
Public Sub Main()
Menu()
End Sub
Sub Menu()
Console.WriteLine("Hangman v2")
Dim MenuChoice As Char
Do
MenuChoice = GetMenuChoice()
'Also check if list is empty - you don't want to keep loading words
If MenuChoice = "a" AndAlso Not words.Any Then
LoadWords()
'Check if the list has word, can't play without words
ElseIf MenuChoice = "b" AndAlso words.Any Then
PlayHangman()
End If
Loop Until MenuChoice = "x"
End Sub
Function GetMenuChoice() As Char
Console.WriteLine(" A - Load Words" & vbNewLine & " B - Play" & vbNewLine & " X - Quit")
Dim MenuChoice = CChar(Console.ReadLine.ToLower)
Return MenuChoice
End Function
Sub LoadWords()
Console.WriteLine("Loading Words")
Dim path = "U:\A Level Computing\words.txt"
'Use the .net IO methods instead of the old vb methods
words = File.ReadAllLines(path).ToList
End Sub
Sub PlayHangman()
Console.WriteLine("Playing Hangman DO NOT Disturb")
Console.ReadLine()
End Sub
End Module

Related

Color a specific word in every line of text in a RichTextBox

I want to color every same word inside a RichTextBox. I can do it for one line but not on multiple lines.
E.g., Welcome "user" .....
I want the word user to be an exact color in every line it's found.
Here's with what i came up so far:
RichTextBox1.Text = "Welcome "
RichTextBox1.Select(RichTextBox1.TextLength, 0)
RichTextBox1.SelectionColor = My.Settings.color
RichTextBox1.AppendText(My.Settings.username)
RichTextBox1.SelectionColor = Color.Black
RichTextBox1.AppendText(" ........." + vbCrLf)
It's on form.Load; I tried to use the richtextbox.TextChange event, but it just colors the last user word and the others are remain the same.
This is a simple Class that enables multiple Selections and Highlights of text for RichTextBox and TextBox controls.
You can use multiple instances of this Class for different controls.
You can add the Words to Select/HighLight to a List and specify which color to use for selecting and/or highlighting the text.
Dim listOfWords As WordList = New WordList(RichTextBox1)
listOfWords.AddRange({"Word1", "Word2"})
listOfWords.SelectionColor = Color.LightBlue
listOfWords.HighLightColor = Color.Yellow
These are the visual results of the Class actions:
In the example, the List of words is filled using:
Dim patterns As String() = TextBox1.Text.Split()
listOfWords.AddRange(patterns)
In the visual example, the Class is configured this way:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim patterns As String() = TextBox1.Text.Split()
Dim listOfWords As WordList = New WordList(RichTextBox1)
listOfWords.AddRange(patterns)
listOfWords.SelectionColor = Color.LightBlue
listOfWords.HighLightColor = Color.Yellow
If RadioButton1.Checked = True Then
listOfWords.WordsSelect()
ElseIf RadioButton2.Checked Then
listOfWords.WordsHighLight()
Else
listOfWords.DeselectAll()
End If
End Sub
This is the Class used to generate the Selections and HighLights:
Imports System.Drawing.Text
Imports System.Text.RegularExpressions
Public Class WordList
Private TextRendererFlags As TextFormatFlags =
TextFormatFlags.Top Or TextFormatFlags.Left Or TextFormatFlags.NoPadding Or
TextFormatFlags.WordBreak Or TextFormatFlags.TextBoxControl
Private textControl As RichTextBox = Nothing
Private wordsList As List(Of Word)
Public Sub New(rtb As RichTextBox)
textControl = rtb
wordsList = New List(Of Word)
ProtectSelection = False
End Sub
Public Property ProtectSelection As Boolean
Public Property HighLightColor As Color
Public Property SelectionColor As Color
Public Sub Add(word As String)
wordsList.Add(New Word() With {.Word = word, .Indexes = GetWordIndexes(word)})
End Sub
Public Sub AddRange(words As String())
For Each WordItem As String In words
wordsList.Add(New Word() With {.Word = WordItem, .Indexes = GetWordIndexes(WordItem)})
Next
End Sub
Private Function GetWordIndexes(word As String) As List(Of Integer)
Return Regex.Matches(textControl.Text, word).
OfType(Of Match)().
Select(Function(chr) chr.Index).ToList()
End Function
Public Sub DeselectAll()
If textControl IsNot Nothing Then
textControl.SelectAll()
textControl.SelectionBackColor = textControl.BackColor
textControl.Update()
End If
End Sub
Public Sub WordsHighLight()
If wordsList.Count > 0 Then
For Each WordItem As Word In wordsList
For Each Position As Integer In WordItem.Indexes
Dim p As Point = textControl.GetPositionFromCharIndex(Position)
TextRenderer.DrawText(textControl.CreateGraphics(), WordItem.Word,
textControl.Font, p, textControl.ForeColor,
HighLightColor, TextRendererFlags)
Next
Next
End If
End Sub
Public Sub WordsSelect()
DeselectAll()
If wordsList.Count > 0 Then
For Each WordItem As Word In wordsList
For Each Position As Integer In WordItem.Indexes
textControl.Select(Position, WordItem.Word.Length)
textControl.SelectionColor = textControl.ForeColor
textControl.SelectionBackColor = SelectionColor
textControl.SelectionProtected = ProtectSelection
Next
Next
End If
End Sub
Friend Class Word
Property Word As String
Property Indexes As List(Of Integer)
End Class
End Class
With a module,you can do it this way :
Imports System.Runtime.CompilerServices
Module Utility
<Extension()>
Sub HighlightText(ByVal myRtb As RichTextBox, ByVal word As String, ByVal color As Color)
If word = String.Empty Then Return
Dim index As Integer, s_start As Integer = myRtb.SelectionStart, startIndex As Integer = 0
While(__InlineAssignHelper(index, myRtb.Text.IndexOf(word, startIndex))) <> -1
myRtb.[Select](index, word.Length)
myRtb.SelectionColor = color
startIndex = index + word.Length
End While
myRtb.SelectionStart = s_start
myRtb.SelectionLength = 0
myRtb.SelectionColor = Color.Black
End Sub
<Obsolete("Please refactor code that uses this function, it is a simple work-around to simulate inline assignment in VB!")>
Private Shared Function __InlineAssignHelper(Of T)(ByRef target As T, value As T) As T
target = value
Return value
End Function
End Module
Or , you can also go with this one as it will allow you to highlight multiple words at the same time :
Private Sub HighlightWords(ByVal words() As String)
Private Sub HighlightWords(ByVal words() As String)
For Each word As String In words
Dim startIndex As Integer = 0
While (startIndex < rtb1.TextLength)
Dim wordStartIndex As Integer = rtb1.Find(word, startIndex, RichTextBoxFinds.None)
If (wordStartIndex <> -1) Then
rtb1.SelectionStart = wordStartIndex
rtb1.SelectionLength = word.Length
rtb1.SelectionBackColor = System.Drawing.Color.Black
Else
Exit While
End If
startIndex += wordStartIndex + word.Length
End While
Next
End Sub
Source Hope this helps :)
This works
Public Sub HighlightText(ByVal txt As String, ByVal obj As RichTextBox)
obj.SelectionStart = 0
obj.SelectAll()
obj.SelectionBackColor = Color.White
Dim len = txt.Length
Dim pos = obj.Find(txt, 0, RichTextBoxFinds.NoHighlight)
While (pos >= 0)
obj.Select(pos, len)
obj.SelectionBackColor = Color.Yellow
If pos + len >= obj.Text.Length Then
Exit While
End If
pos = obj.Find(txt, pos + len, RichTextBoxFinds.NoHighlight)
End While
End Sub
Public Sub HighlightText(ByVal txt As String, ByVal obj As RichTextBox)
obj.SelectionStart = 0
obj.SelectAll()
obj.SelectionBackColor = Color.White
Dim len = txt.Length
Dim pos = obj.Find(txt, 0, RichTextBoxFinds.NoHighlight)
While (pos >= 0)
obj.Select(pos, len)
obj.SelectionBackColor = Color.Yellow
If pos + len >= obj.Text.Length Then
Exit While
End If
pos = obj.Find(txt, pos + len, RichTextBoxFinds.NoHighlight)
End While
End Sub

VB Read delimited text file array

I have a text file delimited by pipes. I want to read the value at the fifth pipe but I cannot figure out how to do that. All I can do is read each section of the array. Can't find examples on this.
EPD|TR2999-01G|SEMI, TRANS, P-CH, SEL|ACTIVE|PS.COE.6|SCS|SCREENEDCOMPONENTS|EPP|Buy|6.237|916.839|147||181|||CCACOE||PS.777.||150||
EPD|TR2309-01G|SEMI, TRANS, P-CH, SEL|ACTIVE|PS.COE.6|SCS|SCREENED COMPONENTS|EPP|Buy|6.237|193.347|31||181|||777||PS.777.||150||
This example is using a text file with these two lines in it:
Line1: EPD|TR2999-01G|SEMI, TRANS, P-CH, SEL|ACTIVE|PS.COE.6|SCS|SCREENED COMPONENTS|EPP|Buy|6.237|916.839|147||181|||CCACOE||PS.777.|‌​|150||
Line2: EPD|TR2309-01G|SEMI, TRANS, P-CH, SEL|ACTIVE|PS.COE.6|SCS|SCREENED COMPONENTS|EPP|Buy|6.237|193.347|31||181|||777||PS.777.||150‌​||
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim fp as string = "" 'enter the full path to your file here
Dim value as string = GetValueForPart(fp, Me.TextBox1.Text)
MsgBox(value) 'in this example, value is set to "6.237" when textbox input is "TR2999-01G"
End Sub
Private Function GetValueForPart(ByVal filepath As String, ByVal SearchPartNum As String) As String
If Not File.Exists(filepath) Then Return Nothing
If SearchPartNum Is Nothing OrElse SearchPartNum.Trim = "" Then Return Nothing
Dim ret As String = Nothing
Using sr As New StreamReader(filepath)
Do While sr.Peek >= 0
Dim line() As String = sr.ReadLine.Split(CChar("|"))
If line IsNot Nothing AndAlso line.Count >= 5 Then
If line(1).Equals(SearchPartNum) Then
ret = line(9)
Exit Do
End If
End If
Loop
End Using
Return ret
End Function
I just tested this, all you need to do is enter your full filepath on the second line

Single-character strings not writing to file correctly

Having trouble with my code, I resort to writing the contents of my arrays to file, like this:-
Private Sub DumpArray (ByRef array_to_dump(,) As MyEnum, ByVal file_name As String)
Dim sw As StreamWriter = New StreamWriter(file_name)
For i As Integer = array_to_dump.GetLowerBound(0) To array_to_dump.GetUpperBound(0)
For j As Integer = array_to_dump.GetLowerBound(1) To array_to_dump.GetUpperBound(1)
If array_to_dump(i, j) = MyEnum.This Then
sw.Write("Fred")
Else
sw.Write("Bert")
End If
sw.Write(vbTab)
Next j
sw.WriteLine
Next i
sw.Flush
sw.Close
End Sub
This produces the output I expect; in the current case a file filled with "Fred", separated by tabs. However, if I change the business end of the code to this:-
If array_to_dump(i, j) = MyEnum.This Then
sw.Write("1")
Else
sw.Write("0")
End If
the file is filled with non-printing characters, that come out as little boxes in Notepad, rather than the rows of "0" separated by tabs that I was expecting. Any other pairs of single-character strings do the same.
While not a matter of pressing importance, I am idly curious as to why this should be. Does anyone know?
You will have to set the encoding to something else when you declare your SteamWriter like #DavidSdot said. Like so:
Dim writer As StreamWriter = New StreamWriter(file_name, Encoding.Default)
Mess around with that property and you will probably find a good value. I'm no expert in encoding but this should be the culprit of your problem.
I tried to repro the problem but the code below works correctly. Compare it to your real code... maybe there is a typo or something in the original?
Option Strict On
Imports System.IO
Public Class Form1
Private Enum MyEnum As Integer
This
That
End Enum
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim myArray(1, 1) As MyEnum
myArray(0, 0) = MyEnum.This
myArray(0, 1) = MyEnum.This
myArray(1, 0) = MyEnum.This
myArray(1, 1) = MyEnum.That
Call DumpArray(myArray, "C:\Junk\Junk.txt")
End Sub
Private Sub DumpArray(ByRef array_to_dump(,) As MyEnum, ByVal file_name As String)
Dim sw As StreamWriter = New StreamWriter(file_name)
For i As Integer = array_to_dump.GetLowerBound(0) To array_to_dump.GetUpperBound(0)
For j As Integer = array_to_dump.GetLowerBound(1) To array_to_dump.GetUpperBound(1)
If array_to_dump(i, j) = MyEnum.This Then
sw.Write("0")
Else
sw.Write("1")
End If
sw.Write(vbTab)
Next j
sw.WriteLine()
Next i
sw.Flush()
sw.Close()
End Sub
End Class

Multiple Search Criteria (VB.NET)

So my problem is:
I have a List of a custom Type {Id as Integer, Tag() as String},
and i want to perform a multiple-criteria search on it; eg:
SearchTags={"Document","HelloWorld"}
Results of the Search will be placed a ListBox (ListBox1) in this format:
resultItem.id & " - " & resultItem.tags
I already tried everything i could find on forums, but it didn't work for me (It was for db's or for string datatypes)
Now, i really need your help. Thanks in advance.
For Each MEntry As EntryType In MainList
For Each Entry In MEntry.getTags
For Each item As String In Split(TextBox1.Text, " ")
If Entry.Contains(item) Then
If TestIfItemExistsInListBox2(item) = False Then
ListBox1.Items.Add(item & " - " & Entry.getId)
End If
End If
Next
Next
Next
Example Custom Array:
(24,{"snippet","vb"})
(32,{"console","cpp","helloworld"})
and so on...
I searched for ("Snippet vb test"):
snippet vb helloWorld - 2
snippet vb tcpchatEx - 16
cs something
test
So, i'll get everything that contains one of my search phrases.
I expected following:
snippet vb tcp test
snippet vb dll test
snippet vb test metroui
So, i want to get everything that contains all my search phrases.
My entire, code-likely class
Imports Newtonsoft.Json
Public Class Form2
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Dim MainList As New List(Of EntryType)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
MainList.Clear()
Dim thr As New Threading.Thread(AddressOf thr1)
thr.SetApartmentState(Threading.ApartmentState.MTA)
thr.Start()
End Sub
Delegate Sub SetTextCallback([text] As String)
Private Sub SetTitle(ByVal [text] As String) ' source <> mine
If Me.TextBox1.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetTitle)
Me.Invoke(d, New Object() {[text]})
Else
Me.Text = [text]
End If
End Sub
Sub thr1()
Dim linez As Integer = 1
Dim linex As Integer = 1
For Each line As String In System.IO.File.ReadAllLines("index.db")
linez += 1
Next
For Each line As String In System.IO.File.ReadAllLines("index.db")
Try
Application.DoEvents()
Dim a As saLoginResponse = JsonConvert.DeserializeObject(Of saLoginResponse)(line) ' source <> mine
Application.DoEvents()
MainList.Add(New EntryType(a.id, Split(a.tags, " ")))
linex += 1
SetTitle("Search (loading, " & linex & " of " & linez & ")")
Catch ex As Exception
End Try
Next
SetTitle("Search")
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim searchTags() As String = TextBox1.Text.Split(" ")
Dim query = MainList.Where(Function(et) et.Tags.Any(Function(tag) searchTags.Contains(tag))).ToList
For Each et In query
ListBox1.Items.Add(et.Id)
Next
End Sub
Private Sub Button4_Click(sender As Object, e As EventArgs) ' test
MsgBox(Mid(ListBox1.SelectedItem.ToString, 1, 6)) ' test
End Sub 'test, removeonrelease
End Class
Public Class EntryType
Public Property Id As Integer
Public Property Tags() As String
Public Sub New(ByVal _id As Integer, ByVal _tags() As String)
Me.Id = Id
Me.Tags = Tags
End Sub
Public Function GetTags() As String
'to tell the Listbox what to display
Return Tags
End Function
Public Function GetId() As Integer
'to tell the Listbox what to display
Return Id
End Function
End Class
I also edited your EntryType class; I added a constructor, removed toString and added GetTags and GetID.
Example "DB" im working with ("db" as "index.db" in exec dir):
{"tags":"vb.net lol test qwikscopeZ","id":123456}
{"tags":"vb.net lol test","id":12345}
{"tags":"vb.net lol","id":1234}
{"tags":"vb.net","id":123}
{"tags":"cpp","id":1}
{"tags":"cpp graphical","id":2}
{"tags":"cpp graphical fractals","id":3}
{"tags":"cpp graphical fractals m4th","id":500123}
Error:
Debugger:Exception Intercepted: _Lambda$__1, Form2.vb line 44
An exception was intercepted and the call stack unwound to the point before the call from user code where the exception occurred. "Unwind the call stack on unhandled exceptions" is selected in the debugger options.
Time: 13.11.2014 03:46:10
Thread:<No Name>[5856]
Here is a Lambda query. The Where filters on a predicate, since Tags is an Array you can use the Any function to perform a search based on another Array-SearchTags. You can store each class object in the Listbox since it stores Objects, you just need to tell it what to display(see below).
Public Class EntryType
Public Property Id As Integer
Public Property Tags() As As String
Public Overrides Function ToString() As String
'to tell the Listbox what to display
Return String.Format("{0} - {1}", Me.Id, String.Join(Me.Tags, " "))
End Function
End Class
Dim searchTags = textbox1.Text.Split(" "c)
Dim query = mainlist.Where(Function(et) et.Tags.Any(Function(tag) searchTags.Contains(tag))).ToList
For Each et In query
Listbox1.Items.Add(et)
Next

The best way to extract data from a CSV file into a searchable datastructure?

I have a csv file with 48 columns of data.
I need to open this file, place it into a data structure and then search that data and present it in a DataRepeater.
So far I have successfully used CSVReader to extract the data and bind it to myDataRepeater. However I am now struggling to place the data in a table so that I can filter the results. I do not want to use SQL or any other database.
Does anyone have a suggestion on the best way to do this?
So far, this is working in returning all records:
Private Sub BindCsv()
' open the file "data.csv" which is a CSV file with headers"
Dim dirInfo As New DirectoryInfo(Server.MapPath("~/ftp/"))
Dim fileLocation As String = dirInfo.ToString & "data.txt"
Using csv As New CsvReader(New StreamReader(fileLocation), True)
myDataRepeater.DataSource = csv
myDataRepeater.DataBind()
End Using
End Sub
Protected Sub myDataRepeater_ItemDataBound(ByVal sender As Object, ByVal e As RepeaterItemEventArgs) Handles myDataRepeater.ItemDataBound
Dim dataItem As String() = DirectCast(e.Item.DataItem, String())
DirectCast(e.Item.FindControl("lblPropertyName"), ITextControl).Text = dataItem(2).ToString
DirectCast(e.Item.FindControl("lblPrice"), ITextControl).Text = dataItem(7).ToString
DirectCast(e.Item.FindControl("lblPricePrefix"), ITextControl).Text = dataItem(6)
DirectCast(e.Item.FindControl("lblPropertyID"), ITextControl).Text = dataItem(1)
DirectCast(e.Item.FindControl("lblTYPE"), ITextControl).Text = dataItem(18)
DirectCast(e.Item.FindControl("lblBedrooms"), ITextControl).Text = dataItem(8)
DirectCast(e.Item.FindControl("lblShortDescription"), ITextControl).Text = dataItem(37)
Dim dirInfo As New DirectoryInfo(Server.MapPath("~/ftp/images/"))
DirectCast(e.Item.FindControl("imgMain"), Image).ImageUrl = dirInfo.ToString & "pBRANCH_" & dataItem(1) & ".jpg"
DirectCast(e.Item.FindControl("linkMap"), HyperLink).NavigateUrl = "http://www.multimap.com/map/browse.cgi?client=public&db=pc&cidr_client=none&lang=&pc=" & dataItem(5) & "&advanced=&client=public&addr2=&quicksearch=" & dataItem(5) & "&addr3=&addr1="
End Sub
Code add to filter results:
Try
Dim csv As New CSVFile(fileLocation)
Dim ds As DataSet = csv.ToDataSet("MyTable")
If Not ds Is Nothing Then
Dim strExpr As String = "Bedrooms >= '3'"
Dim strSort As String = "PropertyID ASC"
'Use the Select method to find all rows matching the filter.
Dim myRows() As DataRow
'myRows = Dt.Select(strExpr, strSort)
myRows = csv.ToDataSet("MyTable").Tables("MyTable").Select(strExpr, strSort)
myDataRepeater.DataSource = myRows
myDataRepeater.DataBind()
End If
Catch ex As Exception
End Try
Which does return the two rows I am expecting but then when it binds to the datarepeater I get the following error:
DataBinding: 'System.Data.DataRow' does not contain a property with the name 'PropertyName'.
Corrected code, filter not being applied:
Public Sub PageLoad(ByVal Sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
If Not Page.IsPostBack Then
ReadCsv()
lblSearch.Text = "Lettings Search"
End If
End Sub
Private Sub ReadCsv()
Dim dirInfo As New DirectoryInfo(Server.MapPath("~/ftp/"))
Dim fileLocation As String = dirInfo.ToString & "data.txt"
Try
Dim csv As New CSVFile(fileLocation)
Dim ds As DataSet = csv.ToDataSet("MyTable")
If Not ds Is Nothing Then
myDataRepeater.DataSource = ds
myDataRepeater.DataMember = ds.Tables.Item(0).TableName
myDataRepeater.DataBind()
End If
ds = Nothing
csv = Nothing
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles btnSubmit.Click
Dim rowCount As Integer
rowCount = QueryCsv()
pnlSearch.Visible = False
lblResults.Visible = True
lblSearch.Text = "Search Results"
lblResults.Text = "Your search returned " & rowCount.ToString & " results"
If rowCount > 0 Then
myDataRepeater.Visible = True
pnlResults.Visible = True
btnBack.Visible = True
End If
End Sub
Protected Function QueryCsv() As Integer
Dim dirInfo As New DirectoryInfo(Server.MapPath("~/ftp/"))
Dim fileLocation As String = dirInfo.ToString & "data.txt"
Dim numberofRows As Integer
Try
Dim csv As New CSVFile(fileLocation)
Dim ds As DataSet = csv.ToDataSet("MyTable")
If Not ds Is Nothing Then
Dim strExpr As String = "PropertyID = 'P1005'"
Dim strSort As String = "PropertyID DESC"
Try
ds.Tables.Item(0).DefaultView.RowFilter = strExpr
ds.Tables.Item(0).DefaultView.Sort = strSort
myDataRepeater.DataSource = ds.Tables.Item(0).DefaultView
Catch ex As Exception
End Try
End If
numberofRows = ds.Tables("MyTable").Rows.Count
Catch ex As Exception
End Try
Return numberofRows
End Function
Why not use the built-in TextFileParser to get the data into a DataTable? Something like Paul Clement's answer in this thread
One of the ways I've done this is by using a structure array and reflection.
First, set up your structure in a module: CSVFileFields.vb
Imports System.Reflection
Public Module CSVFileFields
#Region " CSV Fields "
Public Structure CSVFileItem
Dim NAME As String
Dim ADDR1 As String
Dim ADDR2 As String
Dim CITY As String
Dim ST As String
Dim ZIP As String
Dim PHONE As String
Public Function FieldNames() As String()
Dim rtn() As String = Nothing
Dim flds() As FieldInfo = Me.GetType.GetFields(BindingFlags.Instance Or BindingFlags.Public)
If Not flds Is Nothing Then
ReDim rtn(flds.Length - 1)
Dim idx As Integer = -1
For Each fld As FieldInfo In flds
idx += 1
rtn(idx) = fld.Name
Next
End If
Return rtn
End Function
Public Function ToStringArray() As String()
Dim rtn() As String = Nothing
Dim flds() As FieldInfo = Me.GetType.GetFields(BindingFlags.Instance Or BindingFlags.Public)
If Not flds Is Nothing Then
ReDim rtn(flds.Length - 1)
Dim idx As Integer = -1
For Each fld As FieldInfo In flds
idx += 1
rtn(idx) = fld.GetValue(Me)
Next
End If
Return rtn
End Function
Public Shadows Function ToString(ByVal Delimiter As String) As String
Dim rtn As String = ""
Dim flds() As FieldInfo = Me.GetType.GetFields(BindingFlags.Instance Or BindingFlags.Public)
If Not flds Is Nothing Then
For Each fld As FieldInfo In flds
rtn &= fld.GetValue(Me) & Delimiter
Next
rtn = rtn.Substring(0, rtn.Length - 1)
End If
Return rtn
End Function
End Structure
#End Region
End Module
Next we will make our own collection out of the structure we just made. This will make it easy to use .Add() .Remove() etc for our structure. We can also remove individual items with .RemoveAt(Index). File: CSVFileItemCollection.vb
#Region " CSVFileItem Collection "
Public Class CSVFileItemCollection
Inherits System.Collections.CollectionBase
Public Sub Add(ByVal NewCSVFileItem As CSVFileItem)
Me.List.Add(NewCSVFileItem)
End Sub
Public Sub Remove(ByVal RemoveCSVFileItem As CSVFileItem)
Me.List.Remove(RemoveCSVFileItem)
End Sub
Default Public Property Item(ByVal index As Integer) As CSVFileItem
Get
Return Me.List.Item(index)
End Get
Set(ByVal value As CSVFileItem)
Me.List.Item(index) = value
End Set
End Property
Public Shadows Sub Clear()
MyBase.Clear()
End Sub
Public Shadows Sub RemoveAt(ByVal index As Integer)
Remove(Item(index))
End Sub
End Class
#End Region
Next you need your class to handle the reflection import: CSVFile.vb
Imports System.Reflection
Imports System.IO
Imports Microsoft.VisualBasic.PowerPacks
Public Class CSVFile
#Region " Private Variables "
Private _CSVFile As CSVFileItem, _Delimiter As String, _Items As New CSVFileItemCollection
#End Region
#Region " Private Methods "
Private Sub FromString(ByVal Line As String, ByVal Delimiter As String)
Dim CSVFileElements() As String = Line.Split(Delimiter)
If Not CSVFileElements Is Nothing Then
Dim fldInfo() As FieldInfo = _CSVFile.GetType.GetFields(BindingFlags.Instance Or BindingFlags.Public)
If Not fldInfo Is Nothing Then
Dim itm As System.ValueType = CType(_CSVFile, System.ValueType)
For fldIdx As Integer = 0 To CSVFileElements.Length - 1
fldInfo(fldIdx).SetValue(itm, CSVFileElements(fldIdx).Replace(Chr(34), ""))
Next
_CSVFile = itm
Else
Dim itms As Integer = 0
If Not fldInfo Is Nothing Then
itms = fldInfo.Length
End If
Throw New Exception("Invalid line definition.")
End If
Else
Dim itms As Integer = 0
If Not CSVFileElements Is Nothing Then
itms = CSVFileElements.Length
End If
Throw New Exception("Invalid line definition.")
End If
End Sub
#End Region
#Region " Public Methods "
Public Sub New()
_CSVFile = New CSVFileItem
End Sub
Public Sub New(ByVal Line As String, ByVal Delimiter As String)
_CSVFile = New CSVFileItem
_Delimiter = Delimiter
FromString(Line, Delimiter)
End Sub
Public Sub New(ByVal Filename As String)
LoadFile(Filename)
End Sub
Public Sub LoadFile(ByVal Filename As String)
Dim inFile As StreamReader = File.OpenText(Filename)
Do While inFile.Peek > 0
FromString(inFile.ReadLine, ",")
_Items.Add(_CSVFile)
_CSVFile = Nothing
Loop
inFile.Close()
End Sub
#End Region
#Region " Public Functions "
Public Function ToDataSet(ByVal TableName As String) As DataSet
Dim dsCSV As DataSet = Nothing
If Not _Items Is Nothing AndAlso _Items.Count > 0 Then
Dim flds() As FieldInfo = _Items.Item(0).GetType.GetFields(BindingFlags.Instance Or BindingFlags.Public)
If Not flds Is Nothing Then
dsCSV = New DataSet
dsCSV.Tables.Add(TableName)
For Each fld As FieldInfo In flds
'Add Column Names
With dsCSV.Tables.Item(TableName)
.Columns.Add(fld.Name, fld.FieldType)
End With
Next
'Populate Table with Data
For Each itm As CSVFileItem In _Items
dsCSV.Tables.Item(TableName).Rows.Add(itm.ToStringArray)
Next
End If
End If
Return dsCSV
End Function
#End Region
#Region " Public Properties "
Public ReadOnly Property Item() As CSVFileItem
Get
Return _CSVFile
End Get
End Property
Public ReadOnly Property Items() As CSVFileItemCollection
Get
Return _Items
End Get
End Property
#End Region
End Class
Okay a little explanation. What this class is doing is first getting the line of delimited (",") text and splitting it into a string array. Then it iterates through every field you have in your structure CSVFileItem and based on the index populates that structure variable. It doesn't matter how many items you have. You could have 1 or 1,000 so long as the order in which your structure is declared is the same as the contents you are loading. For example, your input CSV should match CSVFileItem as "Name,Address1,Address2,City,State,Zip,Phone". That is done with this loop here from the above code:
Dim fldInfo() As FieldInfo = _CSVFile.GetType.GetFields(BindingFlags.Instance Or BindingFlags.Public)
If Not fldInfo Is Nothing Then
Dim itm As System.ValueType = CType(_CSVFile, System.ValueType)
For fldIdx As Integer = 0 To CSVFileElements.Length - 1
fldInfo(fldIdx).SetValue(itm, CSVFileElements(fldIdx).Replace(Chr(34), ""))
Next
_CSVFile = itm
Else
Dim itms As Integer = 0
If Not fldInfo Is Nothing Then
itms = fldInfo.Length
End If
Throw New Exception("Invalid line definition.")
End If
To make things easy, instead of having to load the file from our main class, we can simply pass it the file path and this class will do all of the work and return a collection of our structure. I know this seems like a lot of setup, but it's worth it and you can come back and change your original structure to anything and the rest of the code will still work flawlessly!
Now to get everything going. Now you get to see how easy this is to implement with only a few lines of code. File: frmMain.vb
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Try
Dim csv As New CSVFile("C:\myfile.csv")
Dim ds As DataSet = csv.ToDataSet("MyTable")
If Not ds Is Nothing Then
'Add Labels
Dim lblSize As New Size(60, 22), lblY As Integer = 10, lblX As Integer = 10, lblSpacing As Integer = 10
For Each fldName As String In csv.Items.Item(0).FieldNames
Dim lbl As New Label
lbl.AutoSize = True
lbl.Size = lblSize
lbl.Location = New Point(lblX, lblY)
lbl.Name = "lbl" & fldName
lblX += lbl.Width + lblSpacing
lbl.DataBindings.Add(New Binding("Text", ds.Tables.Item(0), fldName, True))
drMain.ItemTemplate.Controls.Add(lbl)
Next
drMain.DataSource = ds
drMain.DataMember = ds.Tables.Item(0).TableName
End If
ds = Nothing
csv = Nothing
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
End Class
This really makes for some dynamic programming. You can wrap these in generic classes and call the functions for any structure. You then have some reusable code that will make your programs efficient and cut down on programming time!
Edit:
Added the ability to dump structure collection to a dataset and then dynamically fill a datarepeater.
Hope this helps. (I know this was a lot of information and seems like a lot of work, but I guarantee you that once you get this in place, it will really cut down on future projects coding time!)