Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Using bound controls on the fly 1

Status
Not open for further replies.

artyk

Programmer
Apr 5, 2006
163
US
I am currently working on an application that creates some controls at runtime based on the number of records in the table. I have run into a strange problem however and wanted to see if anyone had any ideas.

The app adds controls just fine and sets the attributes (name, size, position, etc.) just as it is supposed to. But, when it is setting the selected values for the comboboxes on the form, it sets them all to the same value. The combobox names are created on the fly also and are incremented (ex. cboStaffJob & x+1) according to the iteration #.

They are obviously set up using a variable and referenced by the variable when the values are set.

Code:
Dim cbo as new combobox
cbo.Name = "cboStaffJob" & x+1
.
.
.
Dim tmpRecordJob as string = rstRecord.Item(1)
.
.
.
cbo.SelectedValue = tmpRecordJob

I just can't figure out why they are all being set to the same value. I know it has something to do with the databinding, but I've checked to make sure the names, etc. are being incremented.

I'm obviously doing something wrong, but what?
Thanks in advance!
 
What is rstRecord? A DataRow?
Does it contains the same values (or the same record) for each combobox?

Good luck.
 
Yes, we would have to know more/see more code to have any idea how to help. That piece of code you show doesn't increment the record in any way so they would all have the same data.

-I hate Microsoft!
-Forever and always forward.
-My kingdom for a edit button!
 
Sorry, yes rstRecord is a data row. It gets a value from the table based on whatever iteration it's currently on (represented by 'x') and then passes that to the combobox. The next time through it's supposed to get the next value, and so on. (I'll post some more code).

Code:
Dim cbo As New ComboBox
cbo.Name = "cboStaffJob" & x + 1
cbo.Tag = x + 1
cbo.SetBounds(266, y + 25, 144, 21)
cbo.DataSource = Me.JobsBindingSource
cbo.DisplayMember = "JobTitle"
cbo.ValueMember = "JobID"
Me.Controls.Add(cbo)
.
.
.
Dim tblRecord As DataTable = Me.StaffLevelsTableAdapter.GetDataBy3(cboStaffDay.SelectedItem)
Dim rstRecord As DataRow = tblRecord.Rows(x)
Dim tmpRecordJob As String = rstRecord.Item(1)
Dim tmpRecordNum As Integer = rstRecord.Item(2)
Dim tmpRecordStart As DateTime = rstRecord.Item(3)
Dim tmpRecordEnd As DateTime = rstRecord.Item(4)
.
.
.
cbo.SelectedValue = tmpRecordJob

I have other controls (unbound) that work perfectly. For example, I am using a numerical up and down control, and two date time pickers, and they all display the correct info. So I know it's stepping through everything correctly.

The form is supposed to let the user pick the day of the week and then display the staff positions that are needed for that day, the times they are needed, and the number that is needed for each position. The value that it's setting all the combo boxes to happens to be the value that the last combo box should be set to.
Let's say the values for Monday (in the table, for the combobox) are 'manager', 'cashier', 'janitor'. It is setting all the comboboxes to 'janitor'. Or, if we had just 'manager' and 'cashier' it would set them all to 'cashier', etc.

Hopefully this makes more sense.
 
I'm not the best on bound records so this may not be spot on, but I pretty sure the reason is because they are bound. A bound control by default should show the information for the current record in the bound source.

Since here
Code:
cbo.DataSource = Me.JobsBindingSource
cbo.DisplayMember = "JobTitle"
cbo.ValueMember = "JobID"
you are setting them all to the same item they are going to try to show the same item. They are then returning 'janitor' because that was the last value selected.

How do you fix it? Not sure yet.

-I hate Microsoft!
-Forever and always forward.
-My kingdom for a edit button!
 
Actually I see no way of doing so. The are behaving correctly. You either have to unbind them or bind them to different columns.

-I hate Microsoft!
-Forever and always forward.
-My kingdom for a edit button!
 
Ok, I'll try the "unbound" route. Shouldn't be too difficult. Just a matter of populating the combobox list of items on the fly. Thanks!
 
NP. Someone may come back and tell you different, but I just don't see how it could be possible. Well you could setup multiple BindingSources each pulling from the same data, but that could quickly become crazy.

-I hate Microsoft!
-Forever and always forward.
-My kingdom for a edit button!
 
I've got it adding controls perfectly now, but I had to put in a way for users to be able to remove controls as well. The sub procedure I put in works (sort of), but has a strange quirk. It only removes 2 of the 4 controls that are supposed to be removed. I've placed counts in 2 different locations and the number of controls returned is the same in both locations. For some reason, though, it loops through the code 2 less times than there are controls.

Code:
Private Sub btnStaffRemoveSlot_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStaffRemoveSlot.Click
        'TODO: FIND LAST CONTROL (HIGHEST TAG) AND DELETE CONTROLS WITH THAT TAG
        Dim ctl As Control = Me
        Dim count As Integer 'count # of controls on form
        For Each c As Control In ctl.Controls
            count = count + 1
            If c.Tag = HighestTag Then
                c.Dispose() 'DOESN'T QUITE WORK RIGHT-ONLY DELETES 2 OF THE 4 CONTROLS(1st & 3rd)
                'SEES 31 CONTROLS, BUT RUNS THROUGH THIS PART ONLY 29 TIMES
                MsgBox(count) 'returns 31 controls
            End If

        Next
        'MsgBox(count) 'returns 31 controls
    End Sub

HighestTag represents the highest "tag" value on the form and is how the app distinguishes which controls to delete.

Is there a better way to remove controls that I may be overlooking? Thanks in advance!
 
In my experence Collections, deleting, and for each loops do not work well together. Some times they work and other times they cause nothing but errors. So at the least switch to a standard for loop and see if that helps. Double check your tags. Do a break and watch your c.Tag = HighestTag maybe you acedentily are changing them some where before the delete. The other thing is that I don't think it will Dispose a control that is in use try removing the control first then Dispose it.

So try these changes and watch HighestTag.

Code:
    Private Sub btnStaffRemoveSlot_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStaffRemoveSlot.Click
        Dim ctl As Control = Me
        Dim count As Integer
        For count = 0 to ctl.Controls.Count
            Dim c As Control = ctrl.Countrols.Item(count)
            If c.Tag = HighestTag Then
                ctrl.Controls.Remove(c)
                c.Dispose()
                MsgBox(count)
            End If
        Next
    End Sub

-I hate Microsoft!
-Forever and always forward.
-My kingdom for a edit button!
 
Are any of the controls to be removed in a sub-container, such as a panel or groupbox? If so, you will need to loop through the sub-container's Controls collection to delete the controls you want deleted.



I used to rock and roll every night and party every day. Then it was every other day. Now I'm lucky if I can find 30 minutes a week in which to get funky. - Homer Simpson

Arrrr, mateys! Ye needs ta be preparin' yerselves fer Talk Like a Pirate Day!
 
Thanks for the suggestions. No, there is no sub-container.
 
Well, I'm still having the same issue. It deletes the first DateTimePicker and the ComboBox, but not the second DateTimePicker or the NumericUpDown (it's deleting the 1st and 3rd controls). I even stuck a MsgBox in there to give the "tag" value of each control as the code cycled through the form. Interestingly, there were only 2 controls that gave the highest tag value (should be 4), so I went back to the code where the controls are added. I stepped through and the controls are being added with the correct tag values. I'll see if they are being manipulated somewhere else.
 
