Hey everyone,
I am making a program that requires the user to enter values and select the correct radio button.
It freezes when I click Ok button and I am not sure why, I think I am not using the Do Until...Loop properly.
Ok Button code:
Code:
Public Sub btnOk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOk.Click
Call mainfunction()
End Sub
I also attached a jpeg of the program to give you guys and idea of it.
It's stuck in a loop because it will continually loop until something changes the selected box which it can't because the program is too busy stuck in a logic loop. You need to restructure the code and handle the CheckedChanged events.
I am afraid you got into an infinite loop during the main function.
The following attributes/members/variables never become true in the do loop itself:
No, I want the program to be able to switch from metric to imperial at anytime after the user has pressed Ok. The user will not be clicking ok everytime so i thought I would need a loop somewhere in there that would bring me back to the point where it waits for the user to either change the units or hit reset or hit ok and start over.
The only solution I see to this is to make the program wait for the user to either switch units or location before proceeding (how do I code that?), the problem is I would have to keep writing the same code over and over because theoretically the user has to be able to switch back in forth between units for an unlimited amount of times.
Here is the revised code without the loops...:
Code:
Public Sub mainfunction()
If optMetric.Checked And optBelowGround.Checked Then
Call choosepipecomboboxcoding(cboChoosePipe,
HDPE,
PVC,
ABS,
FRP,
Copper,
Steel,
Ductileiron)
choosecasingcomboboxcoding(cboChooseCasingPipe,
HDPE,
GalvanizedSteel,
Aluminum,
PVC,
Steel)
txtSupplyTemp.Text = SupplyTemp.ToString
txtSoilTemp.Text = SoilTemp.ToString
txtCarrierPipeWallThickness.Text = CarrierPipeWallThickness.ToString
txtInsulationThickness.Text = InsulationThickness.ToString
txtJacketThickness.Text = JacketThickness.ToString
txtExternalDiam.Text = Externaldiameterofcarrierpipe.ToString
txtLayingdepth.Text = LayingDepthtoTopofPipe.ToString
txtTotalRunofPipe.Text = TotalLengthofPipeRun.ToString
txtFlowRate.Text = FlowRate.ToString
txtTemptoreach.Text = Temperaturetoreach.ToString
txtSafetyFactor.Text = SafetyFactor.ToString
ImperialSoilTemp = ConvertSoilTempToImperial(SoilTemp)
ImperialSupplyTemp = ConvertSupplyTempToImperial(SupplyTemp)
ImperialCarrierPipeWallThickness = ConvertCarrierPipeWallThicknessToImperial(CarrierPipeWallThickness)
ImperialExternaldiameterofcarrierpipe = ConvertExternalDiameterofCarrierPIpe(Externaldiameterofcarrierpipe)
FlowRateImperial = ConvertFlowRatetoImperial(FlowRate)
ImperialJacketThickness = ConvertJacketThicknessToImperial(JacketThickness)
ImperialLayingDepthtoTopofPipe = ConvertLayingDepthToTopofPipeToImperial(LayingDepthtoTopofPipe)
ImperialInsulationThickness = ConvertInsulationThicknessToImperial(InsulationThickness)
ImperialTotalLengthofPipeRun = ConvertTotalLengthofPipeRunToImperial(TotalLengthofPipeRun)
ImperialTemperaturetoreach = ConvertTemperaturetoReachImperial(Temperaturetoreach)
InternalDiamCarrierPipeCalculation(Externaldiameterofcarrierpipe, CarrierPipeWallThickness)
internaldiamofcasingpipecalculation(Externaldiameterofcarrierpipe, InsulationThickness)
externaldiamofcasingpipe(InternalDiameterofCasingPipe, JacketThickness)
layingdepthtocenterlineofpipecal(LayingDepthtoTopofPipe, ExternalDiameterofCasingPipe)
crosssectionalareaofpipecal(Externaldiameterofcarrierpipe, CarrierPipeWallThickness)
thermalresistanceofinsulationcal(ThermalConductivityofInsulation, InternalDiameterofCasingPipe, Externaldiameterofcarrierpipe)
thermalresistanceofthecarrierpipecal(ThermalConductivityofCarrierPipe, Externaldiameterofcarrierpipe, InternalDiameterofCarrierPipe)
thermalresistanceofthejacketpipecal(ThermalConductivityofCasingPipe, ExternalDiameterofCasingPipe, InternalDiameterofCasingPipe)
thermalresistanceofsoilcal(ThermalConductivityofSoil, LayingDepthToCenterlineOfPipe, ExternalDiameterofCasingPipe)
timetoreachtempcal(Temperaturetoreach, SoilTemp, SupplyTemp, InternalDiameterofCarrierPipe, ThermalResistanceofTheInsulation, ThermalResistanceOfTheCarrierPipe,
ThermalResistanceofTheJacketPipe, ThermalResistanceOfTheSoil)
totalheatgainfromsoilonpiperuncal(TotalLengthofPipeRun, HeatGain)
increaseinsupplytempatoutletcal(TotalHeatGainFromSoilonPipeRun, FlowRateImperial)
Call HeatTransmissionCoefficientforAsinglePipeCalculation(ThermalResistanceofTheInsulation, ThermalResistanceOfTheCarrierPipe, ThermalResistanceofTheJacketPipe,
ThermalResistanceOfTheSoil)
Call HeatGainCalculation(HeatTransmissionCoefficientforASinglePipe,
SoilTemp,
SupplyTemp)
Call VelocityCalculation(FlowRate, Externaldiameterofcarrierpipe, CarrierPipeWallThickness)
TimeToFreezeCompletelyCalculation(SoilTemp)
HeatTraceRequirementsCal(SoilTemp, SupplyTemp, SafetyFactor)
'WAIT UNTIL USER SWITCHES UNITS OR LOCATION
If optImperial.Checked And optBelowGround.Checked Then
' If user changes units but does not change location
txtSupplyTemp.Text = RoundedImperialSupplyTemp.ToString
txtSoilTemp.Text = RoundedImperialSoiltemp.ToString
txtCarrierPipeWallThickness.Text = RoundedImperialcarrierpipewallthickness.ToString
txtInsulationThickness.Text = roundedimperialInsulationthickness.ToString
txtJacketThickness.Text = roundedImperialJacketthickness.ToString
txtExternalDiam.Text = roundedimperialexternaldiameterofcarrierpipe.ToString
txtLayingdepth.Text = roundedimperiallayingdepthtotopofpipe.ToString
txtTotalRunofPipe.Text = RoundedImperialTotalLengthofPipeRun.ToString
txtFlowRate.Text = Roundedflowrateimperial.ToString
txtTemptoreach.Text = RoundedImperialtemperaturetoreach.ToString
'WAIT UNTIL USER SWITCHES UNITS OR LOCATION
If optMetric.Checked And optBelowGround.Checked Then
' If user changes units but not the location
txtSupplyTemp.Text = SupplyTemp.ToString
txtSoilTemp.Text = SoilTemp.ToString
txtCarrierPipeWallThickness.Text = CarrierPipeWallThickness.ToString
txtInsulationThickness.Text = InsulationThickness.ToString
txtJacketThickness.Text = JacketThickness.ToString
txtExternalDiam.Text = Externaldiameterofcarrierpipe.ToString
txtLayingdepth.Text = LayingDepthtoTopofPipe.ToString
txtTotalRunofPipe.Text = TotalLengthofPipeRun.ToString
txtFlowRate.Text = FlowRate.ToString
txtTemptoreach.Text = Temperaturetoreach.ToString
txtSafetyFactor.Text = SafetyFactor.ToString
End If
ElseIf optImperial.Checked And optAboveGround.Checked Then
MessageBox.Show("Please Reset Values Before Changing Location")
End If
It's stuck in a loop because it will continually loop until something changes the selected box which it can't because the program is too busy stuck in a logic loop. You need to restructure the code and handle the CheckedChanged events.
I had previously tried to insert the code for changing the textboxes in the CheckedChanged event but the problem is, if the user initially enters his values in Metric and switches to imperial, the rounded imperial value needs to be displayed. If the user initially enters his values in imperial, when he changes to metric, the rounded metric value needs to be displayed. So when the user then switches back to the initial units, the values that need to be displayed are not the rounded ones but the initially entered ones. So here is the question: How can i code these CheckedChanged events to know in what units the values where initially entered in...?
Well, you want to handle changes of metric etc., so you will have to handle the events of these controls. Use the following code + sub:
Code:
Public Sub mainfunction()
If optMetric.Checked And optBelowGround.Checked Then
choosepipecomboboxcoding(cboChoosePipe, HDPE, PVC, ABS, FRP, Copper, Steel, Ductileiron)
choosecasingcomboboxcoding(cboChooseCasingPipe, HDPE, GalvanizedSteel, Aluminum, PVC, Steel)
txtSupplyTemp.Text = SupplyTemp.ToString
txtSoilTemp.Text = SoilTemp.ToString
txtCarrierPipeWallThickness.Text = CarrierPipeWallThickness.ToString
txtInsulationThickness.Text = InsulationThickness.ToString
txtJacketThickness.Text = JacketThickness.ToString
txtExternalDiam.Text = Externaldiameterofcarrierpipe.ToString
txtLayingdepth.Text = LayingDepthtoTopofPipe.ToString
txtTotalRunofPipe.Text = TotalLengthofPipeRun.ToString
txtFlowRate.Text = FlowRate.ToString
txtTemptoreach.Text = Temperaturetoreach.ToString
txtSafetyFactor.Text = SafetyFactor.ToString
ImperialSoilTemp = ConvertSoilTempToImperial(SoilTemp)
ImperialSupplyTemp = ConvertSupplyTempToImperial(SupplyTemp)
ImperialCarrierPipeWallThickness = ConvertCarrierPipeWallThicknessToImperial(CarrierPipeWallThickness)
ImperialExternaldiameterofcarrierpipe = ConvertExternalDiameterofCarrierPIpe(Externaldiameterofcarrierpipe)
FlowRateImperial = ConvertFlowRatetoImperial(FlowRate)
ImperialJacketThickness = ConvertJacketThicknessToImperial(JacketThickness)
ImperialLayingDepthtoTopofPipe = ConvertLayingDepthToTopofPipeToImperial(LayingDepthtoTopofPipe)
ImperialInsulationThickness = ConvertInsulationThicknessToImperial(InsulationThickness)
ImperialTotalLengthofPipeRun = ConvertTotalLengthofPipeRunToImperial(TotalLengthofPipeRun)
ImperialTemperaturetoreach = ConvertTemperaturetoReachImperial(Temperaturetoreach)
InternalDiamCarrierPipeCalculation(Externaldiameterofcarrierpipe, CarrierPipeWallThickness)
internaldiamofcasingpipecalculation(Externaldiameterofcarrierpipe, InsulationThickness)
externaldiamofcasingpipe(InternalDiameterofCasingPipe, JacketThickness)
layingdepthtocenterlineofpipecal(LayingDepthtoTopofPipe, ExternalDiameterofCasingPipe)
crosssectionalareaofpipecal(Externaldiameterofcarrierpipe, CarrierPipeWallThickness)
thermalresistanceofinsulationcal(ThermalConductivityofInsulation, InternalDiameterofCasingPipe, Externaldiameterofcarrierpipe)
thermalresistanceofthecarrierpipecal(ThermalConductivityofCarrierPipe, Externaldiameterofcarrierpipe, InternalDiameterofCarrierPipe)
thermalresistanceofthejacketpipecal(ThermalConductivityofCasingPipe, ExternalDiameterofCasingPipe, InternalDiameterofCasingPipe)
thermalresistanceofsoilcal(ThermalConductivityofSoil, LayingDepthToCenterlineOfPipe, ExternalDiameterofCasingPipe)
timetoreachtempcal(Temperaturetoreach, SoilTemp, SupplyTemp, InternalDiameterofCarrierPipe, ThermalResistanceofTheInsulation, ThermalResistanceOfTheCarrierPipe, ThermalResistanceofTheJacketPipe, ThermalResistanceOfTheSoil)
totalheatgainfromsoilonpiperuncal(TotalLengthofPipeRun, HeatGain)
increaseinsupplytempatoutletcal(TotalHeatGainFromSoilonPipeRun, FlowRateImperial)
Call HeatTransmissionCoefficientforAsinglePipeCalculation(ThermalResistanceofTheInsulation, ThermalResistanceOfTheCarrierPipe, ThermalResistanceofTheJacketPipe, ThermalResistanceOfTheSoil)
HeatGainCalculation(HeatTransmissionCoefficientforASinglePipe, SoilTemp, SupplyTemp)
VelocityCalculation(FlowRate, Externaldiameterofcarrierpipe, CarrierPipeWallThickness)
TimeToFreezeCompletelyCalculation(SoilTemp)
HeatTraceRequirementsCal(SoilTemp, SupplyTemp, SafetyFactor)
'WAIT UNTIL USER SWITCHES UNITS OR LOCATION
If optImperial.Checked And optBelowGround.Checked Then
' If user changes units but does not change location
txtSupplyTemp.Text = RoundedImperialSupplyTemp.ToString
txtSoilTemp.Text = RoundedImperialSoiltemp.ToString
txtCarrierPipeWallThickness.Text = RoundedImperialcarrierpipewallthickness.ToString
txtInsulationThickness.Text = roundedimperialInsulationthickness.ToString
txtJacketThickness.Text = roundedImperialJacketthickness.ToString
txtExternalDiam.Text = roundedimperialexternaldiameterofcarrierpipe.ToString
txtLayingdepth.Text = roundedimperiallayingdepthtotopofpipe.ToString
txtTotalRunofPipe.Text = RoundedImperialTotalLengthofPipeRun.ToString
txtFlowRate.Text = Roundedflowrateimperial.ToString
txtTemptoreach.Text = RoundedImperialtemperaturetoreach.ToString
'WAIT UNTIL USER SWITCHES UNITS OR LOCATION
If optMetric.Checked And optBelowGround.Checked Then
' If user changes units but not the location
txtSupplyTemp.Text = SupplyTemp.ToString
txtSoilTemp.Text = SoilTemp.ToString
txtCarrierPipeWallThickness.Text = CarrierPipeWallThickness.ToString
txtInsulationThickness.Text = InsulationThickness.ToString
txtJacketThickness.Text = JacketThickness.ToString
txtExternalDiam.Text = Externaldiameterofcarrierpipe.ToString
txtLayingdepth.Text = LayingDepthtoTopofPipe.ToString
txtTotalRunofPipe.Text = TotalLengthofPipeRun.ToString
txtFlowRate.Text = FlowRate.ToString
txtTemptoreach.Text = Temperaturetoreach.ToString
txtSafetyFactor.Text = SafetyFactor.ToString
End If
ElseIf optImperial.Checked And optAboveGround.Checked Then
MessageBox.Show("Please Reset Values Before Changing Location")
ElseIf optImperial.Checked And optBelowGround.Checked Then
ElseIf optMetric.Checked And optAboveGround.Checked Then
ElseIf optImperial.Checked And optAboveGround.Checked Then
End If
End Sub
Now you call "MainFunction" on all eventhandlers of the controls where you need to update the values:
Code:
Private Sub ControlChanged() Handles optMetric.CheckedChanged, optBelowGround.CheckedChanged, optImperial.CheckedChanged, UpdateButton.Click
mainfunction()
End Sub
I do not think a loop is suitable for this sort of thing.
EDIT
Code is a bit extremely long for me to handle in NotePad, but the main idea I try to show here is not using loops, but updating all controls in one sub. You call this sub every time a state changes, for example, when the checkboxes change state. And: "Call <funcname()>" is the same as "<funcname()>", so best is to keep your code clean and not use Call.
Your code is pretty long, I recommend dividing your function in two separate functions: one for imperial and one for metric.
EDIT2
Ok went through your code again, and I see no reason to use any loops, only "If-statements".
You go through it as if it is a list of requirements, so:
Code:
Private Sub mainfunction()
If optMetric.Checked Then
If optAboveGround.Checked Then
'Coding for metric above ground
Else
'Coding for metric below ground
End If
Else
If optAboveGround.Checked Then
'Coding for imperial above ground
Else
'Coding for imperial below ground
End If
End If
End Sub
Last edited by bergerkiller; Apr 16th, 2011 at 08:37 AM.
Thanks B.Killer!! That just cleared up the coding so much!
Code:
Private Sub ControlChanged() Handles optMetric.CheckedChanged, optBelowGround.CheckedChanged, optImperial.CheckedChanged, UpdateButton.Click
mainfunction()
End Sub
With it re-arranged I dont even have to have a wait function! Can I use this code for comboboxes and text boxes? This way the Results will be able to always update themselves without having to click an update button??
Sure can, look for "TextChanged", "SelectedIndexChanged", "CheckedChanged" and "Click" events, in visual studio you can make handlers by double clicking on the control in question.
Ok but now I am stuck with the same problem at the beginning.
Because for example if the user initially enters his values in metric and then switches to imperial. Because of the ControlChanged() the main function will run again but this time it will be using the entered metric values thinking they are imperial values and all my results go wrong...
I would have to put some coding in the radiobutton.CheckChanged event to do this properly without having to run the main function over and over again? This also means that I would have to find a way to code the radiobuttons to know which unit was initially selected...
If not the only other option I can think of is inserting a "wait" function at the end of the main function
Code:
'WAIT UNTIL USER SWITCHES UNITS OR LOCATION
If optImperial.Checked And optBelowGround.Checked Then
' If user changes units but does not change location
txtSupplyTemp.Text = RoundedImperialSupplyTemp.ToString
txtSoilTemp.Text = RoundedImperialSoiltemp.ToString
txtCarrierPipeWallThickness.Text = RoundedImperialcarrierpipewallthickness.ToString
txtInsulationThickness.Text = roundedimperialInsulationthickness.ToString
txtJacketThickness.Text = roundedImperialJacketthickness.ToString
txtExternalDiam.Text = roundedimperialexternaldiameterofcarrierpipe.ToString
txtLayingdepth.Text = roundedimperiallayingdepthtotopofpipe.ToString
txtTotalRunofPipe.Text = RoundedImperialTotalLengthofPipeRun.ToString
txtFlowRate.Text = Roundedflowrateimperial.ToString
txtTemptoreach.Text = RoundedImperialtemperaturetoreach.ToString
'WAIT UNTIL USER SWITCHES UNITS OR LOCATION
If optMetric.Checked And optBelowGround.Checked Then
' If user changes units but not the location
txtSupplyTemp.Text = SupplyTemp.ToString
txtSoilTemp.Text = SoilTemp.ToString
txtCarrierPipeWallThickness.Text = CarrierPipeWallThickness.ToString
txtInsulationThickness.Text = InsulationThickness.ToString
txtJacketThickness.Text = JacketThickness.ToString
txtExternalDiam.Text = Externaldiameterofcarrierpipe.ToString
txtLayingdepth.Text = LayingDepthtoTopofPipe.ToString
txtTotalRunofPipe.Text = TotalLengthofPipeRun.ToString
txtFlowRate.Text = FlowRate.ToString
txtTemptoreach.Text = Temperaturetoreach.ToString
txtSafetyFactor.Text = SafetyFactor.ToString
End If
But this method limits the amount of times the user can switch back and forth...
I really REALLY recommend not using all those variables. You can just make your functions return a value, instead of storing all these variables.
For example:
Code:
Public Sub MainFunc()
Dim calculatedvalue As Double = CalculateValue(Argument1, 100)
End Sub
Public Function CalculateValue(ByVal val1 As Double, ByVal val2 As Double) As Double
Return val1 * 1000 + val2
End Function
And use the result of this function in the next functions. You should not make functions that change a value, it is a bit useless.
Some basic tip you should follow with the large amount of values you have:
Make three lists on a piece of paper:
- input values to process to the left
- values to make but not output in the middle
- output values to return to the right
And make a diagram of what should happen. You can make multiple pieces of paper, for example, you make one paper called "Metric" and you connect the values based on a metric calculation.
It helps having some sort of "plan".
Once done you make an input and output structure and ONE SINGLE FUNCTION in your form that handles this. Example:
Code:
Public Structure InputStruc
Public SupplyTemp As Double
Public SoilTemp As Double
Public CarrierPipeWallThickness As Double
Public InsulationThickness As Double
Public JacketThickness As Double
Public ExternalDiam As Double
Public Layingdepth As Double
Public TotalRunofPipe As Double
Public FlowRate As Double
Public Temptoreach As Double
Public SafetyFactor As Double
End Structure
Public Structure OutputStruc
Public HeatGain As Double
Public Velocity As Double
Public TimeToTemperature As Double
Public Tempincrease As Double
Public TotalHeatGainSoilPipeRun As Double
Public FreezeTime As Double
Public HeatTraceReq As Double
End Structure
Public Function MetricAboveCalculation(ByVal input As InputStruc) As OutputStruc
Dim output As New OutputStruc
output.HeatGain = input.FlowRate / (input.JacketThickness * 1000)
'more input -> output calculations here
Return output
End Function
Code:
Public Sub MainFunction()
'generate input structure
Dim input As New InputStruc
Dim output As OutputStruc
input.CarrierPipeWallThickness = txtCarrierPipeWallThickness.Text 'will need conversion here
'do the correct function based on what checkbox/radiobutton is checked
If optMetric.Checked Then
If optAbove.Checked Then
output = MetricAboveCalculation(input)
Else
End If
Else
If optAbove.Checked Then
Else
End If
End If
'assign output to the text fields
txtHeatGain.Text = output.HeatGain
End Sub
Last edited by bergerkiller; Apr 17th, 2011 at 04:04 PM.
Really I think the easiest way would be to determine what units where initially selected and code in the radio button:
If metric was initially selected and optImperial.Checked then
Display RoundedImperialValue
else if imperial was initially selected and optImperial.Checked then
Display Imperial value
Else if imperial was initially selected and optMetric.Checked then
Display RoundedMetricValue
Elseif Imperial was initially selected and optImperial.Checked then
Display MetricValues
Endif
I know the syntax is not ok there but its just to give you an idea of what I am trying to do.
EDIT1:
Sorry didint see what you had just posted, And yes I realise that I should have structured the whole thing alittle better... This is my first VB program and its some special project my boss put me on so I am learning at the same time. But really everything else in the program is working great, only the radio button and conversions that are giving me a headache.
I will try to re-structure the whole thing but do you think that this would work ? :
If metric was initially selected and optImperial.Checked then
Display RoundedImperialValue
else if imperial was initially selected and optImperial.Checked then
Display Imperial value
Else if imperial was initially selected and optMetric.Checked then
Display RoundedMetricValue
Elseif Imperial was initially selected and optImperial.Checked then
Display MetricValues
Endif
Thanks again for ure time
Last edited by programmingpat; Apr 17th, 2011 at 04:04 PM.
Of course it works, but the issue there is that you have 4x the same thing which is always useless: you are setting the returned values to the textfields 4x. I do not think different return value NAMES are returned, and that every field must be filled, so best is to do it in the order I posted above:
1. Fill input variables
2. Calculate output variables based on settings
3. Fill fields with output variables
The shorter the easier you can spot bugs. Plus, if you have to change the output real quick you would have to change it four times. By running this same code one time in the end you can change it more easily.
Another important thing: keep a backbone. Hold the output structure (and maybe the input structure) in a global variable so you can easily access it from the outside; like when you have to save them.
If you have to set the input textboxes based on the input structure, then make a separate subroutine for that with as input argument the input structure. (the reverse of setting the structure from the text fields)
Last edited by bergerkiller; Apr 17th, 2011 at 04:12 PM.
Ok I will do this, but also the calculations are always done in metric there are no calculations for imperial thats why I did not do two separate function. So when the user checks imperial and enters his values the function converts those values to metric and perform the calculations and displays them in the required unit.
Anywais I will try to re-structure it and I will keep you updated to make sure I dont go completely off track
Also I would like to know how to code in the radio button to know which radio button was initially selected. Im guessing it will be something similar to optMetric.Checked... (optMetric.initiallyChecked or something like that??)
I made a flowchart in a word doc. It needs to be revised of course this was just a quick thing I made at the begining of the project, at the time I was thinking about loops.