Call a Sub in a Textbox VB.Net - vb.net

Get all combinations which declares an established amount sum
I managed to convert the code written to C #, and I would need some help. I don't know how to let them display the appropriate values in my Textbox.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
' Call (Main) - How to call in a Textbox1.Text
End Sub
Code:
Class SurroundingClass
Private Sub Main()
Dim numbers As Integer() = {3, 9, 8, 4, 5, 7, 10}
Dim target As Integer = 15
sum_up(New List(Of Integer)(numbers.ToList()), target)
End Sub
Private Shared Sub sum_up_recursive(ByVal numbers As List(Of Integer), ByVal target As Integer, ByVal part As List(Of Integer))
Dim s As Integer = 0
For Each x As Integer In part
s += x
Next
If s = target Then
Console.WriteLine("sum(" & String.Join(",", part.[Select](Function(n) n.ToString()).ToArray()) & ")=" + target)
End If
If s >= target Then
Return
End If
For i As Integer = 0 To numbers.Count - 1
Dim remaining = New List(Of Integer)()
Dim n As Integer = numbers(i)
For j As Integer = i + 1 To numbers.Count - 1
remaining.Add(numbers(j))
Next
Dim part_rec = New List(Of Integer)(part)
part_rec.Add(n)
sum_up_recursive(remaining, target, part_rec)
Next
End Sub
Private Shared Sub sum_up(ByVal numbers As List(Of Integer), ByVal target As Integer)
sum_up_recursive(numbers, target, New List(Of Integer)())
End Sub
End Class

When you run the code you will see that it outputs the sets of results one set at a time in the line Console.WriteLine("sum(" & String.Join(",", part.[Select](Function(n) n.ToString()).ToArray()) & ")=" + target).
As you want to get the result sets in one go, you will need to accumulate them at that point instead of writing to the console, and the Sub will need to be a Function with another parameter for the accumulator.
Making those modifications:
Public Class Form1
' Derived from the code at https://stackoverflow.com/questions/4632322/finding-all-possible-combinations-of-numbers-to-reach-a-given-sum
Function SumUpRecursive(numbers As List(Of Integer), target As Integer, part As List(Of Integer), solutions As List(Of List(Of Integer))) As List(Of List(Of Integer))
Dim s = part.Sum()
If s = target Then
' MsgBox("sum(" & String.Join(",", part.[Select](Function(n) n.ToString()).ToArray()) & ")=" & target)
solutions.Add(part)
End If
If s >= target Then
Return Nothing
End If
For i As Integer = 0 To numbers.Count - 1
Dim remaining = New List(Of Integer)()
Dim n As Integer = numbers(i)
For j As Integer = i + 1 To numbers.Count - 1
remaining.Add(numbers(j))
Next
Dim part_rec = New List(Of Integer)(part)
part_rec.Add(n)
SumUpRecursive(remaining, target, part_rec, solutions)
Next
Return solutions
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim numbers As Integer() = {3, 9, 8, 4, 5, 7, 10}
Dim target As Integer = 15
Dim nums = SumUpRecursive((numbers.ToList()), target, New List(Of Integer), New List(Of List(Of Integer)))
If nums Is Nothing Then
MsgBox("Failure :(")
Else
MsgBox(String.Join(vbCrLf, nums.Select(Function(b) String.Join(", ", b))))
' TextBox1.Lines = nums.Select(Function(b) String.Join(", ", b)).ToArray()
End If
End Sub
End Class
I'm sure there's a neater way of writing it.

Related

Using listbox to display an excel file?