After a bit more testing I found out that regardless of what the tag of the controls to be deleted is, it always only sees 2 controls with that tag during the delete procedure.

For example, I have 4 records in the table right now, when I add a new blank row of controls, that makes the 5th record. So, when I run the delete, it sees 4 controls with the tag "1", 4 with "2", 4 with "3", 4 with "4" and 2 with "5". But, if I don't add a new blank row, it does the same thing to the "4"'s. That should mean that the controls are being added correctly and with the correct tag values.

Also, if I run the delete procedure 4 times it will delete all 4 controls from the last row. It deletes 2 the first time, as I described above, and 1 each the next 2 times. All while seeing the correct "highest tag" value (5).

I know that's a lot of info, but I was hoping it might give someone some clues, as I have just about exhausted everything I know to check.

Thanks again to everyone, both in advance, and after the fact. Your help is, and has been, appreciated.
 
So are these controls on a datagrid? Or how are you adding a "row" of controls?

-I hate Microsoft!
-Forever and always forward.
-My kingdom for a edit button!
 
No, they are just regular controls. Basically, when a "row" of controls is added, it adds 2 DateTimePickers, a ComboBox, and a NumericUpDown control to correspond to the type of data that is in the table. It's layed out sort of like a datagrid would be, but I'm just using regular form controls.

It's something like this:

DTP DTP Combo NUD
DTP DTP Combo NUD
.
.
.
etc.
 
Ok, just wanted to make sure. If you could show how you add a row and any changes you made to deleting a row. There is something missing some where because if they are getting tagged right then they should be removed the first time. There is nothing wrong with your thought on that.

-I hate Microsoft!
-Forever and always forward.
-My kingdom for a edit button!
 
Sure, no problem. Here is the procedure that adds the controls:

