Removing Textbox and DateTimePicker in a Panel - vb.net

I have a code that deletes the textbox and datetimepicker i create in a panel... delete code is working for all the TEXTBOX but when its time to delete the datetimepickers it does not delete all the datetimepicker.
Example: there are 4 textbox and 4 datetimepickers when it runs the code the panel will delete all 4 textbox but deletes 2 datetiepickes only. I really cant find out what is wrong. Please help me... Thanks!
code is here:
For Each ctrlTxt As TextBox In panelGroupDependent.Controls.OfType(Of TextBox)()
ctrlTxt.Dispose()
Next
For Each ctrlDtp As DateTimePicker In panelGroupDependent.Controls.OfType(Of DateTimePicker)()
ctrlDtp.Dispose()
Next

As it stands, you are modifying a collection as you are enumerating it, which is not allowed. A For Each loop enumerates a collection so you are not allowed to add or remove items within the loop. When you dispose a control, it is removed from its parent container and thus you are modifying the Controls collection of that parent.
The OfType method doesn't actually generate a new collection of items but rather enumerates the collection it's called on and yields the items of the specified type one by one. As such, enumerating the result of OfType means enumerating the source collection at the same time.
By calling ToArray, you complete the enumeration of the source collection first and populate an array with the items of the specified type, then enumerate that array using the For Each loop. Disposing the controls has no effect on the array so there's no issue.
For Each ctrlTxt In panelGroupDependent.Controls.
OfType(Of TextBox)().
ToArray()
ctrlTxt.Dispose()
Next
For Each ctrlDtp In panelGroupDependent.Controls.
OfType(Of DateTimePicker)().
ToArray()
ctrlDtp.Dispose()
Next
Note that there is also no need to declare the type of the loop control variables as it can be inferred.

Related

Me.Controls does not find all controls on the form

I have a form that takes a list of search terms, and creates a combo box for each result set. The boxes get created at runtime, they are all individually named, and are all directly on the form, none are in panels or any sub controls.
I want to remove the previous searchs combo boxes every time a new search is done. I made the following sub to remove all combo boxes
For Each c As Control In Me.Controls
If c.GetType Is GetType(ComboBox) Then
Me.Controls.Remove(c)
c.Dispose()
End If
Next c
But for some mysterious reason, the Form doesn't see all the combo boxes the previous search created. If I create 3 boxes, it will see the first and third, but not the second. If I run the sub again, it will see the second one and catch it that time. So I need to run the sub twice to actually clear out all the comboboxes that were created. I checked the control collection and it seems to always be 1 short of the actual number of combo boxes that were created in the previous search
Any idea why it is always leaving a combo box behind and I need to clear them all again a second time to get rid of it?
Further to what Jimi said in the comments, I would recommend that you use this:
For Each cmbx In Controls.OfType(Of ComboBox)().ToArray()
cmbx.Dispose()
Next
The OfType does the filtering first and then the ToArray creates an array that you can loop over, so you're not enumerating and modifying a collection at the same time. There's no point removing and disposing the controls because disposing will implicitly remove them.

Is there a way to consolidate iteration through form controls for different activities

I just started rewriting an application from vba (Access) to vb.net + SQLServer so not very experienced in .net.
I am creating custom controls (Form + form controls) with a number of extra properties PrevValue, Modified (similar then the one of Textbox), Dirty, DirtyEnabled, SQLColumnName, SQLTableName to enable AutoUpdating and undoing in my forms the form exposes IsDirty, Initialising and Isready properties and an undo method.
Doing so it occurs that I have to write 3 times the same iteration code in different places:
For each Ctrl as Control in frm.Controls ' frm being a reference to the form
if typeOf Ctrl is MyTextBox
with DirectCast(Ctrl, MyTextBox)
' here comes the variable code depending what needs to be done
end with
elseif TypeOf Ctrl is MyComboBox
' etc.... for MyListBox, MyCheckBox etc....
I also have a number of custom controls MyNumBox and MyDateBox that inherit from MyTextBox but with some modified behavior (Formula evaluation, date manipulation, calendar...) how do I avoid doing an extra test on them.
One version of this Iteration is in the SQLProcessClass where the modified controls are added as SQLParameter and after iteration calling the SQLProcessClass Update or Insert, but ... after successful SQL activity I need to iterate through the controles again to reset the modified flag for each control. Elsewhere I need it to implement a form undo to reset all the controls to their previous values.
It seems to me I have two options
1. repeating that iteration code everywhere I need to iterate through the forms controls. I don't like it as every time I would need to create a new custom control I have to add some lines X times in different modules/classes ... very bad programming
2. Creating one form iteration procedure containing all the different activities that normally belong to another class within that "centralised" procedure, that could be better then (1) but I don't like it that much either.
Is there a better way of doing it using some .net functionality I don't master yet ?
Thanks for any advise.
Iterating through from controls can be tricky since controls are often nested. A more controlled approach would be to add another collection object to your form where you keep references to your added controls.....
e.g
Dim My_Widgets as New List(of Your-Control-Class-Name)
Then when you create the controls to the form also add them to that list.
My_Widgets.Add(Widget_Object)
After that it is a simple matter to iterate through that list.
For Each Widget as My_Widget_CLass in My_Widgets
' do what you need to do to Widget
Next
If you need to reference individual controls directly, use a dictionary object instead..
e.g.
Dim My_Named_Widgets as new Dictionary(of String, Your-Control-Class-Name)
Then add your control references to the dictionary by name
My_Named_WIdgets.add("<Whatever_You_USe_To_Identify_It>", Widget_Object)
You can then reference the specific control by the ID or name
My_Names_Widgets("ID").Property = Whatever '... etc
You seem to be indicating you have other controls for other purposes, as such it would be prudent to create similar collections for each type.