So after some changes, I have used this code:
Dim XlApp = New Microsoft.Office.Interop.Excel.Application
Dim oBook As Object = XlApp.Workbooks.Open("C:\file.xlsx")
Dim oSheet As Object = oBook.Worksheets(1)
Private Sub form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim StartedRow As Integer
Dim TotalRows As Integer
TotalRows = XlApp.ActiveWorkbook.Sheets(1).Range("a1").CurrentRegion.Rows.Count
For StartedRow = 1 To TotalRows
Me.ListBox1.Items.Add(oSheet.Cells(StartedRow, 1).text)
Me.ListBox1.Items.Add(oSheet.Cells(StartedRow, 2).text)
Next
MessageBox.Show("Succesful")
This one works but only shows two rows which is not in proper order, I need to show the whole file.
Sorry, since I'm new to Stack Overflow I find it kinda confusing for now :)
You need to declare your variables as specific types so you can use the properties and methods of that type. In general, don't let variables flap about as Object unless absolutely necessary.
I used .UsedRange to get row count. It seemed a bit simpler.
I used an interpolated string, indicated by the $, to get the range. In versions of Visual Studio prior to 2015 you will have to use
String.Format("A{0}", RowNum)
Excel is hard to get rid of. Therefore, put the Excel stuff in a separate method and add the two GC calls when the method returns.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
DoExcelThing()
GC.Collect()
GC.WaitForPendingFinalizers()
'Even doing this will still leave a Zoombie Excel in Task Manager when debugging. (Hopefully not release)
MessageBox.Show("Succesful")
End Sub
Private Sub DoExcelThing()
Dim XlApp As New Microsoft.Office.Interop.Excel.Application
Dim oBook As Workbook = XlApp.Workbooks.Open("C:\file.xlsx")
Dim oSheet As Worksheet = CType(oBook.Worksheets(1), Worksheet)
Dim TotalRows = oSheet.UsedRange.Rows.Count
For RowNum = 1 To TotalRows
ListBox1.Items.Add(oSheet.Range($"A{RowNum}").Value)
ListBox1.Items.Add(oSheet.Range($"B{RowNum}").Value)
Next
End Sub
#Wakashio1234
This Will loop through all the cells..
TotalRows = XlApp.ActiveWorkbook.Sheets(1).Range("a1").CurrentRegion.Rows.Count
Totalcolumns = XlApp.ActiveWorkbook.Sheets(1).Range("a1").CurrentRegion.columns.Count
For StartedRow = 1 To TotalRows
For Startedcolumn = 1 To Totalcolumns
Me.ListBox1.Items.Add(oSheet.Cells(StartedRow, Startedcolumn).text)
Next
Next
MessageBox.Show("Successful")
The following code shows how to take out the data in Excel and put it into 'List(Of String())', then bind listbox control to the List and draw aligned columns of data in listbox.
Dim XlApp = New Microsoft.Office.Interop.Excel.Application
Dim oBook As Excel.Workbook = XlApp.Workbooks.Open("your file path")
Dim oSheet As Excel.Worksheet = oBook.Worksheets(1)
Dim lst As List(Of String()) = New List(Of String())()
Private RowHeight, RowWidth As Single
Private ColWidths As Single() = Nothing
Private Const RowMargin As Single = 10
Private Const ColumnMargin As Single = 10
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ListBox1.DrawMode = DrawMode.OwnerDrawVariable
ListBox1.DataSource = GetList()
End Sub
Private Function GetList() As List(Of String())
Dim rows As Integer = oSheet.UsedRange.Rows.Count
'If you want to display only two columns, set the value of 'cols' to 2
Dim cols As Integer = oSheet.UsedRange.Columns.Count
For r As Integer = 1 To rows
Dim Value As String() = New String(cols - 1) {}
For c As Integer = 1 To cols
Value(c - 1) = oSheet.Cells(r, c).Text
Next
lst.Add(Value)
Next
oBook.Close()
XlApp.Quit()
Return lst
End Function
Private Sub ListBox1_MeasureItem(sender As Object, e As MeasureItemEventArgs) Handles ListBox1.MeasureItem
If ColWidths Is Nothing Then
GetRowColumnSizes(e.Graphics, ListBox1.Font, lst, RowHeight, ColWidths)
For i As Integer = 0 To ColWidths.Length - 1
ColWidths(i) += ColumnMargin
Next
RowHeight += RowMargin
RowWidth = ColWidths.Sum()
End If
e.ItemHeight = CInt(RowHeight)
e.ItemWidth = CInt(RowWidth)
End Sub
Private Sub ListBox1_DrawItem(sender As Object, e As DrawItemEventArgs) Handles ListBox1.DrawItem
Dim values As String() = CType(ListBox1.Items(e.Index), String())
e.DrawBackground()
If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
DrawRow(e.Graphics, ListBox1.Font, SystemBrushes.HighlightText, Nothing, e.Bounds.X, e.Bounds.Y, RowHeight, ColWidths, values, False)
Else
DrawRow(e.Graphics, ListBox1.Font, Brushes.Black, Nothing, e.Bounds.X, e.Bounds.Y, RowHeight, ColWidths, values, False)
End If
End Sub
Private Sub GetRowColumnSizes(ByVal gr As Graphics, ByVal font As Font, ByVal values As List(Of String()), ByRef max_height As Single, ByRef col_widths As Single())
Dim num_cols As Integer = values(0).Length
col_widths = New Single(num_cols - 1) {}
max_height = 0
For Each row As String() In values
For col_num As Integer = 0 To num_cols - 1
Dim col_size As SizeF = gr.MeasureString(row(col_num), font)
If col_widths(col_num) < col_size.Width Then col_widths(col_num) = col_size.Width
If max_height < col_size.Height Then max_height = col_size.Height
Next
Next
End Sub
Private Sub DrawRow(ByVal gr As Graphics, ByVal font As Font, ByVal brush As Brush, ByVal box_pen As Pen, ByVal x0 As Single, ByVal y0 As Single, ByVal row_height As Single, ByVal col_widths As Single(), ByVal values As String(), ByVal draw_box As Boolean)
Dim rect As RectangleF = New RectangleF()
rect.Height = row_height
Using sf As StringFormat = New StringFormat()
Dim x As Single = x0
For col_num As Integer = 0 To values.Length - 1
sf.Alignment = StringAlignment.Near
sf.LineAlignment = StringAlignment.Center
rect.X = x
rect.Y = y0
rect.Width = col_widths(col_num)
gr.DrawString(values(col_num), font, brush, rect, sf)
If draw_box Then gr.DrawRectangle(box_pen, rect.X, rect.Y, rect.Width, rect.Height)
x += col_widths(col_num)
Next
End Using
End Sub
Result of my test:
For more details you can see: Make an owner-drawn ListBox that justifies columns in C#

