Windows Develop Bookmark and Share   
 index > Windows Forms Data Controls and Databinding > How to make a selection in one field based on a user selection in another
 

How to make a selection in one field based on a user selection in another

We have forms that are databound to a single row datatable. Various controls on the form are bound to the fields on that form as follows (this is for a combo but we also do similar with textboxes etc)

If Not m_DataField = String.Empty Then

    DataBindings.Clear()

    DataBindings.Add(New System.Windows.Forms.Binding("SelectedValue", dt, m_DataField))                

End If


Normally we read the single record from the database using a sproc but id a user is in 'New' Mode we use FillSchema to get the row's schema and then create a blank record programmatically:-
Dim Conn As New SqlConnection(ConnectionString)

Dim Da As New SqlDataAdapter()

Dim Dt As New TSDataTable

Dim r As DataRow



Conn.Open()

'Build up a select command

Da.SelectCommand = (New SqlCommand(sPName, Conn))

Da.SelectCommand.CommandType = CommandType.StoredProcedure

'Ask the DB what parms it would expect for this select command

SqlCommandBuilder.DeriveParameters(Da.SelectCommand)

'Fill the schema from the DB

Da.FillSchema(Dt, SchemaType.Source)

'Go through the resultant columns telling them to allow nulls, be read/write and not to autoincrement ID fields

For Each col As DataColumn In Dt.Columns

        col.AllowDBNull = True

        col.ReadOnly = False

        col.AutoIncrement = False

Next

r = dt.NewRow'Add the new row to the data table

dt.Rows.Add(r)


All pretty standard so far. The problem I'm getting is that, when a user make a selection in one field I want to automatically populate a different field based on the user selection. Specifically, we send letters to our customers and each letter can have a promotional pack with it. Each type of letter has a default pack. So when the user selects the letter they're going to send using a combo, I want the pack combo to display the default pack for that letter and I want the value to go onto the datarow so it saves back correctly later. The way I thought I would achieve this was to trap the selected index changed event on the letter comboand do the following:-
dt.Rows(0).Item("MailshotPackID") = CType(cmbLetterTemplate.SelectedItem, DataRowView).Item("DefaultPack")
Basically, if the the user selects a letter I grab the default pack from the combo's datarow and set that value on the forms datarow. This gives me a problem, though. If the letter selection is the first thing a user does the event fires twice, and the second time Item("DefaultPack") is returning null (so are all the other fields on the record) - effectively it's clearing the selection the user just made. I have no idea what is forcing that second event but the call stack just says 'external code'. If the user has made any change in any other databound field or if they're editing an existing record rather than creating a new one then this doesn't happen and the behaviour is perfect.

I've been told elsewhere that I shouldn't make an edit to the datarow programmatically while it's already being edited (which it will be because the user selection on the letter combo is being pushed bachk to the datatable at this point) but I haven't found a satisfactory alternative. Could someone suggest how I should achieve this?
FunkyDecster  Wednesday, July 29, 2009 8:49 AM

Hi FunkyDecster,

In your first reply, you said the SelectedIndexChanged event is fired twice. We need to figure out the root cause of this issue. We can remove this line of code:
dt.Rows(0).Item("MailshotPackID") = CType(cmbLetterTemplate.SelectedItem, DataRowView).Item("DefaultPack") and then check if the SelectedIndexChanged event is fired twice.
If so, we need to remove the handler before the code I mentioned above and add the handler again after that. This is the code snippet:

Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)

RemoveHandler ComboBox1.SelectedIndexChanged, AddressOf ComboBox1_SelectedIndexChanged

dt.Rows(0).Item("MailshotPackID") = CType(cmbLetterTemplate.SelectedItem, DataRowView).Item("DefaultPack")

AddHandler ComboBox1.SelectedIndexChanged, AddressOf ComboBox1_SelectedIndexChanged

End Sub

Let me know if this helps.
Aland Li


Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
Aland Li  Friday, July 31, 2009 2:41 AM

Yep, I've got it to work with a little bit of twisting. The eventual solution was this:-

