I'm writing a program that has two forms. One form gets the user to enter multiple values, and then does some calculations. Then it passes that information to another form However I can't figure out how to do it. Here is a relevant part of my code. To head some confusion, I am trying to pass 11 values, also initially, form 2 is not shown, and then when the values are passed from form 1 to form 2, then form 1 goes away and form 2 is the only one that shown
NOTE: This is not all my code, I don't believe all my code is required (I have 1000 lines right now) However this is the code with the information I want to be passed to the other form.
A lot of people are apparently saying that this is a duplicate of another question, however that question, he seems to already know how to pass the variables, but is just having issues with it (and even with looking at his, i cant figure it out)
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
'declarations
Dim intNormal As Integer
Dim intChildren As Integer
Dim intBonanza As Integer
Dim intDiamond As Integer
Dim intPictureFrame As Integer
Dim intKite As Integer
Dim intCrazyT As Integer
Dim intLetterX As Integer
Dim int2PostageStamp As Integer
Dim intPick7 As Integer
Dim intJackpot As Integer
Validate()
If txtNormal1.Enabled = False Then
intNormal = intNormInput
Else
intNormal = CalcNormalBooks()
End If
If txtChildren1.Enabled = False Then
intChildren = intChildInput
Else
intChildren = calcChildrensBooks()
End If
If txtBonanza1.Enabled = False Then
intBonanza = intBonInput
Else
intBonanza = calcBonanza()
End If
If txtSpecial1.Enabled = False Then
intSpecial = intSpeInput
Else
intSpecial = calcSpecialBooks(intSpecial)
End If
If txtDiamond1.Enabled = False Then
intDiamond = intDiaInput
Else
intDiamond = calcDiamond(intSpecial)
End If
If txtPictureFrame1.Enabled = False Then
intPictureFrame = intPicInput
Else
intPictureFrame = calcPictureFrame(intSpecial)
End If
If txtKite1.Enabled = False Then
intKite = intKiteInput
Else
intKite = calcKite(intSpecial)
End If
If txtCrazyT1.Enabled = False Then
intCrazyT = intCrazyInput
Else
intCrazyT = calcCrazyT(intSpecial)
End If
If txtLetterX1.Enabled = False Then
intLetterX = intLettInput
Else
intLetterX = calcLetterX(intSpecial)
End If
If txt2PostageStamp1.Enabled = False Then
int2PostageStamp = intPostInput
Else
int2PostageStamp = CalcPostageStamp(intSpecial)
End If
If txtPick71.Enabled = False Then
intPick7 = intPickInput
Else
intPick7 = calcPick7(intSpecial)
End If
If txtJackpot1.Enabled = False Then
intJackpot = intJackInput
Else
intJackpot = calcJackpot()
End If
End Sub
Since I had almost the same requiremnt lately here is my solution:
Custom Event which fires when your 2nd Form is closing
Public Event HotKeyFormClosed As EventHandler(Of HotKeyFormClosedEventArgs)
Custom EventArgs class where you store your values you want to pass to Main Form
Public Class HotKeyFormClosedEventArgs
Inherits EventArgs
'Your properties here
Public Sub New(...) 'your params here
MyBase.New()
'set your properties here
End Sub
End Class
On 2nd Form handle FormClosed event and pass your values to EventArgs
Private Sub HotKey_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs)
RaiseEvent HotKeyFormClosed(Me, New HotKeyFormClosedEventArgs(...)) 'your params here
End Sub
On Main Form handle your custom event (here HotKeyFormClosed) and extract its values
AddHandler frmHotKey.HotKeyFormClosed, AddressOf HotKey_FormClosed;
...
Private Sub HotKey_FormClosed(sender As Object, e As HotKeyFormClosedEventArgs)
'Do stuff with values from e
End If
I have chosen the Event approach since it decouples the two forms from another.
One could easily duplicate the information on both forms, make them public and access it directly thru an object instance.
But I like the observable approach from the events more due to it gives mor flexibility (additonal forms using the same events etc.)
P.S.: I wrote my code in c# and blind entered the VB code here so be gracious.
The values/variables that a method expects to receive (specified in the method's signature) are called Parameters.
The values sent to a method when the method is called are called Arguments.
As long as the arguments used when calling a method match the parameters for that method, those values can be passed.
For example (and I'll try to apply this to your context), if you want to create an instance of a form that takes certain values, you can specify those parameters in the form's New event, like so:
Public Sub New(someInt As Integer)
'do something with someInt here
End Sub
Then when you call this method you'd pass it the arguments, like so:
Dim myInt As Integer = 10
Dim newForm As myForm = New myForm(myInt)
When I say the arguments need to match the parameters, that means the number of values, the order of those values, and the value types must be the same (or in the case of numbers the parameter's type must be the same or larger than the argument's type).
As long as that is true, then it shouldn't really matter how you pass these - you could pass 11 individual arguments, you just have to make sure you are matching the argument to the parameter.
Hope that helps!
Related
i have an external class(colorCode.vb) in same project as my main form.vb.
My objective is to send a value as argument as i call a method in the colorCode.vb class, and use the value returned by the method. I don't know if its logically possible . Here i tried this but failed.
in my colorCode.vb Class i have this codes:
Public Sub getValue(ByVal itemCode As Integer)
Dim codeVal() As Integer = {9999, 3034, 3040, 3035}
Dim colorVal As String
For counter As Integer = 0 To codeVal.Count Step 1
If (itemCode = codeVal(counter)) Then
Select Case codeVal(counter)
Case 9999
colorVal = "BRILLIANT WHITE EMULSION"
Case 3034
colorVal = "OFF-WHITE EMULSION"
End Select
End If
Next
End Sub
and in my main form.vb i did this
Private Sub descTextBox_TextChanged(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles descTextBox.TextChanged
Dim colorDesc As colorCode = New colorCode()
Dim itemCode As Integer = Integer.Parse(itemCodeTextBox.Text)
descTextBox.Text = colorDesc.getValue(itemCode)'this line triggers an error.
End Sub
please i need some help here. already running nuts
Please turn on Option Strict. This is a 2 part process. First for the current project - In Solution Explorer double click My Project. Choose Compile on the left. In the Option Strict drop-down select ON. Second for future projects - Go to the Tools Menu -> Options -> Projects and Solutions -> VB Defaults. In the Option Strict drop-down select ON. This will save you from bugs at runtime.
Subs in vb.net do not return values. You need a Function. Instead of looping through the array you can just check if the array contains itemCode and if it does proceed with the Select Case.
Do not put your code in the text changed. It will run every time a character is entered before your user has a chance to enter the entire code. Create a button and use that. Use .TryParse to validate the input. It will return True if valid and fill in the second parameter with the number.
Public Class colorCode
Public Function getValue(ByVal itemCode As Integer) As String
Dim codeVal() As Integer = {9999, 3034, 3040, 3035}
Dim colorVal As String = ""
If codeVal.Contains(itemCode) Then
Select Case itemCode
Case 9999
colorVal = "BRILLIANT WHITE EMULSION"
Case 3034
colorVal = "OFF-WHITE EMULSION"
Case 3040
colorVal = "Blue"
Case 3035
colorVal = "Green"
End Select
Else
colorVal = "No matching color"
End If
Return colorVal
End Function
End Class
And in the form...
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim itemCode As Integer
If Integer.TryParse(TextBox1.Text, itemCode) Then
Dim colorDesc As colorCode = New colorCode()
TextBox2.Text = colorDesc.getValue(itemCode)
Else
MessageBox.Show("Please enter a valid number")
End If
End Sub
Add the return statement. You should also check the value of itemCodeTextBox.Text to make sure it's a numeric value before you call colorDesc.getValue otherwise your program may crash if a non-numeric value is entered into the textbox
You are missing the "return" part of your method.
Public Function getValue(ByVal itemCode As Integer) As String
That's what it'll take to declare you're returning a string value, and then you actually return your vale at the end of the method.
Next
return colorVal
End Sub
There are other ways to do this as well as better ways to deal with your "getValue" code, but this should get you running for now. The rest of the improvements are likely a different Question, or even series of Questions. Or you could go to the Code Review stack and get more help there.
I am working on a desktop application developed in vb.net. I am trying to select the items in a checkedlistbox depending on the values I get from database. Below is the code to populate the checkedlistboxes
Private Sub LoadDisapprovalList()
cblFedralReasons.Items.Clear()
cblStateReasons.Items.Clear()
cblFedralReasons.DataSource = Main.DataClient.DisapprovalReasonList_Get(FedralReason)
cblFedralReasons.DisplayMember = "DisapprovalReasonTypeDesc"
cblFedralReasons.ValueMember = "DisapprovalReasonTypeGenId"
cblStateReasons.DataSource = Main.DataClient.DisapprovalReasonList_Get(StateReason)
cblStateReasons.DisplayMember = "DisapprovalReasonTypeDesc"
cblStateReasons.ValueMember = "DisapprovalReasonTypeGenId"
End Sub
After that I am trying to select the items based on the values from database. Here is the code
Private Sub LoadApplicationDisapprovalReasons()
Dim lstApplicationDisapprovalReasons As New List(Of DataService.usp_ApplicationDisapprovalReason_Get_Result)
lstApplicationDisapprovalReasons = Main.DataClient.ApplicationDisapprovalReason_Get(_SeqID)
If lstApplicationDisapprovalReasons.Count > 0 Then
For Each item In lstApplicationDisapprovalReasons
Dim selectedDisapprovalId As Integer = item.DisapprovalReasonTypeGenId
Select Case item.DisapprovalReasonType
Case FedralReason
Dim selectedIndex = cblFedralReasons.Items.IndexOf(selectedDisapprovalId)
cblFedralReasons.SetItemCheckState(selectedIndex, CheckState.Checked)
Case StateReason
Dim selectedIndex = cblStateReasons.Items.IndexOf(selectedDisapprovalId)
cblStateReasons.SetItemCheckState(selectedIndex, CheckState.Checked)
End Select
Next
End If
End Sub
But the problem is cblFedralReasons.Items.IndexOf always returns -1. All the data from database is coming correctly but something weird happening with checkedlistbox which I couldn't understand.
EDIT:
Also when I try to get the text of an item by using the following code it returns me name of my collections instead of the text.
cblFedralReasons.items(1).tostring
It returns
DisapprovalReasonList
and not the text of that item!
I'll try to explain what I think about this:
If cblFedralReasons has as Datasource a List(Of DataService.usp_DisapprovalReasonList), if you search a selectedDisapprovalId vía IndexOf passing an Integer on the list.... that -1 value returned, its coherent.
IndexOf, on a collection, are internally doing a Equals comparison. So you are comparing different types: an Integer vs a DataService.usp_DisapprovalReasonList.
There are many ways to get the correct object from the collection.
One idea could be do an override of object.equals in your class:
Public Overrides Function Equals(ByVal p_oAnotherObject As Object) As Boolean
If TypeOf p_oAnotherObject Is DataService.usp_DisapprovalReasonList AndAlso Me.GetType.Equals(p_oAnotherObject.GetType) Then
Return Me.DisapprovalReasonTypeGenId.Equals(DirectCast(p_oAnotherObject, DataService.usp_DisapprovalReasonList).DisapprovalReasonTypeGenId)
Else
Return False
End If
End Function
Assuming you have a constructor accepting an ID, you now can do this:
cblFedralReasons.Items.IndexOf(New DataService.usp_DisapprovalReasonList(selectedDisapprovalId))
and then, you will get it.
Finally, cblFedralReasons.items(1).tostring, you are getting the default GetType.Name. Do this in your class, then:
Public Overrides Function ToString() As String
Return DisapprovalReasonTypeDesc
End Function
Hope I have explained.
I have created two classes, one being an interface for the other. Each time I try to instantiate Transition_Model I get:
Compile error: Object Module needs to implement '~' for interface'~'
To my understanding Implementing class is supposed to have a copy of all public subs, function, & properties. So I don't understant what is the problem here?
Have seen similar questions come up but either they refer to actual Sub or they include other complications making answer too complicated for me to understand.
Also note I tried changing Subs of Transition_Model to Private and add 'IModel_' in front of sub names(Just like top answer in second question I linked) but I still receive the same error.
IModel
Option Explicit
Public Enum Model_Types
Transition
Dummy
End Enum
Property Get M_Type() As Model_Types
End Property
Sub Run(Collat As Collateral)
End Sub
Sub Set_Params(key As String, value As Variant)
End Sub
Transition_Model
Option Explicit
Implements IModel
Private Transitions As Collection
Private Loan_States As Integer
Private Sub Class_Initialize()
Set Transitions = New Collection
End Sub
Public Property Get M_Type() As Model_Types
M_Type = Transition
End Property
Public Sub Run(Collat As Collateral)
Dim A_Transition As Transition
Dim New_Balance() As Double
Dim Row As Integer
For Row = 1 To UBound(Collat.Curr_Balance)
For Each A_Transition In Transitions
If A_Transition.Begining = i Then
New_Balance = New_Balance + Collat.Curr_Balance(Row) * A_Transition.Probability
End If
Next A_Transition
Next
End Sub
Public Sub Set_Params(key As String, value As Double)
Dim Split_key(1 To 2) As String
Dim New_Transition As Transition
Split_key = Split(key, "->")
Set New_Transition = New Transition
With New_Transition
.Begining = Split_key(1)
.Ending = Split_key(2)
.Probability = value
End With
Transitions.Add New_Transition, key
End Sub
Lastly the Sub I am using to test my class
Sub Transition_Model()
Dim Tested_Class As New Transition_Model
Dim Collat As New Collateral
'Test is the model type is correct
Debug.Assert Tested_Class.M_Type = Transition
'Test if Model without transition indeed does not affect balances of its collateral
Collat.Curr_Balance(1) = 0.5
Collat.Curr_Balance(2) = 0.5
Tested_Class.Run (Collat)
Debug.Assert ( _
Collat.Curr_Balance(1) = 0.5 And _
Collat.Curr_Balance(2) = 0.5)
End Sub
Actaully Per the second question I linked has the correct answer which I missed.
All subs need to start with 'IModel_' and rest ot the name has to match the name in IModel.
AND
This is the part i missed, you cannot use underscore in the Sub name.
I have an UltraGrid in which I have many columns of which 2 columns are DateTime style. Now when I use the filter of that columns it shows all the DateTime values as a text in a dropdown. But I need that as a calendar in order to make the filter easy. It is enough to show just a calendar when clicking the filter.
I have tried some code but it doesn't work.
//Code:
Private Sub grdResult_BeforeRowFilterDropDown(ByVal sender As Object, ByVal e As Infragistics.Win.UltraWinGrid.BeforeRowFilterDropDownEventArgs) Handles grdResult.BeforeRowFilterDropDown
e.Cancel = True
UltraCalendarCombo1.Visible = True
UltraCalendarCombo1.Location = New Point(grdResult.Rows.FilterRow.Cells(e.Column).GetUIElement().Rect.Location.X, grdResult.Rows.FilterRow.Cells(e.Column).GetUIElement().Rect.Location.Y - 2)
UltraCalendarCombo1.Size = New System.Drawing.Size(grdResult.Rows.FilterRow.Cells(e.Column).GetUIElement().Rect.Size.Width, grdResult.Rows.FilterRow.Cells(e.Column).GetUIElement().Rect.Size.Height)
' UltraCalendarCombo1.DroppedDown = True
End Sub
The above event will fire when the filter dropdown is clicked.
private sub applyCustomeViewSettings(byval gridFormat as GridFormat)
....
...
For Each ColumnFormat In gridFormat.ColumnFormats
For Each column In Me.grdResult.DisplayLayout.Bands(0).Columns
If column.Key.ToUpper = ColumnFormat.ColumnKey.ToUpper Then
If column.Key.ToUpper = "PCSSTDT" Then
column.Header.Caption = IIf(ColumnFormat.Caption = "", ColumnFormat.ColumnKey, ColumnFormat.Caption)
column.Hidden = ColumnFormat.Hidden
'column.AllowRowFiltering = IIf(ColumnFormat.AllowRowFiltering = False, ColumnFormat.AllowRowFiltering, DefaultableBoolean.True) 'CType(ColumnFormat.AllowRowFiltering, DefaultableBoolean)
column.Width = ColumnFormat.Width
column.Header.VisiblePosition = ColumnFormat.VisiblePosition
column.Format = ColumnFormat.Format
column.SortIndicator = ColumnFormat.SortIndicator
' column.Style = ColumnStyle.Date
'column.EditorComponent = UltraCalendarCombo1
column.FilterOperandStyle = FilterOperandStyle.Default
Else
column.Header.Caption = IIf(ColumnFormat.Caption = "", ColumnFormat.ColumnKey, ColumnFormat.Caption)
column.Hidden = ColumnFormat.Hidden
column.AllowRowFiltering = IIf(ColumnFormat.AllowRowFiltering = False, ColumnFormat.AllowRowFiltering, DefaultableBoolean.True) 'CType(ColumnFormat.AllowRowFiltering, DefaultableBoolean)
column.Width = ColumnFormat.Width
column.Header.VisiblePosition = ColumnFormat.VisiblePosition
column.Format = ColumnFormat.Format
column.SortIndicator = ColumnFormat.SortIndicator
column.Style = ColumnFormat.Style
End If
End If
Next
....
...
End Sub
The above method makes the grid changes(apply settings) to show the filter as calendar.
But this doesn't work and showing me the same normal grid.
How can I achieve this?
I have managed to show a MonthCalendar when you press the icon to filter a DateTime column in an UltraWinGrid.
When the MonthCalendar is dispayed you could select a specific date using the familiar interface provided by this standard WinForm control. After selecting the date you could use the value to apply programmatically a filter condition to the UltraWinGrid column.
To reach this result you first need to add a reference to the Infragistics4.Win.SupportsDialog.v11.2 assembly where you can find the UltraGridFilterUIProvider class
Now, in your form where you need the filtering to appear, add this code: (it is just an example because I haven't your datasource and thus I have a prebuilt one with just one datetime column)
Imports Infragistics.Win.UltraWinGrid
Imports Infragistics.Win.SupportDialogs.FilterUIProvider
Public Class Form1
' This is the key object that let us customize '
' the Filter for the UltraWinGrid'
Dim _filterUIProvider as UltraGridFilterUIProvider
' In the InitializeLayout event we substitute the normal
' filter handler with the custom filter'
Private Sub UltraGrid1_InitializeLayout(sender As Object, e As Infragistics.Win.UltraWinGrid.InitializeLayoutEventArgs) Handles UltraGrid1.InitializeLayout
e.Layout.Override.AllowRowFiltering = Infragistics.Win.DefaultableBoolean.True
_filterUIProvider = New UltraGridFilterUIProvider()
' Comment out the following line to test the default
' **Excel Filter Style Interface** '
AddHandler _filterUIProvider.BeforeMenuPopulate, AddressOf _filterUIProvider_BeforeMenuPopulate
e.Layout.Override.FilterUIProvider = _filterUIProvider
End Sub
' Before the UltraGridFilterUIProvider shows its standard form interface
' we start a custom form used to apply our filtering logic '
' and block the display of the UltraGridFilterUIProvider interface '
Private Sub _filterUIProvider_BeforeMenuPopulate(sender As Object, e As Infragistics.Win.SupportDialogs.FilterUIProvider.BeforeMenuPopulateEventArgs)
' A custom form with the MonthCalendar and 3 buttons '
' to handle the filter logic '
Using fDate = new FormDate()
' Open our custom form with the monthcalendar
if (DialogResult.OK = fDate.ShowDialog())
' We need a nullable date to allow the removing of the filter'
Dim dtFilter As DateTime? = fDate.SelectedDate
if (dtFilter.HasValue)
' Apply programmatically a filtercondition to the column
' In this case I have only one column. so I use the index 0
' in your case this should change to reflect your column index
Dim fc = new FilterCondition(FilterComparisionOperator.Equals, dtFilter.Value)
ultraGrid1.DisplayLayout.Bands(0).ColumnFilters(0).FilterConditions.Add(fc)
Else
ultraGrid1.DisplayLayout.Bands(0).ColumnFilters.ClearAllFilters()
End If
End If
End Using
e.Handled = true ' Stop the standard interface'
End Sub
End Class
Now we need only a simple form called FormDate that contains a MonthCalendar and three buttons (Set, Clear, Cancel) that return (Set) a Date to set the filter, (Clear) a null value to remove previous filter and a cancel button to abort the processing
Here the code for the form, the design is trivial
Public Class FormDate
Public Property SelectedDate As DateTime?
Private Sub FormDate_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.SelectedDate = Nothing
End Sub
Private Sub cmdSet_Click(sender As Object, e As EventArgs) Handles cmdSet.Click
'This button has DialogResult=OK'
Me.SelectedDate = monthCalendar1.SelectionStart
End Sub
Private Sub cmdClear_Click(sender As Object, e As EventArgs) Handles cmdClear.Click
'This button has DialogResult=OK'
Me.SelectedDate = Nothing
End Sub
End Class
This could resolve your problem, however, I have discoverd what seems to be a bug in UltraGridFilterUIProvider. When I call the e.Handled=True my expected result is the filter to not show anything but, instead a small window still appears and I have to press Escape to hide it. I have not found any way to automatically hide it.
It seems to be a problem to signal to the Infragistics team.
I suggest you also to test the Excel Style Filter Interface provided automatically by the UltraGridFilterUIProvider. This interface has a lot of options and is much more preferable to the standard UI filter. To test this interface you should only comment out the AddHandler line above
EDIT Following the comment from #Alhalama I have tried to use the BeforeRowFilterDropDown event and the result are better (well it is perfect now). So I have commented out the line with the AddHandler, removed the code for the BeforeMenuPopulate and added the code for the BeforeRowFilterDropDown
Private Sub UltraGrid1_BeforeRowFilterDropDown(sender As Object, e As BeforeRowFilterDropDownEventArgs) Handles UltraGrid1.BeforeRowFilterDropDown
If e.Column.Key = "DateRequest" Then
Using fDate = New FormDate()
If DialogResult.OK = fDate.ShowDialog() Then
Dim dtFilter As DateTime? = fDate.SelectedDate
If (dtFilter.HasValue) Then
Dim fc As FilterCondition = New FilterCondition(FilterComparisionOperator.Equals, dtFilter.Value)
UltraGrid1.DisplayLayout.Bands(0).ColumnFilters(0).FilterConditions.Add(fc)
Else
UltraGrid1.DisplayLayout.Bands(0).ColumnFilters.ClearAllFilters()
End If
End If
End Using
e.Cancel = True
End If
End Sub
Now, when I try to open the filter for the column named DateRequest I open immediately the FormDate and at the end I set the Cancel property of the BeforeRowFilterDropDownEventArgs to true to avoid further processing of the filter dialog. This seems to be perfect...... Great credit for this to Mr. Alhalama. If you wish I think you should post your own answer because your suggestion really makes the difference.
I have a small VB.Net program that shows a progress form. On that box, I show a progress bar and a percentage complete in a text box. Both of these can be derived by a single integer, but now I've been asked to put a 'Details' text box on the progress form, which updates with text to show how the job is doing.
My question - how do I pass both the percentage, and the text details to the ProgressChanged() function? I've tried passing an array and an object, but I can't seem to get it right. Thanks.
I'm calling the function like this (from the bw_DoWork() function) -
details = "Textbox details will go here"
progress = (100 / (UBound(servers) + 1)) * (i + 1)
bw.ReportProgress(progress) ' How do I also pass 'details'?
And here is the function it self -
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
Me.proProgress.Value = e.ProgressPercentage
Me.txtProgress.Text = e.ProgressPercentage.ToString() & "% complete..."
'Me.txtDetails.Text = Me.txtDetails.Text & IIf(Me.txtDetails.Text <> "", vbCrLf, "") & e.details ' How do I also pass 'details'?
End Sub
The best way is to create your own custom "status" object that contains all of the data you want to pass to whatever is handling the progress change. Then you include that in your call as part of the userState object.
Public Class MyObjectState
Public Message As String
Public StatusState As String
Public Whatever as Integer
Public Whosit as Decimal
End Class
...
Dim currentState as New MyObjectState()
currentState.Message = "blah blah blah"
bw.ReportProgress(progress, someMyObjectStateInstance)
...
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e as ProgressChangedEventArgs)
Me.proProgress.Value = e.ProgressPercentage
Dim state as MyObjectState = CType(e.UserState, MyObjectState)
'You can now use the state object with any of the instance information you need
'At this point message would be "blah blah blah"
End Sub
This basically works like a "snapshot", so I would make your state object as detailed as possible, but only transfer what you need.
The ReportProgress event has an overload that accepts two parameters.
The first one is the Percentage that you get in the ProgressPercentage property.
The second one is an object that you get from the ObjectState property
So you can create a miniclass like this
Public Class bwObjectState
Public String Details
End Class
Then when you call ReportProgress
Dim data As new bwObjectState()
data.Details = "Textbox details will go here"
progress = (100 / (UBound(servers) + 1)) * (i + 1)
bw.ReportProgress(progress, data)
The class object is useful if you plan to pass other values, but if you need to pass just a string you could simply assign the string to the second parameter
bw.ReportProgress(progress, "Textbox details will go here")
Of course, in the ProgressChanged event, you need to cast the ObjectState object back to its original type
Dim data as bwObjectState = CType(e.UserState, bwObjectState)
or simply
Dim details as String = CType(e.UserState, String)