Variable not setting or for loop not working correctly

i'm having an issue with setting a variable to the return value of a function i made i can't seem to be able to get it to work and i get no errors or feedback from visual studios even in strict mode
Imports System.Net
Imports System.IO
Imports System.ComponentModel
Public Class Form2
Dim i As Integer
Public CleanSearchTexts As String()
Dim count As Integer = 0
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If BackgroundWorker1.IsBusy = False Then
i = RichTextBox1.Lines.Count
i = i - 1
BackgroundWorker1.RunWorkerAsync()
Else
MsgBox("Threads are busy")
End If
End Sub
Private Sub StartThreads()
Dim SearchText As String
For count = count To i
SearchText = LineFunc(count)
count += 1
SearchText = CType(Ask_Query(SearchText), String)
SearchText = CType(Bing_Query(SearchText), String)
SearchText = CType(Yahoo_Query(SearchText), String)
Dim thread_count As String = CType(Process.GetCurrentProcess().Threads.Count - 20, String)
Label_T(thread_count)
Threading.Thread.Sleep(500)
If SearchText.Contains("All_Query:Yes") Then
SearchText = SearchText.Replace("All_Query:Yes", "")
RTB(SearchText)
End If
Next
End Sub
Private Delegate Sub UpdateStatus(ByVal s As String)
Private Delegate Sub UpdateLabel(ByVal thread_count As String)
Public Delegate Function GetTextBox(ByVal index As Integer) As String
Public Function LineFunc(ByVal index As Integer) As String
If InvokeRequired Then
Invoke(New GetTextBox(AddressOf LineFunc), index)
Else
Dim indexSearchText As String
indexSearchText = RichTextBox1.Lines(index)
Return indexSearchText
End If
End Function
Public Sub RTB(ByVal s As String)
If Me.InvokeRequired Then
Me.Invoke(New UpdateStatus(AddressOf RTB), New Object() {s})
Else
RichTextBox2.AppendText(Environment.NewLine & s)
End If
End Sub
Public Sub Label_T(ByVal thread_count As String)
If Me.InvokeRequired Then
Me.Invoke(New UpdateLabel(AddressOf Label_T), New Object() {thread_count})
Else
Label3.Text = "Threads Running: " + thread_count
End If
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
BackgroundWorker1.WorkerSupportsCancellation = True
BackgroundWorker1.WorkerReportsProgress = True
Dim count As Integer
Dim num As Integer = CInt(TextBox1.Text) - 1
For count = 0 To num
Dim thread = New Threading.Thread(AddressOf StartThreads)
thread.IsBackground = True
thread.Name = "Web Thread #" + CType(count, String)
thread.Start()
Threading.Thread.Sleep(500)
Next
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
MsgBox("Work is done")
End Sub
End Class
the issue is clearly with either the line getting function or the for count
i have tested the function by adding a button and doing the process of setting a variable to the return and msgboxing it and it works perfectly not only that when i breakpoint it it shows it's returning the right output but its just not setting it for whatever reason in this sub
Public Sub StartThreads(ByVal List As Object)
Dim List2 As String() = CType(List, String())
For Each dork As String In List2
dork = CType(Ask_Query(dork), String)
dork = CType(Bing_Query(dork), String)
dork = CType(Yahoo_Query(dork), String)
Dim thread_count As String = CType(Process.GetCurrentProcess().Threads.Count - 20, String)
Label_T(thread_count)
Threading.Thread.Sleep(500)
If dork.Contains("All_Query:Yes") Then
dork = dork.Replace("All_Query:Yes", "")
RTB(dork)
End If
Next
End Sub
and here is the function
Public Delegate Function GetTextBox(ByVal index As Integer) As String
Public Function LineFunc(ByVal index As Integer) As String
If InvokeRequired Then
Invoke(New GetTextBox(AddressOf LineFunc), index)
Else
Dim indexSearchText As String
indexSearchText = RichTextBox1.Lines(index)
Return indexSearchText
End If
End Function
Update: List Split Func
Public Function splitData(ByVal width As Integer, ByVal dd As List(Of String)) As List(Of List(Of String))
Dim dds As New List(Of List(Of String))
Dim numberOfLists As Integer = (dd.Count \ width)
For i As Integer = 0 To numberOfLists
Dim newdd As List(Of String)
newdd = dd.Skip(i * width).Take(width).ToList()
dds.Add(newdd)
Next i
Return dds
End Function
Updated Background Worker
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
BackgroundWorker1.WorkerSupportsCancellation = True
BackgroundWorker1.WorkerReportsProgress = True
Dim dds As New List(Of String)
GetList(dds)
Dim num As Integer = CInt(TextBox1.Text)
Dim ThreadCount As Integer = CInt(TextBox1.Text)
If ThreadCount > 1 Then
ThreadCount -= 1
End If
Dim RTBLines As Integer = RTB_Lines()
num = CInt(RTBLines / num)
num = CInt(Math.Ceiling(num))
Dim splitdd As List(Of List(Of String)) = CType(splitData(num, dds), List(Of List(Of String)))
For count = 0 To ThreadCount
Dim ListArray As String() = splitdd(count).ToArray
Dim newthread As New Thread(AddressOf StartThreads)
newthread.Name = "Web Thread #" + CType(count, String)
newthread.Start(ListArray)
Threading.Thread.Sleep(500)
Next
End Sub