Dim letterTemplateID As Integer = CType(cmbLetterTemplate.SelectedItem, DataRowView).Item("ID")
RemoveHandler cmbLetterTemplate.SelectedIndexChanged, AddressOf cmbLetterTemplate_SelectedIndexChanged
m_DataTable.Rows(0).Item("MailshotPackID") = CType(cmbLetterTemplate.SelectedItem, DataRowView).Item("DefaultPack")
m_DataTable.Rows(0).Item("LetterTemplateID") = letterTemplateID
AddHandler cmbLetterTemplate.SelectedIndexChanged, AddressOf cmbLetterTemplate_SelectedIndexChanged

Removing the handler in the way you suggested worked perfectly as far as preventing the clearing of the mailshot pack combo was concerned. However, whatever was re-firing the event was still clearing down the letter combo. Storing the selected value and then resetting it seems to have done the trick on that front too.

In an ideal world I'd still like to find out why setting the mailshot pack id causes the letter combo to clear but this seems to be working for now. Thanks alot for the help.


>I am sorry that I cannot understand your words clearly.
It's just that I've accidentally registered twice on the forum. I'd like to merge the profiles if possible.On most forums I'd send a message to a mod but I can't see how to do that (I'm probably missing something obvious).
FunkyDecster  Monday, August 03, 2009 8:31 AM

Hi FunkyDecster,

You said:
I've been told elsewhere that I shouldn't make an edit to the datarow programmatically while it's already being edited (which it will be because the user selection on the letter combo is being pushed bachk to the datatable at this point) but I haven't found a satisfactory alternative.

It points out the key problem. My solution is to copy the data in the DataTable to a new DataTable and bind that new DataTable to the field. This is the code snippet:

If Not m_DataField = String.Empty Then

DataBindings.Clear()

Dim newDt As DataTable = dt.Copy()

DataBindings.Add(New System.Windows.Forms.Binding("SelectedValue", newDt, m_DataField))

End If

Let me know if this helps.
Aland Li


Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
Aland Li  Thursday, July 30, 2009 9:56 AM
Hi Aland and thanks for the response.

I'm not sure I understand how that's going to help me, though. I can see why it would stop the event firing twice because the combo a user was selecting in (the letter combo) would be bound to a completely different table to the combo I want to do the programmatic select in (the pack combo). But I then won't have a single datarow with a complete view will I? Am I missunderstanding you?
FunkyDecster  Thursday, July 30, 2009 10:59 AM

Hi FunkyDecster,

Yes, your understanding is correct. You said:
But I then won't have a single datarow with a complete view will I?

If you mean the data in two tables would not be consistent, it is true. We need to synchronize the data later. Could you please tell me the function of the bound field in detail. I can give you another suggestions if I know your needs more clearly.

Best regards,
Aland Li


Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
Aland Li  Thursday, July 30, 2009 12:27 PM
Hi Aland

>>We need to synchronize the data later
I guess I could do that. Actually it'd be pretty simple. There's only one combo/field affected so I simply copy the value in that field off the copy datatable into the main datatable immediately before saving. It does feel like quite a horrible cludge though so if you've got any better suggestions I'd still like to hear them. Apart from anything, all the binding is handled in an overridden version of the combo box (or text box, etc) and our framework is built around that assumption. Using a copy would mean breaking the framework which I'd rather avoid if possible.

As far as the function of the bound field, there's two bound fields I'm concerned with. In this scenario the user is creating a batch of mailshots.A batch of mailshots is basically a bunch of letter to be sent to customers. The whole batch will use the same letter, the same pack, be printed on the same day etc. Each Letterhas a default pack that's associated with it but the user canchoose to send a different pack if they wish. Specifically, when a user select the letter (which is selected from a combo)for the batch I want the pack (which is also selected from a combo) to show the default pack for that letter. Both the letter and the pack combosare bound to different fields on the same underlying single row datatable.

Also, both combos are populated by setting their data sources to datatables and setting the Value and Display Members. Obviously it's a different table for each combo box.
FunkyDecster  Thursday, July 30, 2009 1:10 PM

Hi FunkyDecster,

In your first reply, you said the SelectedIndexChanged event is fired twice. We need to figure out the root cause of this issue. We can remove this line of code:
dt.Rows(0).Item("MailshotPackID") = CType(cmbLetterTemplate.SelectedItem, DataRowView).Item("DefaultPack") and then check if the SelectedIndexChanged event is fired twice.
If so, we need to remove the handler before the code I mentioned above and add the handler again after that. This is the code snippet:

Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)

