1
Public Class Form1

    Dim controlNames() As String = {"Tea", "Cola", "Coffee", "Orange", "Water", "VanillaCone", "VanillaShake", "StrawberryShake", "ChocolateMilkshake", "Fries", "Salad", "Hamburger", "OnionRings", "ChickenSalad", "FishSandwich", "CheeseSandwich", "ChickenSandwich"}


    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load

        For Each ctrl As Control In Me.Panel2.Controls

            If TypeOf ctrl Is TextBox Then
                    ctrl.Enabled = False
                End If

        Next

    End Sub

    Private Sub Timer1_Tick(sender As Object, e As EventArgs)
        For i = 0 To 16
            For Each ctrl As Control In Me.Panel2.Controls
                If TypeOf ctrl Is CheckBox Then
                    If ctrl.Name = "chk" & controlNames(i) Then
                        If DirectCast(ctrl, CheckBox).CheckState = CheckState.Checked Then
                            If TypeOf ctrl Is TextBox Then
                                If ctrl.Name = "txt" & controlNames(i) Then
                                    ctrl.Enabled = True
                                End If
                            End If
                        End If
                    End If
                End If
            Next
        Next
    End Sub

I have a VB.NET assignment and I am attempting to enable textboxes based on whether or not the checkbox with the same name was checked. This is my code so far and it obviously doesn't work. What I want to do essentially:

All textboxes start as disabled. Then, textboxes only get enabled if the corresponding checkboxes are checked. For example, if chkTea is checked, then it enables txtTea.

I am aware I can copy paste something like "if chkTea = checked then txt.tea = enabled". I do not want to do this as this seems like a poor way to go about this. I want to know if I can do something like my barely readable code shows.

David Wilson
  • 4,369
  • 3
  • 18
  • 31
  • I think the problem lies in the nested IF statements, you will only look for ctrl type Textbox IF the ctrl type is a CheckBox – Benno Feb 05 '18 at 11:36
  • That's what I want to do...I think... I want to check each checkbox whether it's ticked or not, and if it is ticked, I want to enable the textbox that has the same name. – Lorem Ipsum Feb 05 '18 at 11:38
  • After the line with DirectCast ctrl will still contain the CheckBox - not the TextBox. So you still need to find the corresponding TextBox. – Benno Feb 05 '18 at 11:45
  • So how else can I do it? Would you be able to give me some pseudo code as to what a better approach to my problem would be? – Lorem Ipsum Feb 05 '18 at 11:49
  • 1
    Why are you not using the `CheckBox.CheckChanged` event? – Sasha Feb 05 '18 at 11:51
  • I assume I'd have to do it for every single checkbox separately, whereas I want to use corresponding names of checkboxes and textboxes. What I don't want is: if chkTea = checked then txtTea = enabled if chkCoffee = checked then txtCoffee.enabled. I want to compare a checkboxes name, with a textbox, and if it is checked then the textbox should be enabled. – Lorem Ipsum Feb 05 '18 at 11:52
  • Actually you don't need a separate event handler for each `CheckBox`, you can have one handler that handles _**all**_ `CheckedChanged` events. See this answer of mine: https://stackoverflow.com/a/48248889/3740093 -- The code uses text boxes, labels, and the `GotFocus` event, otherwise it's exactly the same as your question. – Visual Vincent Feb 05 '18 at 13:50
  • Possible duplicate of [Shorten code vb.net](https://stackoverflow.com/questions/48248680/shorten-code-vb-net) – Visual Vincent Feb 05 '18 at 13:50
  • @VisualVincent I'm not sure that the op wants to specifically shorten the code, but wants to get it to work :-) – David Wilson Feb 05 '18 at 14:39
  • @ViualVincent that doesn't seem to target the corresponding control. That just seems to target all of them. I don't want one checkbox to enable ALL textboxes, I want a specific checkbox to only enable a specific textbox. – Lorem Ipsum Feb 05 '18 at 14:58
  • Also, with your answer before, adapting it for my code gives me the error "Value of type 'Control()' cannot be converted to 'Control.ControlCollection'." – Lorem Ipsum Feb 05 '18 at 15:06
  • If an answer sorts out your problem, don't forget to click the tick next to the answer - It makes sure that the user gets reputation points and makes it easier for anyone else who has the same problem to see that your question has an accepted answer that might work for them as well. (And the person answering the question gets reputation points btw) – David Wilson Feb 05 '18 at 16:16
  • @DavidWilson : Don't focus on the title, but my answer. I VTC'd as a dupe since it's basically the same, and my answer applies to this question as well. – Visual Vincent Feb 05 '18 at 18:44
  • @Lorem : No, both the question and my answer focuses on a **pair** of one label and one textbox (or in your case one checkbox and one textbox). **EDIT:** Fixed the error in my answer. Thanks for the heads up! – Visual Vincent Feb 05 '18 at 18:45
  • @Lorem : The way my answer works is by getting the name of the current control (say `chkTea`), removes the `chk` part, replaces it with `txt` and gets the control of the new name (`txtTea`). You just need to modify the `NamePrefix` and `NewPrefix` variables, and also change `Control(0).Visible = True` to `Control(0).Enabled = DirectCast(sender, CheckBox).Checked`. – Visual Vincent Feb 05 '18 at 18:52

2 Answers2

3

If you're renaming all your controls to something different to the default name with a number, this code should work just fine.

You dont need a timer, it just fires when any of the CheckBoxes have their state changed.

In the example code, I've created a CheckedChanged handler and set it to handle some of the CheckBoxes (You'll want to add all those you want to handle). If you click on any of these CheckBoxes, the handler will fire. The handler then passes which checkbox has been changed to the SyncTextBoxWithCheckBoxState method. This then finds the matching textbox using the FindMatchingCheckBox method and sets the .Enabled state of the Text box to the same as the .Checked state of the CheckBox