Load comboboxes with one line of code?

I have around 15 comboboxes on my form, all being loaded with the same information pulled from a table(~150 entries). Currently I am taking the information from the table, then looping through the entries and adding them to each textbox. I'm wondering if there's a more efficient way to load these comboboxes then having to individually add the table entry into each combobox, having to list 15 lines of code within the For loop.
I'm not seeing any performance issues with this, but figured I might as well work with the most efficient way possible rather than stick with what works. :)
You can create a list of the combo boxes, and then just loop through them. For instance:
Dim cbos() As ComboBox = {ComboBox1, ComboBox2, ComboBox3}
For Each cbo As ComboBox In cbos
' Load cbo from table
Next
Alternatively, if they are named consistently, you could find the combo box by name:
For i As Integer = 1 to 15
Dim cbo As ComboBox = DirectCast(Controls("ComboBox" & i.ToString())), ComboBox)
' Load cbo from table
Next
Since Combobox items are a collection, if their elements are the same, you can build and array with the objects you want to insert, and then just insert this array to each ComboBox with the method AddRange() (it's a method which exists inside the Combobox.items).
Getting an example from MSDN:
Dim installs() As String = New String() {"Typical", "Compact", "Custom"}
ComboBox1.Items.AddRange(installs)
Then you would only have to do a loop to add the array to each ComboBox. Of course, you will need to build your array first on your own, instead of this easy string array from the example.
Reference:
MSDN - AddRange
You could also do it this way since you mentioned that you already have a table.
Use a datatable
Change your table object into a datatable, which will assist in binding to the comboboxes. It might help if you add the datatable to a dataset too. That way you can attach all ComboBoxes (which are UI elements that let users see information) to the same DataSource, which is the datatable, in the dataset.
Binding
Now all you need to do is loop through all the comboboxes and set the datasource to the same table, that is if you decide to do it programmatically like so:
ComboBox1.DataSource = ds.Tables(0)
ComboBox1.ValueMember = "au_id"
ComboBox1.DisplayMember = "au_lname"
A further tutorial on this with the example above is found here
You can then also get the user selected value with ComboBox1.selectedValue.
On the other hand, if you did this with C# WPF, you can bind each comboBox in the XAML directly, I am unsure if this can be done in VB.net as I tried to look for the option but did not manage to do so, something you might want to try though.
Some very useful tutorials and guides on Data binding, which you might be interested:
~ denotes recommended reading for your question
MSDN: Connect data to objects
DotNetPerls on DataGridView (note this isn't a combobox, just displaying values)
~ VBNet DataTable Usage from DotNetPerls (this is in relation to 1.)
~ SO Q&A on Binding a comboBox to a datasource
Concepts of Databinding

Clearing combobox dropdownlists from application

I am working on an assignment that requires a user to answer 20 questions( multiple choice ). I am using the DropDownList property so the user cannot input anything other than A, B, C, or D.
Basically, I have 20 comboboxes and I have a button that clears them, but the code I should obviously be a loop, but I am not sure how to do that.
As of now, my code looks like this:
cboQuestion1.Items.Clear()
cboQuestion2.Items.Clear()
...
cboQuestion20.Items.Clear()
If anyone could shed some light on this, I will be grateful.
All controls reside in the form's Controls collection, so one way would be to iterate that (assumes these CBOs are the only ones you wish to clear):
For Each cbo As ComboBox In Controls.OfType(Of ComboBox)
cbo.Items.Clear
Next
Another way is to store the names of the target controls in a List(of String). Think of this as a shopping list of the controls you wish to track or treat in some special way:
Private myCBONamesList As List(of String)
'...
myCBONamesList.Add("cboQuestion1")
' etc
' add many/all at once:
myCBONamesList.Addrange(New String(){"cboQuestion1", "cboQuestion2" ...etc})
The New String() creates a temp array containing the literal values listed (in {}) and the whole thing is passed to your List to populate it. To use it:
For Each s As String in myCBONamesList
Controls(s).Items.Clear
Next
This method allows you to target certain CBOs and leave others alone.
It may or may not be the best way, but you could add all of your combo boxes to a List and then iterate over the list to clear them all.
Just iterate over the Form's Controls collection.
Here is an example of iterating over the Forms Controls collection with filtering to make sure you don't accidentally clear a non-question ComboBox:
For Each cbo As ComboBox In Me.Controls.OfType(Of ComboBox)
If cbo.Name Like "cboQuestion*" Then
cbo.Items.Clear()
End If
Next
Edit: Or if you're into one-lining things:
For Each cbo As ComboBox In Me.Controls.OfType(Of ComboBox).Where(Function(x) x.Name Like "cboQuestion*")
cbo.Items.Clear()
Next

What is the equivalent of EOF in Bindingsource?

Can you help me with my problem in navigating BindingSource.
I want to convert this vb6 code in vb.net using BindingSource
Do Until recordSet.EOF
if isnull(resordSet!FieldName)=true then
recordSet.delete
end if
recordset.movenext
loop
You can replace the entire loop with BindingSource.Clear:
The Clear method removes all elements from the underlying list
represented by the List property and sets the Count property to zero.
This method raises the ListChanged event.