RemoveHandler ComboBox1.SelectedIndexChanged, AddressOf ComboBox1_SelectedIndexChanged

dt.Rows(0).Item("MailshotPackID") = CType(cmbLetterTemplate.SelectedItem, DataRowView).Item("DefaultPack")

AddHandler ComboBox1.SelectedIndexChanged, AddressOf ComboBox1_SelectedIndexChanged

End Sub

Let me know if this helps.
Aland Li


Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
Aland Li  Friday, July 31, 2009 2:41 AM
If I remove that line of code it doesn't fire twice. It's definitely setting the value of MailshotPackID that's causing the problem (I'm sure of that from my own testing.)

Removing the handler sounds perfect. That should prevent it firing the second time at ll. I'll give it a try out on Monday and post back with the results. Thanks alot.


BTW I notice you're a mod. I messed up a bit when I registered and now have two active logins I'd like to merge. I can't see how I'm supposed to contact a mod on the forum to sort it out. Can you point me in the right direction.
FunkyDecster  Saturday, August 01, 2009 11:35 AM

Hi FunkyDecster,

Thanks for your reply. If you have resolved you issue, it is appreciated if you can post the answer here. If you still have some questions unsolved, you can post here, we would like to give you other suggestions.

You said:
BTW I notice you're a mod. I messed up a bit when I registered and now have two active logins I'd like to merge. I can't see how I'm supposed to contact a mod on the forum to sort it out. Can you point me in the right direction.

I am sorry that I cannot understand your words clearly. If you mean how to find a experienced person to answer your question, it is hard for me to give you an accurate answer. I can only assure that there are so many professional MVPs and MSFTs here and all of us would try our best to solve your issue.


Best regards,
Aland Li


Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
Aland Li  Monday, August 03, 2009 2:14 AM

Yep, I've got it to work with a little bit of twisting. The eventual solution was this:-

Dim letterTemplateID As Integer = CType(cmbLetterTemplate.SelectedItem, DataRowView).Item("ID")
RemoveHandler cmbLetterTemplate.SelectedIndexChanged, AddressOf cmbLetterTemplate_SelectedIndexChanged
m_DataTable.Rows(0).Item("MailshotPackID") = CType(cmbLetterTemplate.SelectedItem, DataRowView).Item("DefaultPack")
m_DataTable.Rows(0).Item("LetterTemplateID") = letterTemplateID
AddHandler cmbLetterTemplate.SelectedIndexChanged, AddressOf cmbLetterTemplate_SelectedIndexChanged

Removing the handler in the way you suggested worked perfectly as far as preventing the clearing of the mailshot pack combo was concerned. However, whatever was re-firing the event was still clearing down the letter combo. Storing the selected value and then resetting it seems to have done the trick on that front too.

In an ideal world I'd still like to find out why setting the mailshot pack id causes the letter combo to clear but this seems to be working for now. Thanks alot for the help.


>I am sorry that I cannot understand your words clearly.
It's just that I've accidentally registered twice on the forum. I'd like to merge the profiles if possible.On most forums I'd send a message to a mod but I can't see how to do that (I'm probably missing something obvious).
FunkyDecster  Monday, August 03, 2009 8:31 AM

Hi FunkyDecster,

In the last reply, you said: In an ideal world I'd still like to find out why setting the mailshot pack id causes the letter combo to clear but this seems to be working for now. Since I cannot reproduce the error you met, I need a complete code snippet to figure out your issue. You can move the code into a small test project in which the error can be reproduced and then send it to me via this email: alala666888@hotmail.com.

You also said: It's just that I've accidentally registered twice on the forum. I'd like to merge the profiles if possible.You can post in this forum to get more help:
http://social.msdn.microsoft.com/Forums/en-US/reportabug/threads.

By the way, please make my reply as answer if you think it helps. This would help others to search the solution for their issues.

Best regards,
Aland Li

Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
Aland Li  Tuesday, August 04, 2009 2:15 AM

