Formating In Visual Basic - vb.net

lstPrint.Items.Add(String.Format("{0,-20} {1,5}", "Denomination", "Count"))
For x As Integer = 0 To 6
lstPrint.Items.Add(String.Format("{0,-20} {1,13:S}", ouputArray(x), "Count"))
Next
For the sake of making things easier, Dim outputArray As String() = {"1$", "2$", "5$", "10$", "20$", "50$", "100$"}, and I swapped the second array and made it just say Count.
typically I would simply use Convert.ToChar(Keys.Tab) to make all my columns line up, but I'm trying to get better with string formatting. How would I go about compensating for the difference in characters?

You need to use a fixed-width font if you expect that sort of formatting to produce aligned text. Spaces are much narrower than other characters in variable-width fonts.
Otherwise, how about using a control that actually has columns instead of a ListBox, e.g. ListView or DataGridView? Using the best tool for the job is always a good idea.

I set the font for the list box to a fixed width font. Next I got the length of the longest string in the array. I used .PadLeft to make all the strings the same length.
Private Sub OPCode()
ListBox1.Font = New Font("Consolas", 12)
Dim outputArray As String() = {"1$", "2$", "5$", "10$", "20$", "50$", "100$"}
Dim longest As Integer = outputArray.OrderByDescending(Function(s) s.Length).FirstOrDefault().Length
For Each s In outputArray
ListBox1.Items.Add(s.PadLeft(longest) & " Count")
Next
End Sub
The result:

Related

My text file lines sometimes contain the same string in vb.net

I have a text file which contains the same string of characters in different lines. I read the file using this code:
Dim readTexte() As String = File.ReadAllLines(OuvrirFichier, Encoding.UTF8)
Dim t As String
For Each t In readTexte
If t.Contains(TreeView1.SelectedNode.Text) Then
TextBox2.Text = Trim(t.Substring(0, 18))
TextBox1.Text = Trim(t.Substring(18, 90))
TextBox4.Text = Trim(t.Substring(107, 120))
End If
Next
However, I have a problem because when cutting and reading these strings. The code is not able to choose the right line to match the TreeView node, because it identifies several equally perfect strings.
For example, the first line contains 3 substrings (fixed width fields):
Saint, Augustine, and Doctor of the Church.
The second line contains three sub-strings as well, slightly different:
Saint, Monica, and mother of Saint Augustine
When I want to read, my code gives me two Augustines, and mixes Augustine and Monique! The TextBox2 contains the string contained in the treeview.
How can I fix this?
The treeview is created as simply as possible, thus:
Dim readText () As String = File.ReadAllLines (OpenFile,
Encoding.UTF8)
Dim s As String
For Each s In readText
TextBox2.Text = Trim (s.Substring (0, 18))
TextBox1.Text = Trim (s.Substring (18, 90))
TextBox4.Text = Trim (s.Substring (107, 120))
Dim node As TreeNode = Me.TreeView1.Nodes (0)
TreeView1.Nodes (0) .Nodes.Add (New TreeNode (TextBox1.Text))
Next
We need to know more about how the TreeView is built before we can answer this. It's possible there is simply not enough data associated with the TreeView right now, and the solution will be in a completely different area of the code.
However, I can provide some notes. First, reading a file is one of the slowest things possible to do in a computer. We already see this is small enough to fit in memory; if it's also reasonably stable (doesn't change often), you can save significant work by loading to the array once when the program starts.
Next, I wouldn't keep just a simple array of strings. Instead, I'd parse the data into separate fields right at load. A Tuple, Class, or even string array can all work.
Finally, this code will continue looping even after if finds a match. I'd have a way to stop once we find what we're looking for.
Put it all together like this:
'Create a set of Tuples. Could also use a class here.
Dim readTexte() As IEnumerable(Of (String, String, String)) =
File.ReadLines(OuvrirFichier, Encoding.UTF8).
Select(Function(line) (Trim(line.SubString(0, 18)), Trim(line.SubString(18,90)), Trim(line.SubString(107,120))) )
'Search the collection for the first match
Dim result = readTexte.First(Function(record) TreeView1.SelectedNode.Text.Equals(record.Item1))
TextBox2.Text = result.Item1
TextBox1.Text = result.Item2
TextBox4.Text = result.Item3
Again, this doesn't solve your matching problem, because the question doesn't contain the information we need to help do that. Please edit the question to include more details on how the TreeView is created.

Streamwriter: write two listboxes on the same row