Private Sub chkTea_CheckedChanged(sender As Object, e As EventArgs) Handles chkTea.CheckedChanged, chkCoffee.CheckedChanged, chkCola.CheckedChanged, chkOrange.CheckedChanged, chkTea.CheckedChanged, chkVanillaCone.CheckedChanged, chkVanillaCone.CheckedChanged, chkWater.CheckedChanged
    SyncTextBoxWithCheckBoxState(CType(sender, CheckBox))
End Sub

Private Sub SyncTextBoxWithCheckBoxState(chkBox As CheckBox)
    Dim txtBox As TextBox = FindMatchingTextBox(chkBox)
    txtBox.Enabled = chkBox.Checked
End Sub

Private Function FindMatchingTextBox(chkbox As CheckBox) As TextBox
    For Each ctrl As Control In Panel2.Controls
        If ctrl.GetType Is GetType(TextBox) And ctrl.Name.Contains(chkbox.Name.Substring(3)) Then
            Return CType(ctrl, TextBox)
        End If
    Next
    Return Nothing
End Function

EDIT

To have the code target more than one panel, add all the checkboxes you want to detect to the event handler like before, and in the FindMatchingTextBox method, just add another loop around the existing one to loop through each panel. Like so ..

Private Function FindMatchingTextBox(chkbox As CheckBox) As TextBox
    'This is the new loop which loops through the two panels. It's
    'a bit quick and dirty, but it works
    For Each pnl As Panel In New Panel() {Panel2, Panel3}
    'In this line note that the reference to Panel2 now refers to pnl
        For Each ctrl As Control In pnl.Controls
            If ctrl.GetType Is GetType(TextBox) And ctrl.Name.Contains(chkbox.Name.Substring(3)) Then
                Return CType(ctrl, TextBox)
            End If
        Next
    'End point of the new loop
    Next
    Return Nothing
End Function
David Wilson
  • 4,369
  • 3
  • 18
  • 31
  • Thank you! This works pretty much perfectly. One last issue however, my controls are split between Panel2 and Panel3, how do I target them both at once? I tried separate loops but that causes crashes. – Lorem Ipsum Feb 05 '18 at 15:24
  • 1
    @LoremIpsum - see my updated answer - it works for me, but depending on how your code and panels are set, you might need to tweak it. – David Wilson Feb 05 '18 at 16:01
  • 1
    As two minor optimizations I'd like to suggest you switch from `CType` to `DirectCast` and from [`And` to `AndAlso`](https://stackoverflow.com/q/8409467), since they're faster. Otherwise a nice answer with a good explanation! +1 – Visual Vincent Feb 05 '18 at 19:00
0

After you've looped through each checkbox, you are only concerned with that checkbox, so when you find the correct checkbox, you need to then RELOOP back through all the controls on the form checking if they are the corresponding textbox.

Something like the below should work, or at the very least start you off down the right path:

Dim controlNames() As String = {"Tea", "Cola", "Coffee", "Orange", "Water", "VanillaCone", "VanillaShake", "StrawberryShake", "ChocolateMilkshake", "Fries", "Salad", "Hamburger", "OnionRings", "ChickenSalad", "FishSandwich", "CheeseSandwich", "ChickenSandwich"}


Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load

For Each ctrl As Control In Me.Panel2.Controls

    If TypeOf ctrl Is TextBox Then
            ctrl.Enabled = False
        End If

Next

End Sub

Private Sub Timer1_Tick(sender As Object, e As EventArgs)
For i = 0 To 16
    For Each ctrl As Control In Me.Panel2.Controls
        If TypeOf ctrl Is CheckBox Then
            If ctrl.Name = "chk" & controlNames(i) Then
                If DirectCast(ctrl, CheckBox).CheckState = CheckState.Checked Then
    For Each ctrl As Control In Me.Panel2.Controls
                    If TypeOf ctrl Is TextBox Then
                        If ctrl.Name = "txt" & controlNames(i) Then
                            ctrl.Enabled = True
                        End If
                    End If
Next
                End If
            End If
        End If
    Next
Next
End Sub
RussAwesome
  • 464
  • 5
  • 18