How to display random ques from text file in vb?

I have created a Quiz application in Visual Basic.I have stored the questions in a text file and I'm using streamreader to read the lines.The text file looks like this
If x is the first of five consecutive odd numbers then what is their average ?
x
x+1
x+4
x+3
3
Which of the following number is divisible by 24 ?
76300
78132
80424
81234
3
The first line is the question,the lines 2 to 5 is the options and the 6th line is the answer key and there are more than 100 questions and I should print random questions and its corresponding choices each time I open the application and it should not repeat the same question.Can any one give me a code snippet for this?
Imports System.IO
Imports System.Runtime.InteropServices
Public Class Quiz
Public ques As Integer = 1
Dim Shuffle As Integer = 0
Dim SCORE As Integer = 0
Dim val As Integer = 30
Public anskey As String
Private currentQuestion As Integer
Private listOfQuestions As List(Of Question) = New List(Of Question)
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
End Function
Public Sub Reset_all()
val = 30
SCORE = 0
ProgressBar1.Value = 0
Button3.Hide()
ProgressBar1.Minimum = 0
ProgressBar1.Maximum = 30
Timer1.Enabled = True
Using reader = New System.IO.StreamReader("Quiz.txt")
Dim line = reader.ReadLine()
While (Not String.IsNullOrWhiteSpace(line))
Dim question = New Question
question.Question = line
question.Choice1 = reader.ReadLine()
question.Choice2 = reader.ReadLine()
question.Choice3 = reader.ReadLine()
question.Choice4 = reader.ReadLine()
question.Answer = reader.ReadLine()
listOfQuestions.Add(question)
line = reader.ReadLine()
End While
End Using
If listOfQuestions.Count > 0 Then
LoadQuestion(0)
End If
End Sub
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Reset_all()
End Sub
Sub LoadQuestion(questionIndex As Integer)
Dim question = listOfQuestions(questionIndex)
currentQuestion = questionIndex
If listOfQuestions.Count - 1 = currentQuestion Then
End If
With question
Label3.Text = ques
Label1.Text = .Question
RadioButton1.Text = .Choice1
RadioButton2.Text = .Choice2
RadioButton3.Text = .Choice3
RadioButton4.Text = .Choice4
anskey = .Answer
End With
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If (SCORE > 0) Then
SCORE -= 1
End If
If (currentQuestion > 0) Then
If (ques > 0) Then
ques -= 1
LoadQuestion(currentQuestion - 1)
End If
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If (anskey = "a" And RadioButton1.Checked = True Or anskey = "b" And RadioButton2.Checked = True Or anskey = "c" And RadioButton3.Checked = True Or anskey = "d" And RadioButton4.Checked = True) Then
SCORE += 1
End If
If (currentQuestion < listOfQuestions.Count - 1) Then
If (ques <= 99) Then
ques += 1
LoadQuestion(currentQuestion + 1)
End If
End If
End Sub
Private Sub Quiz_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
Dashboard.Show()
Me.Hide()
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
ProgressBar1.Value += 1
val -= 1
Label2.Text = val & " Sec"
If ProgressBar1.Value = ProgressBar1.Maximum Then
Timer1.Enabled = False
End If
If ProgressBar1.Value > 23 Then
SendMessage(ProgressBar1.Handle, 1040, 2, 0)
Button3.Show()
End If
If ProgressBar1.Value = 30 Then
End If
End Sub
Private Sub SubmitResult()
MsgBox("You have Scored " + SCORE.ToString + " Out of 100")
End Sub
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim Re As Integer = MsgBox("Are you sure you want to submit?",
vbYesNo, "Submit")
If (Re = 6) Then
SubmitResult()
Try
Me.Close()
Dashboard.Show()
Catch ex As Exception
End Try
End If
End Sub
Private Sub Label1_Click(sender As Object, e As EventArgs) Handles Label1.Click
End Sub
End Class
Public Class Question
Public Property Question As String
Public Property Choice1 As String
Public Property Choice2 As String
Public Property Choice3 As String
Public Property Choice4 As String
Public Property Answer As String
End Class
I would make a class "question" like this:
public Class Question
public questionas String
public answer1 as String
public answer2 as String
public answer3 as String
public answer4 as String
public correctAnswer as integer
public sub new(que as string, a1 as string, a2 as string, a3 as string, a4 as string, answer as integer)
question= que
answer1=a1
answer2=a2
answer3=a3
answer4=a4
correctAnswer=answer
end sub
end Class
Now load all your questions in the programm like this:
Imports System
Imports System.IO
Class MainWindow
private listQuestions as List(Of Question)
Public Sub window_loaded() handles MainWindow.loaded
listQuestions = loadAllQuestions()
End Sub
private function loadAllQuestions() as List(Of Question)
Dim str() As String
Try
' Open the file using a stream reader.
Using sr As New StreamReader("example.txt")
Dim line As String
' Read the stream to a string and write the string to the console.
line = sr.ReadToEnd()
Str = line.Split(vbNewLine)
End Using
Catch e As Exception
Console.WriteLine("The file could not be read:")
Console.WriteLine(e.Message)
End Try
'So now put the questions in your list:
dim list as new List(Of Question)
For i = 0 to str.count - 1
if (i+1) mod 5 = 0 then 'is divible without rest by 6
list.add(new Question(str(i-5), str(i-4), str(i-3), str(i-2), str(i-1), str(i))
end if
next
return list
end sub
'Load a random question:
private sub btNext_click() handles btNext.click()
dim ranQuestion as Question
dim r as new random
ranQuestion = listFragen.item(r.next(0,listQuestions.count))
End Class
Hope i could help you. To prevent that the programm can show the same question again its your job :)
First, I would recommend using the File.ReadAllLines() function to get an array of lines in the text file with questions. Then you can access them easily.
If you observe, the index of the line of the question will be (n - 1) * 6, where n is the question number. Once you get that, the indices of the options are given by:
i + 1
i + 2
i + 3
i + 4
where i = (n - 1) * 6. The answer key is given by:
i + 5
That should get you started. If you get stuck, leave a comment :)
So you would do the first part by:
Dim lines() As String = File.ReadAllLines("<yourQuestions.txt")
Then, you can generate a random number within the required range using:
Dim questionNumber As Integer = Random.Next(1, (lines.Length / 6) + 1)
After that, you can retrieve the question, options and answer key by:
Dim i As Integer = (questionNumber - 1) * 6
Dim question As String = lines(i)
Dim options() As String = {lines(i + 1), lines(i + 2), lines(i + 3), lines(i + 4)}
Dim answerKey As String = lines(Integer.Parse(lines(i + 5)))
You can also consider creating a class Question:
Public Class Question
Public Property Question As String
Public Property Choice1 As String
Public Property Choice2 As String
Public Property Choice3 As String
Public Property Choice4 As String
Public Property Answer As String
Public Sub New(q As String, c1 As String, c2 As String, c3 As String, c4 As String, ans As String)
Question = q
Choice1 = c1
Choice2 = c2
Choice3 = c3
Choice4 = c4
Answer = ans
End Sub
End Class
Then you can assign the properties.
Another alternative (might be better in performance, in fact, I think it should be) would be to use the File.ReadLines() function and use the Take() and Skip() extension methods of IEnumerable<T> (LINQ):
Dim questionNumber As Integer = Random.Next(1, (File.ReadLines("<yourQuestions.txt").Count() / 6) + 1)
Dim blockLines = File.ReadLines("<yourQuestions.txt").Skip((questionNumber - 1) * 6).Take(6)
Dim currentQuestion As New Question(blockLines(0), blockLines(1), blockLines(2), blockLines(3), blockLines(4), blockLines(blockLines(5)))
To start with, load your questions this way:
Dim questions = _
File _
.ReadLines("questions.txt") _
.Select(Function (x, n) New With { .Line = X, .Index = n }) _
.GroupBy(Function (xn) xn.Index \ 6, Function (xn) xn.Line) _
.Select(Function (xs) New Question() With _
{ _
.Question = xs(0), _
.Choice1 = xs(1), _
.Choice2 = xs(2), _
.Choice3 = xs(3), _
.Choice4 = xs(4), _
.Answer = xs(5) _
}) _
.ToArray()
That'll give you an array for your questions:
Next, you need to create a "queue.txt" file that contains the indices of your questions in a random order that you wish to display them in. Here's how to create your queue:
Dim rnd = New Random()
Dim queue = _
Enumerable _
.Range(0, questions.Length) _
.OrderBy(Function (n) rnd.Next()) _
.Select(Function (n) n.ToString()) _
.ToArray()
File.WriteAllLines("queue.txt", queue)
Then when you load your program, you can read this file and choose the next question to ask, and save the file, skipping the first question for next time, like this:
Dim queue = File.ReadAllLines("queue.txt")
Dim questionToAsk As Question = questions(Integer.Parse(queue.First()))
File.WriteAllLines("queue.txt", queue.Skip(1))
It would be up to you to make sure that the file is created when it doesn't exist and to write the code that checks if you've asked all the questions and need to re-create the queue.
Here are the basics of a class that uses XML.
Public Class QuestionAndAnswer
'the container for all questions/answers
Private ReadOnly qa As XElement = <QandA></QandA>
'the container for a question and some number of possible answers
Private ReadOnly ent As XElement = <entry></entry>
'the question
Private ReadOnly aquestion As XElement = <question></question>
'an answer - the c attribute will be "y" for the correct answer
Private ReadOnly ananswer As XElement = <answer c=""></answer>
Private theQA As XElement
Public Sub New()
Me.theQA = New XElement(qa) 'set up
End Sub
Public Sub New(path As String)
Me.theQA = XElement.Load(path)
End Sub
Public Sub Save(path As String)
Me.theQA.Save(path)
End Sub
Private Function AddQuestion(theQuestion As String, correctAnsw As String) As XElement
Dim e As New XElement(ent)
Dim q As New XElement(aquestion)
Dim a As New XElement(ananswer)
q.Value = theQuestion
a.Value = correctAnsw
a.#c = "y"
e.Add(q)
e.Add(a)
Me.theQA.Add(e)
Return e
End Function
Public Function AddQuestion(theQuestion As String, correctAnsw As String,
ans1 As String) As XElement
Dim e As XElement = Me.AddQuestion(theQuestion, correctAnsw)
Dim a As New XElement(ananswer)
a.Value = ans1
e.Add(a)
Return e
End Function
Public Function AddQuestion(theQuestion As String, correctAnsw As String,
ans1 As String, ans2 As String) As XElement
Dim e As XElement = Me.AddQuestion(theQuestion, correctAnsw, ans1)
Dim a As New XElement(ananswer)
a.Value = ans2
e.Add(a)
Return e
End Function
Public Function AddQuestion(theQuestion As String, correctAnsw As String,
ans1 As String, ans2 As String, ans3 As String) As XElement
Dim e As XElement = Me.AddQuestion(theQuestion, correctAnsw, ans1, ans2)
Dim a As New XElement(ananswer)
a.Value = ans3
e.Add(a)
Return e
End Function
Public Function AddQuestion(theQuestion As String, correctAnsw As String,
ans1 As String, ans2 As String, ans3 As String, ans4 As String) As XElement
Dim e As XElement = Me.AddQuestion(theQuestion, correctAnsw, ans1, ans2, ans3)
Dim a As New XElement(ananswer)
a.Value = ans4
e.Add(a)
Return e
End Function
Private Shared prng As New Random
Public LastQuestionAnswer As String
Public Function RandomQuestion() As String
Dim q As XElement = Me.SelectRandomQ
If q IsNot Nothing Then
Dim rv As New System.Text.StringBuilder
rv.AppendLine(q.<question>.Value)
rv.AppendLine()
Dim ie As IEnumerable(Of XElement)
ie = From qa In q.<answer>
Select qa
ie = ie.OrderBy(Function() prng.Next(q.<answer>.Count))
Dim x As Integer = 1
For Each a As XElement In ie
If a.#c = "y" Then
Me.LastQuestionAnswer = x.ToString
End If
rv.AppendFormat("{0}. {1}", x, a.Value)
rv.AppendLine()
x += 1
Next
rv.AppendLine()
Me.LastQuestionAnswer = Me.LastQuestionAnswer.Insert(0, rv.ToString)
Debug.WriteLine(Me.LastQuestionAnswer)
Return rv.ToString
End If
Return ""
End Function
Private Function SelectRandomQ() As XElement
If Me.theQA IsNot Nothing AndAlso Me.theQA.<entry>.Count > 0 Then
Dim ie As IEnumerable(Of XElement)
ie = From ent In Me.theQA.Elements
Where ent.#used <> "y"
Select ent
Dim rv As XElement = ie(prng.Next(ie.Count))
rv.#used = "y"
Return rv
End If
Return Nothing
End Function
End Class
A form with a richtextbox and a button shows it
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim foo As New QuestionAndAnswer
foo.RandomQuestion()
foo.AddQuestion("Which Vietnam War film directed by Francis Ford Coppola followed a screenplay based on famous adventure story Heart of Darkness?",
"Apocalypse Now",
"Good Morning Vietnam",
"Born on the Fourth of July",
"Band of Brothers")
foo.AddQuestion("What was the name of the baseball pitcher that hit a bird with a pitch during a 2001 Spring Training game?",
"Randy Johnson",
"Mike Mussina",
"Roger Clemens",
"Greg Maddux",
"Johan Santana")
foo.AddQuestion("The third largest desert in the world is the Sahara, what is the first?",
"Antarctic",
"Gobi",
"Sonoran")
foo.AddQuestion("How many US presidents have died while in office?",
"8",
"6",
"7")
foo.AddQuestion("If x is the first of five consecutive odd numbers > 0, then what is their average?",
"x + 4",
"x",
"x + 1",
"x + 3")
Dim qa As New System.Text.StringBuilder
foo.RandomQuestion()
qa.AppendLine(foo.LastQuestionAnswer)
qa.AppendLine()
foo.RandomQuestion()
qa.AppendLine(foo.LastQuestionAnswer)
qa.AppendLine()
foo.RandomQuestion()
qa.AppendLine(foo.LastQuestionAnswer)
RichTextBox1.Text = qa.ToString
End Sub

