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. - Marked As Answer byAland LiMSFT, ModeratorWednesday, August 05, 2009 11:59 AM
-
| | 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).
- Marked As Answer byAland LiMSFT, ModeratorWednesday, August 05, 2009 11:59 AM
- Edited byFunkyDecster Monday, August 03, 2009 8:33 AM
-
| | 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. - Marked As Answer byAland LiMSFT, ModeratorWednesday, August 05, 2009 11:59 AM
-
| | 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).
- Marked As Answer byAland LiMSFT, ModeratorWednesday, August 05, 2009 11:59 AM
- Edited byFunkyDecster Monday, August 03, 2009 8:33 AM
-
| | 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 |
|