I am trying to write, in order to export on txt file, information in two listbox with the same number of rows. I have to export them with the following format: Listbox1, Listbox2. In order to do this, I've tried to use the following code:
Using writer = New StreamWriter(SaveFileDialog1.FileName)
For Each o As Object In Form3.ListBox1.Items And Form3.ListBox2.Items
writer.WriteLine(o)
Next
End Using
I'm receiving the following error:
BC30452 Operator 'And' is not defined for types 'ListBox.ObjectCollection' and 'ListBox.ObjectCollection'.
I've also tried to perform three For Each loops, the first for the LB1, the second for the commas and the third for LB2, but I'm having it exported with content on single lines. How could I solve this?
If you use Enumerable.Zip, as suggested in another answer, then you can make the code more succinct by doing away with the explicit loop:
File.WriteAllLines(SaveFileDialog1.FileName,
Form3.ListBox1.
Items.
Cast(Of Object).
Zip(Form3.ListBox2.
Items.
Cast(Of Object),
Function(x1, x2) $"{x1}, {x2}"))
If you didn't use Zip then you can use a loop this way:
Dim items1 = Form3.ListBox1.Items
Dim items2 = Form3.ListBox2.Items
Using writer = New StreamWriter(SaveFileDialog1.FileName)
For i = 0 To Math.Min(items1.Count, items2.Count)
writer.WriteLine($"{items1(i)}, {items2(i)}")
Next
End Using
The Math.Min part is just in case there are different numbers of items in each ListBox. If you know there aren't then you can do away with that and just use one Count. If there might be different counts but you want to output all items then the code would become slightly more complex to handle that.
As the error message says, the syntax you attempted is simply not valid. There's no feature in VB.NET that does that sort of thing.
However, the .NET Framework API does provide a means for something similar, which would probably work in your case. See Enumerable.Zip(). You can use it like this:
Using writer = New StreamWriter(SaveFileDialog1.FileName)
For Each o As String In Form3.ListBox1.Items.Cast(Of Object).Zip(Form3.ListBox2.Items.Cast(Of Object), Function(x1, x2) x1 & ", " & x2)
writer.WriteLine(o)
Next
End Using
Since you said that both list boxes have the same number of items we can use the number of items in the first listbox less one (indexes start at zero) in a For loop.
I used a StringBuilder so the code does not have to throw away and create a new string on each iteration.
I used an interpolated string indicate by the $ preceding the string. This means I can insert variables in braces, right along with literals.
Call .ToString on the StringBuilder to write to the text file.
Private Sub SaveListBoxes()
Dim sb As New StringBuilder
For i = 0 To ListBox1.Items.Count - 1
sb.AppendLine($"{ListBox1.Items(i)}, {ListBox2.Items(i)}")
Next
File.WriteAllText("C:\Users\xxx\Desktop\ListBoxText.txt", sb.ToString)
End Sub

How to insert text into a string at a point of a specific character(s)

I have searched the whole internet, and the only things I can find is IndexOf. The issue with that is I need a way to put the insert at a specific one of those chars.
I am currently using this
RichTextBox1.Text = RichTextBox1.Text.Insert(RichTextBox1.Text.Substring(0,RichTextBox1.Text.Split("^")(CurrentSlide).Length), "^")
Which of course is completely incorrect after thinking about it, because the length of the index is not the real length of the text to the index.
I think you have the right idea. It seems like you have a "^" delimited string, and you want to insert an empty at some point. Since a collection is easier to work with than a string, your Split is a good start.
Dim parts = RichTextBox1.Text.Split("^")
Let's make that a list, so we can insert into it:
Dim parts = RichTextBox1.Text.Split("^").ToList()
Now, just insert an empty string where you want the new element:
Dim parts = RichTextBox1.Text.Split("^").ToList()
parts.Insert(CurrentSlide, "")
And combine them back for editing in your textbox:
Dim parts = RichTextBox1.Text.Split("^").ToList()
parts.Insert(CurrentSlide, "")
RichTextBox1.Text = String.Join("^", parts)

How should I handle filling multiple textboxes when I don't know how many of them will have data?

