dcsimg
Results 1 to 15 of 15

Thread: Passing Controls to a Routine

  1. #1

    Thread Starter
    New Member
    Join Date
    Aug 2006
    Posts
    14

    Passing Controls to a Routine

    Hi all,

    Interestingly, I just visited the forums having a very similar issue.

    I have a bunch of checkmark and textbox controls in a form (over 30 of each) which have the following default states:
    Checkmark: unchecked; textbox: disabled.

    Now, each time the checkmark changes value, the code verifies that and enables the textbox accordingly. So far, so good. I have wrote a line in each sub dealing with that. However, I would like to automate the behavior by having a separate sub usable for any control pair.

    Original code for each control pair is:

    Code:
    Private Sub CkCudaSchedule_CheckedChanged(sender As Object, e As EventArgs) Handles CkCudaSchedule.CheckedChanged
            If CkCudaSchedule.CheckState = CheckState.Checked Then txtCudaSchedule.Enabled = True Else txtCudaSchedule.Enabled = False
        End Sub
    I want to replace this with a sub reference like this:

    Code:
    Private Sub CkCudaSchedule_CheckedChanged(sender As Object, e As EventArgs) Handles CkCudaSchedule.CheckedChanged
            checkenable("CudaSchedule")
        End Sub
    So I have built a sub having this code:

    Code:
    Private Sub checkenable(name As String)
            Dim ck As String, txt As String
            Dim ckobj As CheckBox
            Dim txtobj As TextBox
            ck = name
            txt = name
            With ckobj
                .Name = "Ck" & ck
            End With
            With txtobj
                .Name = "txt" & txt
            End With
            If ckobj.CheckState = CheckState.Checked Then txtobj.Enabled = True Else txtobj.Enabled = False
        End Sub
    Needless to say it throws an exception when called:

    System.NullReferenceException
    HResult=0x80004003
    Message=Object reference not set to an instance of an object.
    Source=CoalWorks AIO
    StackTrace:
    at WindowsApp1.FrmAdvancedConfiguration.checkenable(String name) in W:\WaR\Work\Programming\Visual Basic\CoalWorks AIO\CoalWorks AIO\Form2.vb:line 10
    at WindowsApp1.FrmAdvancedConfiguration.ckStaleJobs_CheckedChanged(Object sender, EventArgs e) in W:\WaR\Work\Programming\Visual Basic\CoalWorks AIO\CoalWorks AIO\Form2.vb:line 107
    at System.Windows.Forms.CheckBox.OnCheckedChanged(EventArgs e)
    at System.Windows.Forms.CheckBox.set_CheckState(CheckState value)
    at System.Windows.Forms.CheckBox.OnClick(EventArgs e)
    at System.Windows.Forms.CheckBox.OnMouseUp(MouseEventArgs mevent)
    at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
    at System.Windows.Forms.Control.WndProc(Message& m)
    at System.Windows.Forms.ButtonBase.WndProc(Message& m)
    at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
    at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
    at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
    at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
    at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoCompo nentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
    at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
    at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
    at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()
    at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()
    at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)
    at WindowsApp1.My.MyApplication.Main(String[] Args) in :line 81
    The exception is:

    System.NullReferenceException: 'Object reference not set to an instance of an object.'

    $W0 was Nothing.
    I'm a beginner, having only used VBA under Excel in the past, and I need some guidance on how to properly create this automated sub. Hopefully I have provided all details, but if need be I can bring more.

    Thanks in advance for your answers!

  2. #2
    Hyperactive Member
    Join Date
    Nov 2017
    Posts
    465

    Re: Passing particular control to subroutine

    There are much more efficient ways of handling what you want to do, but to answer your specific question on passing Controls, try this:

    Code:
    Private Sub CkCudaSchedule_CheckedChanged(sender As Object, e As EventArgs) Handles CkCudaSchedule.CheckedChanged
        checkenable(CkCudaSchedule, txtCudaSchedule)
    End Sub
    With this:

    Code:
    Private Sub checkenable(chkobj as CheckBox, txtobj As TextBox)
        If chkobj.CheckState = CheckState.Checked Then txtobj.Enabled = True Else txtobj.Enabled = False
    End Sub

  3. #3
    Hyperactive Member
    Join Date
    Nov 2017
    Posts
    465

    Re: Passing particular control to subroutine

    Another option, which builds more on your original code of passing part of the control name to the procedure and then accessing the Checkbox and TextBox with that control name suffix (with some superfluous steps consolidated):

    Code:
    Private Sub checkenable(name As String)
        Dim ckobj As CheckBox = CType(Me.Controls("Ck" & name), CheckBox)
        Dim txtobj As TextBox = CType(Me.Controls("txt" & name), TextBox)
        txtobj.Enabled = ckobj.Checked
    End Sub
    Note that I'm pretty sure this works (specifically the Me.Controls part) if the CheckBox and TextBox are directly on your main form and not in a container like a Panel or GroupBox. If, for example, they are in a Panel, I believe you would just change the Me.Controls to Me.PanelName.Controls.
    Last edited by OptionBase1; Dec 25th, 2017 at 08:13 PM.

  4. #4
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    32,755

    Re: Passing Controls to a Routine

    This thread was started in this other thread:

    http://www.vbforums.com/showthread.p...-to-subroutine

    While they are related, the discussions for each situation were diverging, which would get confusing over time, so I split this off to it's own space, as it's a worthwhile discussion all on its own.
    My usual boring signature: Nothing

  5. #5

    Thread Starter
    New Member
    Join Date
    Aug 2006
    Posts
    14

    Re: Passing Controls to a Routine

    @OptionBase1: thank you very much, the second option works very well and is exactly what I needed. Yes, I can add them inside the grouping control if needed, for now it's not the case.
    I have included this in my knowledge repo - will certainly use it in the future and mention you in my code.

  6. #6
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    32,755

    Re: Passing Controls to a Routine

    Another thing you might consider is adding a Dictionary(of Checkbox, Textbox). By doing that, you could have a link between the two controls where the checkbox is the key and the textbox is the value. This would have slightly better performance than going with the name, but you'd never see the difference in speed, so that doesn't really matter. One thing you'd be able to do, then is this:
    Code:
    Private Sub CkCudaSchedule_CheckedChanged(sender As Object, e As EventArgs) Handles CkCudaSchedule.CheckedChanged
        yourDictionary(DirectCast(sender,Checkbox)).Enabled =DirectCast(sender,Checkbox).Checked
    End Sub
    This could be used for all the checkboxes, not just one. The sender argument IS the checkbox, so you can cast it to type Checkbox, and with the checkbox and textbox related via the dictionary, you can use the checkbox to look up the right textbox, and set the enabled state based on the checked state. The DirectCast is faster than CType, though only marginally so. You'd just chain the Handles clauses together for all the checkboxes.

    Of course, this would mean creating the dictionary, which would be done in either the constructor of the Load event. You probably use less code to look up the controls by name, and the performance difference won't matter, but this alternative does have some advantages, so it's good to consider.
    My usual boring signature: Nothing

  7. #7
    Hyperactive Member
    Join Date
    Nov 2017
    Posts
    465

    Re: Passing Controls to a Routine

    Quote Originally Posted by war4peace View Post
    @OptionBase1: thank you very much, the second option works very well and is exactly what I needed. Yes, I can add them inside the grouping control if needed, for now it's not the case.
    I have included this in my knowledge repo - will certainly use it in the future and mention you in my code.
    Since all your checkbox/textbox controls are paired up by name like CkSomething, txtSomething; ckSomething2, txtSomething2, you could take this one step further and have one single CheckedChanged method that handles the CheckedChanged event of all checkboxes. If you're unfamiliar with how that works, at the end of the method where it says

    Code:
    Handles CkCudaSchedule.CheckedChanged
    you just add additional control.eventname separated by commas, like this:

    Code:
    Handles CkCudaSchedule.CheckedChanged, CkOtherCheckbox.CheckedChanged, CkYetAnotherCheckBox.Checkchanged
    Ok, so now you have one single method that fires when any of those checkboxes is checked, how does that help? Well, you're trying to get the name of the Checkbox minus the "Ck" prefix into a string. So you do this inside that Checkbox_CheckedChanged function:

    Code:
    Dim ckTempCheckBox As CheckBox
    Dim strControlSuffix As String
    
    ckTempCheckBox = CType(sender, CheckBox)  'Convert the sender object to a CheckBox object that we can work with
    'Below will take a Checkbox Name like "CkCudaSchedule" and return "CudaSchedule"
    strControlSuffix = Strings.Right(ckTempCheckBox.Name, Len(ckTempCheckBox.Name - 2))
    'Pass that string to the CheckEnable method
    Call CheckEnable(strControlSuffix)
    The advantages of this are, you don't need a separate CheckedChanged method for each control pair, and therefore you don't need a separate call to CheckEnable (with a hard-coded control suffix in each) for each control pair, and if you add additional checkbox/textbox pairs, the only code change you need to make is to add the CheckBoxName.CheckedChanged event to the end of the Handles list in the single CheckedChanged method.

  8. #8

    Thread Starter
    New Member
    Join Date
    Aug 2006
    Posts
    14

    Re: Passing Controls to a Routine

    Oh, man, thanks a lot, this helps even more, cleans up code really nicely, the sub code just needed a slight correction due to a parenthesis being in the wrong place, here it is below for reference:

    Code:
    Dim ckTempCheckBox As CheckBox
    Dim strControlSuffix As String
    
    ckTempCheckBox = CType(sender, CheckBox)  'Convert the sender object to a CheckBox object that we can work with
    'Below will take a Checkbox Name like "CkCudaSchedule" and return "CudaSchedule"
    strControlSuffix = Strings.Right(ckTempCheckBox.Name, Len(ckTempCheckBox.Name) - 2)
    'Pass that string to the CheckEnable method
    Call CheckEnable(strControlSuffix)
    If I may ask for one more thing... would there be a way to pass the list of controls as an array?

    For example using "handles ACCheckboxes", where "ACCheckboxes" is an array containing the list of controls and their events. And then all I would need to do is to add data to the array.
    In the end there will be 40+ checkmarks and their attached textboxes, just trying to make their management easier.

  9. #9
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    32,755

    Re: Passing Controls to a Routine

    You can't do that in the Handles clause, but if you had a collection of the controls, you could use AddHandler instead. It would look like this:
    Code:
    For each itm in YourCollectionOfItems
      AddHandler itm.CheckChanged, AddressOf YourCheckChangedHandlerMethod
    Next
    You'd need to perform that loop early on, such as in the constructor or perhaps Form Load (the constructor would be better).
    My usual boring signature: Nothing

  10. #10

    Thread Starter
    New Member
    Join Date
    Aug 2006
    Posts
    14

    Re: Passing Controls to a Routine

    I understand.
    For now I don't think I need a collection of items, but if I feel the need in the future I could use that.
    Thank you again for your excellent responses!

  11. #11
    Frenzied Member ChrisE's Avatar
    Join Date
    Jun 2017
    Location
    Frankfurt
    Posts
    1,432

    Re: Passing Controls to a Routine

    Hi,

    another option..
    this will go threw all checkboxes on the Form and return the CheckState... checked or unchecked
    and add the 'CheckState' to a Listbox

    Code:
     Private Sub myCheckbox(ByRef frm As System.Windows.Forms.Form)
            For Each ctrl As Control In frm.Controls
                If TypeOf ctrl Is CheckBox Then
                    ListBox2.Items.Add(DirectCast(ctrl, CheckBox).CheckState & " " & ctrl.Name.ToString())
                End If
    
            Next
        End Sub
    
        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            Call myCheckbox(Me)
        End Sub
    regards
    Chris
    Last edited by ChrisE; Dec 27th, 2017 at 03:55 AM.
    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.

  12. #12
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    32,755

    Re: Passing Controls to a Routine

    One thing to be aware of is that EVERY control has a Controls collection, and all controls belong to just one Controls collection. If your checkboxes are on the form directly, they'll be in the form Controls collection. If they are on a panel, in a groupbox, on a tab page, or in any other control, they'll be in the Controls collection for that control, and won't be in the controls collection of the form. This can cause people trouble if they have controls both on the form and in a groupbox (which doesn't even look like a control), or on a panel, as it means they can't iterate through just one controls collection to get all their controls.
    My usual boring signature: Nothing

  13. #13
    Frenzied Member ChrisE's Avatar
    Join Date
    Jun 2017
    Location
    Frankfurt
    Posts
    1,432

    Re: Passing Controls to a Routine

    Hi Shaggy,

    you are right with the above.

    I'm wondering if this is the correct way to go threw all the Containers to get the Controls(Checkbox).

    I added a TabControl with a Panel inside
    in the Panel I added a Checkbox

    the State of the Checkbox (checked or unchecked) show correctly in Listbox2.

    Code:
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Call EnumControls(Me)
        End Sub
        Private Sub EnumControls(ByVal Parent As Control)
            'Get all the Controls and add them to Listbox1
            For Each ctrl As Control In Parent.Controls
                ListBox1.Items.Add(ctrl.GetType().ToString() & " " & ctrl.Name.ToString())
                'Get the Checkbox State and add to Listbox2
                'I added a TabControl with a Panel inside
                'in the Panel I added a Checkbox
                If TypeOf ctrl Is CheckBox Then
                    ListBox2.Items.Add(DirectCast(ctrl, CheckBox).CheckState & " " & ctrl.Name.ToString())
                End If
    
                If ctrl.Controls.Count > 0 Then
                    EnumControls(ctrl)
                End If
            Next
    
        End Sub
    or would it be better to address the Containers and search the Checkbox 'State' ?

    regards
    Chris
    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.

  14. #14
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    32,755

    Re: Passing Controls to a Routine

    At some point, I feel that it is better to create your own list in the constructor and populate it. It would certainly be possible to do a recursive search through all Controls containers on a form, but I wouldn't want to be doing that more than once due to the cost incurred, which might add up on a complicated form. If I wanted to know about N controls, I'd take the time to put them into a list in the constructor, and just deal with that from then on. There's a bit of maintenance involved there, if the set of controls is ever increased, but I still think it easier.
    My usual boring signature: Nothing

  15. #15
    Frenzied Member ChrisE's Avatar
    Join Date
    Jun 2017
    Location
    Frankfurt
    Posts
    1,432

    Re: Passing Controls to a Routine

    Quote Originally Posted by Shaggy Hiker View Post
    At some point, I feel that it is better to create your own list in the constructor and populate it. It would certainly be possible to do a recursive search through all Controls containers on a form, but I wouldn't want to be doing that more than once due to the cost incurred, which might add up on a complicated form. If I wanted to know about N controls, I'd take the time to put them into a list in the constructor, and just deal with that from then on. There's a bit of maintenance involved there, if the set of controls is ever increased, but I still think it easier.
    Thanks,

    like you said evan if there will be some maintenance involved, in the end it will be better.

    regards
    Chris
    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width