New Random in a For-Next-Statement Repeats the Same Value

I am creating 3 random bytes in a for-next-statement.
However, they are always the same for each for-next, for example "333", or "777".
I wonder why this is so and what I am doing wrong.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim s As String = String.Empty
For i As Integer = 0 To 2
s += GetRandomByte().ToString
Next
Me.TextBox1.Text = s
End Sub
Public Function GetRandomByte(Optional ByVal uLow As Byte = 1, Optional ByVal uHigh As Byte = 9) As Byte
Dim r As New Random
Dim b As Integer = r.Next(CInt(uLow), CInt(uHigh + 1))
Return b
End Function
In your constructor for the new Random, give it a new seed each time. I like to use the current timestamp's hashcode value
New Random(Now.GetHashCode)
keep same instance for all calls:
Dim r As New Random()
Public Function GetRandomByte(Optional ByVal uLow As Byte = 1, Optional ByVal uHigh As Byte = 9) As Byte
Return r.Next(CInt(uLow), CInt(uHigh + 1))
End Function
or, pass to Random constructor a seed:
Dim r As New Random(CInt(Date.Now.Ticks And &h0000FFFF))

VB.net multithreading for loop, parallel threads

I have a simple form with 2 RichTextBoxes and 1 button, the code grabs the url address from RichTextBox1 and phrases the page for the title field using regex and appends it to RichTextBox2. I want to multithread everything in such way that none of the url's are skipped and the thread numbers can be set ( according to the system free resources ) For example, let's say 10 threads to run in parallel. I searched everything and the best that I managed to do is run everything in a background worker and keep the GUI from freezing while working. A short code sample will be of much help, I am a beginner in VB.net.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
For i = 0 To RichTextBox1.Lines.Length - 1
If RichTextBox1.Lines(i).Contains("http://") Then
Dim html As String = New System.Net.WebClient() _
.DownloadString(RichTextBox1.Lines(i))
Dim pattern As String = "(?<=\<title\>)([^<]+?)(?=\</title\>)"
Dim match As System.Text.RegularExpressions.Match = _
System.Text.RegularExpressions.Regex.Match(html, pattern)
Dim title As String = match.Value
RichTextBox2.AppendText(title & vbCrLf)
End If
Next
End Sub
End Class
Updated code ( throwing "Index was outside the bounds of the array." errors. )
Imports System
Imports System.Threading
Public Class Form1
Public Sub test(ByVal val1 As String, ByVal val2 As String)
Dim zrow As String
zrow = RichTextBox1.Lines(val1)
If zrow.Contains("http://") Then
Dim html As String = New System.Net.WebClient().DownloadString(zrow)
Dim pattern As String = "(?<=\<title\>)([^<]+?)(?=\</title\>)"
Dim match As System.Text.RegularExpressions.Match = System.Text.RegularExpressions.Regex.Match(html, pattern)
Dim title As String = match.Value
RichTextBox2.AppendText(val2 & title & vbCrLf)
End If
End Sub
Public Sub lastfor(ByVal number)
Dim start As Integer = number - 100
For x = start To number - 1
Try
test(x, x)
RichTextBox2.AppendText(x & RichTextBox1.Lines(x).Trim & vbCrLf)
Catch ex As Exception
'MsgBox(ex.Message)
RichTextBox3.AppendText(ex.Message & vbCrLf & vbCrLf)
End Try
Next
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Control.CheckForIllegalCrossThreadCalls = False
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim TotalLines As String = RichTextBox1.Lines.Length - 1
Dim TotalThreads As Integer = 10
Dim LinesPerThread As Integer = TotalLines / TotalThreads
Dim increment As String = LinesPerThread
Dim zdata(TotalThreads) As String
For i = 0 To TotalThreads - 1
zdata(i) = increment
increment = increment + LinesPerThread
Next
Dim lst As New List(Of Threading.Thread)
For Each bump As String In zdata
Dim t As New Threading.Thread(Function(l As String)
'Do something with l
'Update GUI like this:
If bump = String.Empty Or bump Is Nothing Then
Else
lastfor(l)
'MsgBox(l)
End If
End Function)
lst.Add(t)
t.Start(bump)
Next
'test(1)
End Sub
End Class
There are two ways two achieve this:
First, if you are using .NET 4.0, you could use a Parallel.ForEach loop:
Parallel.ForEach(RichTextBox1.Lines, Function(line As String)
' Do something here
' To update the GUI use:
Me.Invoke(Sub()
' Update GUI like this...
End Sub)
Return Nothing
End Function)
The other way is to do this manually (and you will have slightly more control):
Dim lst As New List(Of Threading.Thread)
For Each line In RichTextBox1.Lines
Dim t As New Threading.Thread(Function(l As String)
'Do something with l
'Update GUI like this:
Me.Invoke(Sub()
'Update Gui...
End Sub)
End Function)
lst.Add(t)
t.Start(line)
Next
Both of these are very crude, but will get the job done.
EDIT:
Here is a sample code that will control the number of threads:
Dim lst As New List(Of Threading.Thread)
Dim n As Integer = 1 ' Number of threads.
Dim npl As Integer = RichTextBox1.Lines / n
Dim seg As New List(Of String)
For Each line In RichTextBox1.Lines
For i = npl - n To npl
seg.Add(RichTextBox1.Lines.Item(i))
Next
Dim t As New Threading.Thread(Function(l As String())
For Each lin In l
' TO-DO...
Next
'Do something with l
'Update GUI like this:
Me.Invoke(Sub()
'Update Gui...
End Sub)
End Function)
lst.Add(t)
t.Start(seg.ToArray())
Next
*The above code might have bugs.