I'm writing an application in VB in which I need to show the user some information which will be copy and pasted into another application however limitations of the other application mean that the string needs to be split into chunks no larger than 55 characters (it's just written notes). I thought the neatest way to do this was to have several textboxes each with a 'copy to clipboard' button to make it convenient for the user.
The code I have is:
Dim invdesc As List(Of String) = Split(splitstring, 55)
txtinvDesc1.Text = invdesc(0)
txtinvDesc2.Text = invdesc(1)
txtinvDesc3.Text = invdesc(2)
...
Split uses a regular expression to return a list of several lines without breaking up words and most of the time this will return a maximum of seven results but occasionally six (my original string max length is 330) and often fewer so my original idea to fill out any strings shorter than 330 with trailing spaces won't work as it's still possible I will either miss text or call a result that isn't there.
Ideally I would just do some kind of loop that only inputs to txtinvDesc(x) while there is data available and ignores the rest (or hides them) but I don't know any way to refer to a textbox other than explicitly or how to put them in any kind of list/array.
So it's a bit of an open question in "how best can I handle this requirement?"
You can create a collection (e.g., Array or List) of TextBox like with any other type/class (as you are doing with String in your code). Sample:
Dim allTextBoxes As New List(Of TextBox)
allTextBoxes.Add(txtinvDesc1)
allTextBoxes.Add(txtinvDesc2)
allTextBoxes.Add(txtinvDesc3)
Alternatively, you might iterate through all the controls in the main form by checking its type (a textbox or not). In that case you would have to set a relationship between the given name of the textbox and the data list index, via other collection for example:
Dim mappingList As New List(Of String)
mappingList.Add("txtinvDesc1")
mappingList.Add("txtinvDesc2")
mappingList.Add("txtinvDesc3")
For Each ctr As Control In Me.Controls
If (TypeOf ctr Is TextBox AndAlso mappingList.Contains(ctr.Name)) Then
ctr.Text = invdesc(mappingList.IndexOf(ctr.Name))
End If
Next
--- CLARIFICATION (not as evident as I thought)
The proposed for each loop relies on a mapping approach, that is, it relates each element in invdesc with the corresponding TextBox name. By definition, both arrays HAVE TO have the same number of elements (otherwise the mapping system wouldn't have made any sense). This is the most efficient and overall-applicable alternative; if the names of the textboxes and invdesc have elements in common (e.g., the numbers), you might just compare the names. BUT WHEN MAPPING YOU HAVE TO ACCOUNT FOR ALL THE ELEMENTS (if there is no associated TextBox to a given item, let the value blank; but all the items have to be accounted).
If you want to index the tbs:
Private TBs as New List (of TextBox)
Early on (after FormLoad) maybe in a FormSetup:
TBs.Add(txtinvDesc1)
TBs.Add(txtinvDesc2)
TBs.Add(txtinvDesc3)
...
Then:
Dim invdesc As List(Of String) = Split(splitstring, 55)
For n As Integer = 0 To invdesc.Count-1
TBs(n).Text = invdesc(n)
Next
' handle the varying 7th TB:
For n As Integer = invdesc.Count-1 To TBs.Count - 1
TBs(n).Enabled = False
TBs(n).Text =""
Next
Or a For/Each:
Dim ndx As Integer = 0
For Each tb As TextBox In TBs
tb.Text = invdesc(ndx)
ndx += 1 ' thanks varo!
Next
Then hide/disable or at least clear the text from any empty ones.
If it turns out there are always 6 you really only need an if statement:
txtinvDesc1.Text = invdesc(0)
txtinvDesc2.Text = invdesc(1)
txtinvDesc3.Text = invdesc(2)
...
If incDesc.Count-1 = 6 Then
txtinvDesc7.Text = invdesc(6)
Else
txtinvDesc7.Enabled= False
txtinvDesc7.Text = ""
End If
I would change the TB names to start at txtinvDesc0.Text to avoid getting confused (as I may have)
Use multiline textbox and in OnKeyPress event force 55 chars per line. You can find subclassed TextBox with that feature in this SO answer :
https://stackoverflow.com/a/17082189/351383

How can I read individual lines of a CSV file into a string array, to then be selectively displayed via combobox input?

I need your help, guys! :|
I've got myself a CSV file with the following contents:
1,The Compact,1.8GHz,1024MB,160GB,440
2,The Medium,2.4GHz,1024MB,180GB,500
3,The Workhorse,2.4GHz,2048MB,220GB,650
It's a list of computer systems, basically, that the user can purchase.
I need to read this file, line-by-line, into an array. Let's call this array csvline().
The first line of the text file would stored in csvline(0). Line two would be stored in csvline(1). And so on. (I've started with zero because that's where VB starts its arrays). A drop-down list would then enable the user to select 1, 2 or 3 (or however many lines/systems are stored in the file). Upon selecting a number - say, 1 - csvline(0) would be displayed inside a textbox (textbox1, let's say). If 2 was selected, csvline(1) would be displayed, and so on.
It's not the formatting I need help with, though; that's the easy part. I just need someone to help teach me how to read a CSV file line-by-line, putting each line into a string array - csvlines(count) - then increment count by one so that the next line is read into another slot.
So far, I've been able to paste the numbers of each system into an combobox:
Using csvfileparser As New Microsoft.VisualBasic.FileIO.TextFieldParser _
("F:\folder\programname\programname\bin\Debug\systems.csv")
Dim csvalue As String()
csvfileparser.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
csvfileparser.Delimiters = New String() {","}
While Not csvfileparser.EndOfData
csvalue = csvfileparser.ReadFields()
combobox1.Items.Add(String.Format("{1}{0}", _
Environment.NewLine, _
csvalue(0)))
End While
End Using
But this only selects individual values. I need to figure out how selecting one of these numbers in the combobox can trigger textbox1 to be appended with just that line (I can handle the formatting, using the string.format stuff). If I try to do this using csvalue = csvtranslator.ReadLine , I get the following error message:
"Error 1 Value of type 'String' cannot be converted to '1-dimensional array of String'."
If I then put it as an array, ie: csvalue() = csvtranslator.ReadLine , I then get a different error message:
"Error 1 Number of indices is less than the number of dimensions of the indexed array."
What's the knack, guys? I've spent hours trying to figure this out.
Please go easy on me - and keep any responses ultra-simple for my newbie brain - I'm very new to all this programming malarkey and just starting out! :)
Structure systemstructure
Dim number As Byte
Dim name As String
Dim procspeed As String
Dim ram As String
Dim harddrive As String
Dim price As Integer
End Structure
Private Sub csvmanagement()
Dim systemspecs As New systemstructure
Using csvparser As New FileIO.TextFieldParser _
("F:\folder\programname\programname\bin\Debug\systems.csv")
Dim csvalue As String()
csvparser.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
csvparser.Delimiters = New String() {","}
csvalue = csvparser.ReadFields()
systemspecs.number = csvalue(0)
systemspecs.name = csvalue(1)
systemspecs.procspeed = csvalue(2)
systemspecs.ram = csvalue(3)
systemspecs.harddrive = csvalue(4)
systemspecs.optical = csvalue(5)
systemspecs.graphics = csvalue(6)
systemspecs.audio = csvalue(7)
systemspecs.monitor = csvalue(8)
systemspecs.software = csvalue(9)
systemspecs.price = csvalue(10)
While Not csvparser.EndOfData
csvalue = csvparser.ReadFields()
systemlist.Items.Add(systemspecs)
End While
End Using
End Sub
Edit:
Thanks for your help guys, I've managed to solve the problem now.
It was merely a matter calling loops at the right point in time.
I would recommend using FileHelpers to do the reading.
The binding shouldn't be an issue after that.
Here is the Quickstart for Delimited Records:
Dim engine As New FileHelperEngine(GetType( Customer))
// To Read Use:
Dim res As Customer() = DirectCast(engine.ReadFile("FileIn.txt"), Customer())
// To Write Use:
engine.WriteFile("FileOut.txt", res)
When you get the file read, put it into a normal class and just bind to the class or use the list of items you have to do custom stuff with the combobox. Basically, get it out of the file and into a real class asap, then things will be easier.
At least take a look at the library. After using it, we use a lot more simple flat files since it is so easy, and we haven't written a file access routine since (for that kinda stuff).
http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.textfieldparser.aspx
I think your main problem is understanding how arrays work (hence the error message).
You can use split and join functions to convert strings into and out of arrays
dim s() as string = split("1,2,3",",") gives and array of strings with 3 elements
dim ss as string = join(s,",") gives you the string back
Firstly, it's actually really good that you are using the TextFieldParser for reading CSV files - most don't but you won't have to worry about extra commas and quoted text etc...
The Readline method only gives you the raw string, hence the "Error 1 Value of type 'String' cannot be converted to '1-dimensional array of String'."
What you may find easier with combo boxes etc is to use an object (e.g. 'systemspecs') rather than strings. Assign the CSV data to the objects and override the "ToString" method of the 'systemspecs' class to display in the combo box how you want with formatting etc. That way when you handle the SelectedIndexChanged event (or similar) you get the "SelectedItem" from the combo box (which can be Nothing so check) and cast it as the 'systemspecs' to use it. The advantage is that you are not restricted to display the exact data in the combo etc.
' in "systemspecs"...
Public Overrides Function ToString() As String
Return Name ' or whatever...
End Function ' ToString
e.g.
dim item as new systemspecs
item.ID = csvalue(1)
item.Name = csvalue(2)
' etc...
combobox1.Items.Add(item)
Let me know if that makes sense!
PK :-)