To reproduce it:-
1. create a new windows forms app with a single form and put two combo boxes on it called combobox1 and combobox2
2. you'll need two stored procedure to populate the combos. The first should return an ID, Description and DefaultPack fields, the second should return ID and Description fields. ID and Default Pack fields are integers, Description fields are varchars (the type probably doesn't matter though)
3. you'll need a third stored procedure to create a blank row from. It should return two integer fields called LetterTemplateID and MailshotPackID
4. cut and paste the following code into the form:-

Imports System.Data.SqlClient

Public Class Form1
    Private m_DataTable As DataTable
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim conn As New SqlConnection("A Connection String")

        '*******************************************************
        'Populate the combos
        Dim dt1 As New DataTable
        Dim da1 As New SqlDataAdapter
        Dim cmd1 As SqlCommand

        conn.Open()
        cmd1 = New SqlCommand("GetLetters", conn) 'Sproc must return ID, Description and a DefaultPack fields
        cmd1.CommandType = CommandType.StoredProcedure
        da1.SelectCommand = cmd1
        da1.Fill(dt1)
        ComboBox1.DataSource = dt1
        ComboBox1.ValueMember = "ID"
        ComboBox1.DisplayMember = "Description"
        conn.Close()

        Dim dt2 As New DataTable
        Dim da2 As New SqlDataAdapter
        Dim cmd2 As SqlCommand

        conn.Open()
        cmd2 = New SqlCommand("GetPacks", conn) 'Sproc must return ID and Description fields
        cmd2.CommandType = CommandType.StoredProcedure
        da2.SelectCommand = cmd2
        da2.Fill(dt2)
        ComboBox2.DataSource = dt2
        ComboBox2.ValueMember = "ID"
        ComboBox2.DisplayMember = "Description"
        conn.Close()

        '*******************************************************
        'Now populate the form with an empty record and bind it:-
        Dim Da As New SqlDataAdapter()
        Dim r As DataRow
        m_DataTable = New DataTable

        'Get the schema of an empty record
        conn.Open()
        Da.SelectCommand = (New SqlCommand("GetMailshotBatch", conn)) 'Sproc Must return LetterTemplateID and MailshotPackID fields
        Da.SelectCommand.CommandType = CommandType.StoredProcedure
        SqlCommandBuilder.DeriveParameters(Da.SelectCommand)
        Da.FillSchema(m_DataTable, SchemaType.Source)
        For Each col As DataColumn In m_DataTable.Columns
            col.AllowDBNull = True
            col.ReadOnly = False
            col.AutoIncrement = False
        Next

        'Now add an empty record to the data table
        r = m_DataTable.NewRow
        m_DataTable.Rows.Add(r)

        'Bind the combos to the fields on the empty row.
        ComboBox1.DataBindings.Clear()
        ComboBox1.DataBindings.Add(New System.Windows.Forms.Binding("SelectedValue", m_DataTable, "LetterTemplateID"))
        ComboBox2.DataBindings.Clear()
        ComboBox2.DataBindings.Add(New System.Windows.Forms.Binding("SelectedValue", m_DataTable, "MailshotPackID"))
    End Sub

    Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
        If m_DataTable IsNot Nothing Then
            m_DataTable.Rows(0).Item("MailshotPackID") = CType(ComboBox1.SelectedItem, DataRowView).Item("DefaultPack")
        End If
    End Sub
End Class

 
5. Run the form and immediately make a selection in combo 1 (the letters combo). Your selection will leave both combos blank - even though you selected an actual value.

What seems to be happening is that your selection is initially being honoured and the selected index changed event fires. But then when the line of code that sets the mailshotpackid runs something (and I can't tell what) clears your selection in combobox1. That refires the event and clears the selection in the combobox2 because DefaultPack will be null if there's no row being selected in combo1.

Thanks for all your help with this. I really apreciate it.
FunkyDecster  Tuesday, August 04, 2009 8:46 AM

You can use google to search for other answers

Custom Search

More Threads

• edit button
• Dataset
• Binding to a TextBox Problem
• On a DataGridView control, is it possible to ...
• Data Binding
• resizing of comboBox and TextBox
• How can add ListBox in DataGridView using C#.net (Window Application)?
• Newbie question: Cstr vs toString
• GeidView Button and Link Fields
• Changing a BindingSource's DataSource (without telling it)