Code:
Private Sub AddControls(ByVal x As Integer, ByRef y As Integer, ByVal blnButton As Boolean)
        'adds controls as needed and populates them with data

        Dim dtp As New DateTimePicker
        dtp.Format = DateTimePickerFormat.Custom
        dtp.CustomFormat = "h:mm tt"
        dtp.Name = "dtpStaffTimeBeg" & x + 1
        dtp.Tag = x + 1
        dtp.SetBounds(28, y + 25, 82, 20)
        dtp.ShowUpDown = True
        Me.Controls.Add(dtp)

        Dim dtp2 As New DateTimePicker
        dtp2.Format = DateTimePickerFormat.Custom
        dtp2.CustomFormat = "h:mm tt"
        dtp2.Name = "dtpStaffTimeEnd" & x + 1
        dtp2.Tag = x + 1
        dtp2.SetBounds(128, y + 25, 82, 20)
        dtp2.ShowUpDown = True
        Me.Controls.Add(dtp2)

        Dim cbo As New ComboBox
        cbo.Name = "cboStaffJob" & x + 1
        cbo.Tag = x + 1
        cbo.SetBounds(266, y + 25, 144, 21)

        Dim tblJobs As DataTable = Me.JobsTableAdapter.GetData
        Dim intJobs As Integer = Me.SchedulerDataSet.Jobs.Rows.Count
        Dim z As Integer
        For z = 0 To (intJobs - 1)
            Dim rstJobs As DataRow = tblJobs.Rows(z)
            Dim strJobs As String = rstJobs.Item(1)
            cbo.Items.Add(strJobs)
        Next z
        Me.Controls.Add(cbo)

        Dim nud As New NumericUpDown
        nud.Name = "nudStaffLevel" & x + 1
        nud.Tag = x + 1
        nud.SetBounds(471, y + 25, 41, 20)
        Me.Controls.Add(nud)

        y = nud.Location.Y

        'get the "tag"
        If blnButton = False Then

            Dim tblRecord As DataTable = Me.StaffLevelsTableAdapter.GetDataBy3(cboStaffDay.SelectedItem)
            'gets all records for the selected day
            Dim rstRecord As DataRow = tblRecord.Rows(x)
            'gets the "x" record from the selected day
            Dim tmpRecordJob As String = rstRecord.Item(1)
            Dim tmpRecordNum As Integer = rstRecord.Item(2)
            Dim tmpRecordStart As DateTime = rstRecord.Item(3)
            Dim tmpRecordEnd As DateTime = rstRecord.Item(4)

            dtp.Value = tmpRecordStart
            dtp2.Value = tmpRecordEnd

            Dim tbltmpJob As DataTable = Me.JobsTableAdapter.GetRow(tmpRecordJob)
            Dim rsttmpJob As DataRow = tbltmpJob.Rows(0)
            Dim strtmpJob As String = rsttmpJob.Item(1)
            cbo.SelectedItem = strtmpJob

            nud.Value = tmpRecordNum
        End If

        HighestTag = x + 1

    End Sub

And here is the current delete procedure:

Code:
Private Sub btnStaffRemoveSlot_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStaffRemoveSlot.Click
        'TODO: FIND LAST CONTROL (HIGHEST TAG) AND DELETE CONTROLS WITH THAT TAG
        Dim ctl As Control = Me
        Dim count As Integer 'count # of controls on form
        MsgBox(HighestTag)
        For Each c As Control In ctl.Controls 
            count = count + 1
            MsgBox(c.Tag, MsgBoxStyle.OkOnly, c.Name)
            
            If c.Tag = HighestTag Then
                ctl.Controls.Remove(c)
                c.Dispose() 
                
            End If

        Next
    
    End Sub

When I step through the program at the add procedure, everything looks ok.
 
Ok, I believe I have a solution. Try this:

For c As Integer = Me.Controls.Count - 1 to 0 Step -1
If Me.Controls(c).Tag = HighestTag Then
Me.Controls.RemvoeAt(c)
End If
Next

What is happening in your current code is that as each control is removed, the controls after it in the collection are moved up to fill the space. You then move to the next space and so delete the second control after the first deleted one, not the next one as you would expect. This also explains why subsequent runs of your code delete one control each run.

What the code I provided does is step through the controls backwards. Then when a control is removed, any controls following it have already been checked or removed, and the position of the next control to be checked is not affected by this control's removal.

Anyway, give the code a shot and let me know if it works.

I used to rock and roll every night and party every day. Then it was every other day. Now I'm lucky if I can find 30 minutes a week in which to get funky. - Homer Simpson

Arrrr, mateys! Ye needs ta be preparin' yerselves fer Talk Like a Pirate Day!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top