FIX: Data Bound User Control Updates Only First Bound Column (812918)



The information in this article applies to:

  • Microsoft ADO.NET (included with the .NET Framework) 1.0
  • Microsoft Visual Basic .NET (2002)
  • Microsoft Visual C# .NET (2002)

SYMPTOMS

When you try to update more than one column of a table by using a bound user control that has multiple bound properties, only the modified data for the first bound property is updated in the table. You may notice this behavior only for the first row of the table.

WORKAROUND

To work around this problem in ADO.NET 1.0, call EndCurrentEdit to end the current edit operation for all the bound controls. You can call EndCurrentEdit in the Validating event of each bound control, or you can call the OnValidating event of the UserControl class as demonstrated in the following code:

Call EndCurrentEdit in the Validate Event of UserControl

Append the following code to the UserControl1 class:

Visual Basic .NET Code

   ' Override the OnValidating of a Control 
   Protected Overrides Sub OnValidating(ByVal e As System.ComponentModel.CancelEventArgs)
      ' Iterate through each bound object and then end the current edit
      Dim b As Binding
      For Each b In Me.DataBindings
         Dim cm As CurrencyManager = b.BindingManagerBase
         cm.EndCurrentEdit()
      Next
   End Sub

Visual C# .NET Code

     // Override the OnValidating of a Control 
     protected override void OnValidating(CancelEventArgs e)
     {
       // Iterate through each bound object and then end the current edit
      	foreach (Binding b in this.DataBindings)
     	{
     		CurrencyManager cm =(CurrencyManager)(b.BindingManagerBase);
     		cm.EndCurrentEdit();
     	}
     }

Call EndCurrentEdit in the Validating Event of Each Control That Is Used in UserControl


Add the following code to the Validating event of the control that is used in the UserControl class. For example, when you use a TextBox control in your UserControl class, add the following Validating event code for the TextBox1 control:

Visual Basic .NET Code

   ' Validating event of First control
   Private Sub TextBox1_Validating(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
      m_Property1 = TextBox1.Text
      OnValidating(e)
      ' Iterate through each bound object and then end the current edit
      Dim b As Binding
      For Each b In Me.DataBindings
         Dim cm As CurrencyManager = b.BindingManagerBase
         cm.EndCurrentEdit()
      Next
   End Sub

 Protected Overrides Sub OnValidating(ByVal e As System.ComponentModel.CancelEventArgs)
 End Sub

Visual C# .NET Code

      // Validating event of First control
      private void textBox1_Validating(object sender, System.ComponentModel.CancelEventArgs e)
      {
         m_Property1= textBox1.Text;
         OnValidating(e);
         foreach (Binding b in this.DataBindings)
         {
            CurrencyManager cm =(CurrencyManager)(b.BindingManagerBase);
            cm.EndCurrentEdit();
         }
      }

      protected override void OnValidating(CancelEventArgs e)
      {
      }

STATUS

Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article. This bug was corrected in ADO.NET version 1.1 for data binding.

MORE INFORMATION

Steps to Reproduce the Behavior

  1. Create a new Windows Control Library project by using Microsoft Visual C# .NET or Microsoft Visual Basic .NET. Name the Project MyUserControl. By default, UserControl1 is created.
  2. Drag two TextBox controls from the Toolbox to UserControl1. TextBox1 and TextBox2 are created.
  3. In Solution Explorer, right-click UserControl1, and then click View Code.
  4. Replace the existing code with the following code:

    Visual Basic .NET

    Public Class UserControl1
        Inherits System.Windows.Forms.UserControl
    
    #Region " Windows Form Designer generated code "
    
       Public Sub New()
          MyBase.New()
          InitializeComponent()
       End Sub
    
       'UserControl1 overrides dispose to clean up the component list.
       Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
          If disposing Then
             If Not (components Is Nothing) Then
                components.Dispose()
             End If
          End If
          MyBase.Dispose(disposing)
       End Sub
    
       'Required by the Windows Form Designer
       Private components As System.ComponentModel.IContainer
    
       Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
       Friend WithEvents TextBox2 As System.Windows.Forms.TextBox
       <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
          Me.TextBox1 = New System.Windows.Forms.TextBox()
          Me.TextBox2 = New System.Windows.Forms.TextBox()
          Me.SuspendLayout()
          'TextBox1
          Me.TextBox1.Location = New System.Drawing.Point(8, 8)
          Me.TextBox1.Name = "TextBox1"
          Me.TextBox1.Size = New System.Drawing.Size(96, 20)
          Me.TextBox1.TabIndex = 0
          Me.TextBox1.Text = ""
          'TextBox2
          Me.TextBox2.Location = New System.Drawing.Point(112, 8)
          Me.TextBox2.Name = "TextBox2"
          Me.TextBox2.Size = New System.Drawing.Size(96, 20)
          Me.TextBox2.TabIndex = 1
          Me.TextBox2.Text = ""
          'UserControl1
          Me.BackColor = System.Drawing.SystemColors.ActiveCaption
          Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.TextBox2, Me.TextBox1})
          Me.Name = "UserControl1"
          Me.Size = New System.Drawing.Size(216, 40)
          Me.ResumeLayout(False)
    
       End Sub
    
    #End Region
    
       ' Public Properties
       Private m_Property1 As String
       Private m_Property2 As String
    
       Public Property Property1() As String
          Get
             Return m_Property1
          End Get
          Set(ByVal Value As String)
             m_Property1 = Value
             TextBox1.Text = m_Property1
          End Set
       End Property
    
       Public Property Property2() As String
          Get
             Return m_Property2
          End Get
          Set(ByVal Value As String)
             m_Property2 = Value
             TextBox2.Text = m_Property2
          End Set
       End Property
    
       ' Set the value in the TextBox1 to Property1
       Private Sub TextBox1_Validating(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
          m_Property1 = TextBox1.Text
       End Sub
    
       ' Set the value in the TextBox2 to Property2
       Private Sub TextBox2_Validating(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles TextBox2.Validating
          m_Property2 = TextBox2.Text
       End Sub
    
    End Class
    

    Visual C# .NET

    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Windows.Forms;
    
    namespace MyUserControl
    {
    	public class UserControl1 : System.Windows.Forms.UserControl
    	{
          private System.Windows.Forms.TextBox textBox1;
          private System.Windows.Forms.TextBox textBox2;
    		private System.ComponentModel.Container components = null;
    
    		public UserControl1()
    		{
    			// This call is required by the Windows.Forms Form Designer.
    			InitializeComponent();
    
    			// TODO: Add any initialization after the InitForm call
    
    		}
    
    		protected override void Dispose( bool disposing )
    		{
    			if( disposing )
    			{
    				if( components != null )
    					components.Dispose();
    			}
    			base.Dispose( disposing );
    		}
        #region Component Designer generated code
         private void InitializeComponent()
         {
             this.textBox1 = new System.Windows.Forms.TextBox();
             this.textBox2 = new System.Windows.Forms.TextBox();
             this.SuspendLayout();
             // textBox1
             this.textBox1.Location = new System.Drawing.Point(8, 8);
             this.textBox1.Name = "textBox1";
             this.textBox1.Size = new System.Drawing.Size(88, 20);
             this.textBox1.TabIndex = 0;
             this.textBox1.Text = "";
             this.textBox1.Validating += new System.ComponentModel.CancelEventHandler(this.textBox1_Validating);
             // textBox2
             this.textBox2.Location = new System.Drawing.Point(104, 8);
             this.textBox2.Name = "textBox2";
             this.textBox2.Size = new System.Drawing.Size(88, 20);
             this.textBox2.TabIndex = 1;
             this.textBox2.Text = "";
             this.textBox2.Validating += new System.ComponentModel.CancelEventHandler(this.textBox2_Validating);
             // UserControl1
             this.BackColor = System.Drawing.SystemColors.ActiveCaption;
             this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                          this.textBox2,
                                                                          this.textBox1});
             this.Name = "UserControl1";
             this.Size = new System.Drawing.Size(200, 40);
             this.ResumeLayout(false);
    
          }
    
         #endregion
          // Public Properties
          private string m_Property1;
          private string m_Property2;
    
          public string Property1
          {
             get
             {
                return m_Property1;
             }
             set
             {
                m_Property1 = value;
                textBox1.Text = m_Property1;
             }
          }
    
          public string Property2
          {
             get
             {
                return m_Property2;
             }
             set
             {
                m_Property2 = value;
                textBox2.Text = m_Property2;
             }
          }
    
          // Set the value in the TextBox1 to Property1
          private void textBox1_Validating(object sender, System.ComponentModel.CancelEventArgs e)
          {
             m_Property1 = textBox1.Text;
          }
    
          // Set the value in the TextBox2 to Property2
          private void textBox2_Validating(object sender, System.ComponentModel.CancelEventArgs e)
          {
             m_Property2 = textBox2.Text;
          }
    }
    }
    
  5. On the Build menu, click Build Solution.
  6. On the File menu, point to Add Project, and then click New Project.
  7. Under Project Type, click Visual Basic Projects or Visual C# Projects.
  8. Under Templates, click Windows Application, and then click OK.
  9. In Solution Explorer, right-click the Windows Application project name, and then click Set as StartUp Project.
  10. Drag the UserControl (that you created in step 5) from the Toolbox to Form1.
  11. Drag a DataGrid control from the Toolbox to Form1.
  12. In Design View, double-click Form1 to add code to the Load event of the Form1.
  13. Replace the Form_Load event code with the following code:

    Visual Basic .NET Code

       Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
          Dim dt As DataTable
          ' Create a Datasource
          dt = New DataTable("Student")
          dt.Columns.Add("StudentName", Type.GetType("System.String"))
          dt.Columns.Add("Age", Type.GetType("System.String"))
    
          ' Add data to the table
          Dim dr As DataRow = dt.NewRow()
          dr("StudentName") = "Robert"
          dr("Age") = "15"
          dt.Rows.Add(dr)
    
          dr = dt.NewRow()
          dr("StudentName") = "Mike"
          dr("Age") = "12"
          dt.Rows.Add(dr)
    
          dr = dt.NewRow()
          dr("StudentName") = "Bob"
          dr("Age") = "14"
          dt.Rows.Add(dr)
    
          UserControl11.DataBindings.Add("Property1", dt, "StudentName")
          UserControl11.DataBindings.Add("Property2", dt, "Age")
    
          DataGrid1.DataSource = dt
       End Sub

    Visual C# .NET Code

          
          private void Form1_Load(object sender, System.EventArgs e)
          {
             DataTable dt;
             // Create a Datasource
             dt = new DataTable("Student");
             dt.Columns.Add("StudentName", Type.GetType("System.String"));
             dt.Columns.Add("Age", Type.GetType("System.String"));
    			
             // Add data to the table
             DataRow dr = dt.NewRow();
             dr["StudentName"] = "Robert";
             dr["Age"] = "15";
             dt.Rows.Add(dr);
    
             dr = dt.NewRow();
             dr["StudentName"] = "Mike";
             dr["Age"] = "12";
             dt.Rows.Add(dr);
    
             dr = dt.NewRow();
             dr["StudentName"] = "Bob";
             dr["Age"] = "14";
             dt.Rows.Add(dr);
    
             userControl11.DataBindings.Add("Property1", dt, "StudentName");
             userControl11.DataBindings.Add("Property2", dt, "Age");
    
             dataGrid1.DataSource = dt;
          }
  14. On the Debug menu, click Start to run the application. In the UserControl, the StudentName is displayed as "Robert" and Age as "15". The DataGrid displays the data in the DataTable.
  15. Change the StudentName in the UserControl to "Thomas" and the Age to "20".
  16. Press ENTER. Verify that the StudentName in the DataGrid is modified but the Age is not.

REFERENCES

For additional information, click the following article number to view the article in the Microsoft Knowledge Base:

313482 INFO: Roadmap for Windows Forms Data Binding



Modification Type:MajorLast Reviewed:1/22/2004
Keywords:kbDataview kbDataBinding kbfix kbControl kbSystemData kbCtrl kbWindowsForms KB812918 kbAudDeveloper