Simple Data Binding of two TextBox controls to an array list source. Update Problems with the Te

Hi,

I tried two bind two TextBoxes with simple Data Binding to the same List Source.
The List Source is an array containing 10 objects of classmYSource (See code example). Use Visual Studio 2003 or Visual 2005 -> same result.


textBox3 and textBox4 are bound to the List SourcemyDataSourceList.

richTextBox2 displays the properties of the actual selected DataSource instance in the arraymyDataSourceList.

Change the "Name" via textBox3, set focus with mouse or TAB to another control. The DataSource is correctly updated ( see richTextBox2 , displaying the data source properties ), ok. But TextBox4is not updated, even though textBox4 is bound to same DataSource than textBox3.

So why does it go wrong?
Thanks.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace DataBinding
{

publicclass Form1 : System.Windows.Forms.Form

{

private System.Windows.Forms.ToolBar toolBar1;

private System.Windows.Forms.ToolBarButton toolBarButtonAgeDec;

private System.Windows.Forms.ToolBarButton toolBarButtonAgeInc;

private System.ComponentModel.IContainer components =null;

private System.Windows.Forms.TextBox textBox1;

private System.Windows.Forms.Label labelSourceName;

private System.Windows.Forms.Label labelName;

private System.Windows.Forms.TextBox textBox2;

private System.Windows.Forms.Label labelAlter;

private System.Windows.Forms.NumericUpDown numericUpDownAge;

private System.Windows.Forms.ToolBarButton toolBarButtonClearName;

private System.Windows.Forms.Label label1;

private System.Windows.Forms.Label label3;

private System.Windows.Forms.RichTextBox richTextBox1;

private mySource m_dataSource =new mySource();

private System.Windows.Forms.ToolBarButton toolBarButtonPrev;

private System.Windows.Forms.ToolBarButton toolBarButtonNext;

private System.Windows.Forms.Label label2;

private System.Windows.Forms.NumericUpDown numericUpDownAgeList;

private System.Windows.Forms.Label labelAlterList;

private System.Windows.Forms.Label labelNameList;

private System.Windows.Forms.TextBox textBox4;

private System.Windows.Forms.Label labelSourceNameList;

private System.Windows.Forms.TextBox textBox3;

private System.Windows.Forms.RichTextBox richTextBox2;

private System.Windows.Forms.Label label4;

// Data Source List

private mySource[] myDataSourceList;

public Form1()

{

InitializeComponent();

///////////////////////////////////////////////////////////////////////////////

// left GUI Part: example with one mySource object as data source

///////////////////////////////////////////////////////////////////////////////

this.numericUpDownAge.Value = m_dataSource.Age;

this.updateDataSourceRichText(this, System.EventArgs.Empty );

// Simple Data Binding use m_dataSource.AgeChanged event

numericUpDownAge.DataBindings.Add(new Binding("Value", m_dataSource,"Age" ));

// Simple Data Binding use m_dataSource.NameChanged event

textBox2.DataBindings.Add(new Binding("Text", m_dataSource,"Name" ));

// Simple Data Binding use m_dataSource.NameChanged event

textBox1.DataBindings.Add(new Binding("Text", m_dataSource,"Name" ) );

// Update rich text, when DataSource changed

m_dataSource.AgeChanged +=new System.EventHandler(this.updateDataSourceRichText );

m_dataSource.NameChanged +=new System.EventHandler(this.updateDataSourceRichText );

///////////////////////////////////////////////////////////////////////////////

// rightern GUI part: example with DataSourceList as data source

///////////////////////////////////////////////////////////////////////////////

// fill DataSourceList array

myDataSourceList =new mySource[10 ];

for(int index =0; index < myDataSourceList.Length; index++ )

{

myDataSourceList[ index ] =new mySource();

myDataSourceList[ index ].Name ="Foo " + index;

myDataSourceList[ index ].Age = index +1;

}

// initialize right rich text

this.numericUpDownAgeList.Value = myDataSourceList[0].Age;

this.updateDataSourceListRichText(this, System.EventArgs.Empty );

// Data Binding, subscribe to mySource.AgeChanged event ( done by binding manager )

numericUpDownAgeList.DataBindings.Add("Value", myDataSourceList,"Age" );

// Data Binding, subscribe to mySource.NameChanged event ( done by binding manager )

textBox4.DataBindings.Add("Text", myDataSourceList,"Name" );

// Data Binding, subscribe to mySource.NameChanged event ( done by binding manager )

textBox3.DataBindings.Add("Text", myDataSourceList,"Name" );

// Update rich text, when any property in DataSourceList changed

for(int index =0; index < myDataSourceList.Length; index++ )

{

myDataSourceList[ index ].AgeChanged +=new System.EventHandler(this.updateDataSourceListRichText );

myDataSourceList[ index ].NameChanged +=new System.EventHandler(this.updateDataSourceListRichText );

}

// subscribe to Binding Managers PositionChanged event

BindingManagerBase bm = BindingContext[ myDataSourceList ];

bm.PositionChanged +=new EventHandler(bm_PositionChanged);

}

///<summary>

///

///</summary>

protectedoverridevoid Dispose(bool disposing )

{

if( disposing )

{

if (components !=null)

{

components.Dispose();

}

}

base.Dispose( disposing );

}

#region Vom Windows Form-Designer generierter Code

///<summary>

/// Erforderliche Methode für die Designerunterstützung.

/// Der Inhalt der Methode darf nicht mit dem Code-Editor ge?ndert werden.

///</summary>

privatevoid InitializeComponent()

{

this.toolBar1 =new System.Windows.Forms.ToolBar();

this.toolBarButtonAgeInc =new System.Windows.Forms.ToolBarButton();

this.toolBarButtonAgeDec =new System.Windows.Forms.ToolBarButton();

this.toolBarButtonClearName =new System.Windows.Forms.ToolBarButton();

this.toolBarButtonPrev =new System.Windows.Forms.ToolBarButton();

this.toolBarButtonNext =new System.Windows.Forms.ToolBarButton();

this.textBox1 =new System.Windows.Forms.TextBox();

this.labelSourceName =new System.Windows.Forms.Label();

this.labelName =new System.Windows.Forms.Label();

this.textBox2 =new System.Windows.Forms.TextBox();

this.labelAlter =new System.Windows.Forms.Label();

this.numericUpDownAge =new System.Windows.Forms.NumericUpDown();

this.label1 =new System.Windows.Forms.Label();

this.label3 =new System.Windows.Forms.Label();

this.richTextBox1 =new System.Windows.Forms.RichTextBox();

this.label2 =new System.Windows.Forms.Label();

this.numericUpDownAgeList =new System.Windows.Forms.NumericUpDown();

this.labelAlterList =new System.Windows.Forms.Label();

this.labelNameList =new System.Windows.Forms.Label();

this.textBox4 =new System.Windows.Forms.TextBox();

this.labelSourceNameList =new System.Windows.Forms.Label();

this.textBox3 =new System.Windows.Forms.TextBox();

this.richTextBox2 =new System.Windows.Forms.RichTextBox();

this.label4 =new System.Windows.Forms.Label();

((System.ComponentModel.ISupportInitialize)(this.numericUpDownAge)).BeginInit();

((System.ComponentModel.ISupportInitialize)(this.numericUpDownAgeList)).BeginInit();

this.SuspendLayout();

//

// toolBar1

//

this.toolBar1.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[] {

this.toolBarButtonAgeInc,

this.toolBarButtonAgeDec,

this.toolBarButtonClearName,

this.toolBarButtonPrev,

this.toolBarButtonNext});

this.toolBar1.DropDownArrows =true;

this.toolBar1.Location =new System.Drawing.Point(0,0);

this.toolBar1.Name ="toolBar1";

this.toolBar1.ShowToolTips =true;

this.toolBar1.Size =new System.Drawing.Size(576,42);

this.toolBar1.TabIndex =0;

this.toolBar1.ButtonClick +=new System.Windows.Forms.ToolBarButtonClickEventHandler(this.toolBar1_ButtonClick);

//

// toolBarButtonAgeInc

//

this.toolBarButtonAgeInc.Text ="Age +";

this.toolBarButtonAgeInc.ToolTipText ="Incremet Age in DataSource";

//

// toolBarButtonAgeDec

//

this.toolBarButtonAgeDec.Text ="Age -";

this.toolBarButtonAgeDec.ToolTipText ="Decrement Age in DataSource";

//

// toolBarButtonClearName

//

this.toolBarButtonClearName.Text ="Reset Name";

//

// toolBarButtonPrev

//

this.toolBarButtonPrev.Text ="Prev";

//

// toolBarButtonNext

//

this.toolBarButtonNext.Text ="Next";

//

// textBox1

//

this.textBox1.Location =new System.Drawing.Point(128,164);

this.textBox1.Name ="textBox1";

this.textBox1.Size =new System.Drawing.Size(136,20);

this.textBox1.TabIndex =1;

this.textBox1.Text ="";

//

// labelSourceName

//

this.labelSourceName.Location =new System.Drawing.Point(8,164);

this.labelSourceName.Name ="labelSourceName";

this.labelSourceName.TabIndex =2;

this.labelSourceName.Text ="Name TextBox1";

//

// labelName

//

this.labelName.Location =new System.Drawing.Point(8,196);

this.labelName.Name ="labelName";

this.labelName.TabIndex =4;

this.labelName.Text ="Name TextBox2";

//

// textBox2

//

this.textBox2.Location =new System.Drawing.Point(128,196);

this.textBox2.Name ="textBox2";

this.textBox2.Size =new System.Drawing.Size(136,20);

this.textBox2.TabIndex =3;

this.textBox2.Text ="";

//

// labelAlter

//

this.labelAlter.Location =new System.Drawing.Point(8,228);

this.labelAlter.Name ="labelAlter";

this.labelAlter.TabIndex =6;

this.labelAlter.Text ="Age";

//

// numericUpDownAge

//

this.numericUpDownAge.Location =new System.Drawing.Point(128,228);

this.numericUpDownAge.Name ="numericUpDownAge";

this.numericUpDownAge.Size =new System.Drawing.Size(136,20);

this.numericUpDownAge.TabIndex =7;

//

// label1

//

this.label1.Location =new System.Drawing.Point(0,42);

this.label1.Name ="label1";

this.label1.Size =new System.Drawing.Size(264,23);

this.label1.TabIndex =1;

this.label1.Text ="DataSource";

//

// label3

//

this.label3.Location =new System.Drawing.Point(0,132);

this.label3.Name ="label3";

this.label3.Size =new System.Drawing.Size(304,23);

this.label3.TabIndex =11;

this.label3.Text ="TextBox1,2 Age bound to DataSource";

//

// richTextBox1

//

this.richTextBox1.BackColor = System.Drawing.SystemColors.Info;

this.richTextBox1.Location =new System.Drawing.Point(0,65);

this.richTextBox1.Name ="richTextBox1";

this.richTextBox1.ReadOnly =true;

this.richTextBox1.Size =new System.Drawing.Size(264,47);

this.richTextBox1.TabIndex =2;

this.richTextBox1.TabStop =false;

this.richTextBox1.Text ="";

//

// label2

//

this.label2.Location =new System.Drawing.Point(304,136);

this.label2.Name ="label2";

this.label2.Size =new System.Drawing.Size(304,23);

this.label2.TabIndex =19;

this.label2.Text ="TextBox3,4 Age bound to DataSourceList";

//

// numericUpDownAgeList

//

this.numericUpDownAgeList.Location =new System.Drawing.Point(432,232);

this.numericUpDownAgeList.Name ="numericUpDownAgeList";

this.numericUpDownAgeList.Size =new System.Drawing.Size(136,20);

this.numericUpDownAgeList.TabIndex =18;

//

// labelAlterList

//

this.labelAlterList.Location =new System.Drawing.Point(312,232);

this.labelAlterList.Name ="labelAlterList";

this.labelAlterList.TabIndex =17;

this.labelAlterList.Text ="Age";

//

// labelNameList

//

this.labelNameList.Location =new System.Drawing.Point(312,200);

this.labelNameList.Name ="labelNameList";

this.labelNameList.TabIndex =16;

this.labelNameList.Text ="Name TextBox4";

//

// textBox4

//

this.textBox4.Location =new System.Drawing.Point(432,200);

this.textBox4.Name ="textBox4";

this.textBox4.Size =new System.Drawing.Size(136,20);

this.textBox4.TabIndex =15;

this.textBox4.Text ="";

//

// labelSourceNameList

//

this.labelSourceNameList.Location =new System.Drawing.Point(312,168);

this.labelSourceNameList.Name ="labelSourceNameList";

this.labelSourceNameList.TabIndex =14;

this.labelSourceNameList.Text ="Name TextBox3";

//

// textBox3

//

this.textBox3.Location =new System.Drawing.Point(432,168);

this.textBox3.Name ="textBox3";

this.textBox3.Size =new System.Drawing.Size(136,20);

this.textBox3.TabIndex =13;

this.textBox3.Text ="";

//

// richTextBox2

//

this.richTextBox2.BackColor = System.Drawing.SystemColors.Info;

this.richTextBox2.Location =new System.Drawing.Point(304,64);

this.richTextBox2.Name ="richTextBox2";

this.richTextBox2.ReadOnly =true;

this.richTextBox2.Size =new System.Drawing.Size(264,47);

this.richTextBox2.TabIndex =4;

this.richTextBox2.TabStop =false;

this.richTextBox2.Text ="";

//

// label4

//

this.label4.Location =new System.Drawing.Point(304,40);

this.label4.Name ="label4";

this.label4.Size =new System.Drawing.Size(264,23);

this.label4.TabIndex =3;

this.label4.Text ="DataSourceList";

//

// Form1

//

this.AutoScaleBaseSize =new System.Drawing.Size(5,13);

this.ClientSize =new System.Drawing.Size(576,270);

this.Controls.Add(this.label4);

this.Controls.Add(this.richTextBox2);

this.Controls.Add(this.label2);

this.Controls.Add(this.numericUpDownAgeList);

this.Controls.Add(this.labelAlterList);

this.Controls.Add(this.labelNameList);

this.Controls.Add(this.textBox4);

this.Controls.Add(this.labelSourceNameList);

this.Controls.Add(this.textBox3);

this.Controls.Add(this.richTextBox1);

this.Controls.Add(this.label3);

this.Controls.Add(this.label1);

this.Controls.Add(this.numericUpDownAge);

this.Controls.Add(this.labelAlter);

this.Controls.Add(this.labelName);

this.Controls.Add(this.textBox2);

this.Controls.Add(this.labelSourceName);

this.Controls.Add(this.textBox1);

this.Controls.Add(this.toolBar1);

this.Name ="Form1";

this.Text ="Simple Data Binding";

((System.ComponentModel.ISupportInitialize)(this.numericUpDownAge)).EndInit();

((System.ComponentModel.ISupportInitialize)(this.numericUpDownAgeList)).EndInit();

this.ResumeLayout(false);

}

#endregion

///<summary>

/// Class with properties Name and Age. Used in this example as "Data Source".

///</summary>

publicclass mySource

{

// "property"Changed events where Controls can subscribe to.

publicevent EventHandler AgeChanged;

publicevent EventHandler NameChanged;

privatestring m_name ="";

privateint m_age =0;

// property Age

publicdecimal Age

{

get{return (decimal) m_age; }

set

{

if( System.Decimal.ToInt32(value ) != m_age )

{

int old_value = m_age;

m_age = System.Decimal.ToInt32(value );

// notify subscribers

if(null != AgeChanged )

{

AgeChanged(this,new myIntEventArgs( old_value ));

}

}

}

}

// property Name

publicstring Name

{

get{return m_name; }

set

{

if(value != m_name )

{

string oldName ="";

oldName = m_name.ToString();

m_name =value;

// notify subscribers

if(null != NameChanged )

{

NameChanged(this,new myStringEventArgs( oldName ) );

}

}

}

}

}

publicclass myStringEventArgs : System.EventArgs

{

privatestring m_value;

public myStringEventArgs(string t_value )

{

m_value = t_value;

}

publicstring Content

{

get{return m_value; }

}

}

publicclass myIntEventArgs : System.EventArgs

{

privateint m_value;

public myIntEventArgs(int t_value )

{

m_value = t_value;

}

publicint Content

{

get{return m_value; }

}

}

///<summary>

/// Der Haupteinstiegspunkt für die Anwendung.

///</summary>

[STAThread]

staticvoid Main()

{

Application.Run(new Form1());

}

///<summary>

/// Toolbar click events, used to manipulate the data sources.

///</summary>

///<param name="sender"></param>

///<param name="e"></param>

privatevoid toolBar1_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)

{

ToolBar theBar = (ToolBar)sender;

if(null != theBar )

{

BindingManagerBase bm = BindingContext[ myDataSourceList ];

switch( theBar.Buttons.IndexOf( e.Button ) )

{

case0:// increment Age

int newValue = ( System.Decimal.ToInt32( m_dataSource.Age ) ) +1;

m_dataSource.Age = (decimal ) newValue;

// update DataSourceList

newValue = ( System.Decimal.ToInt32( myDataSourceList[ bm.Position ].Age ) ) +1;

myDataSourceList[ bm.Position ].Age = (decimal ) newValue;

break;

case1:// decrement Age, minimum 0

int actValue = System.Decimal.ToInt32( m_dataSource.Age );

if( actValue >0 )

{

--actValue;

}

else

{

actValue =0;

}

m_dataSource.Age = (decimal ) actValue;

// update DataSourceList

actValue = System.Decimal.ToInt32( myDataSourceList[ bm.Position ].Age );

if( actValue >0 )

{

--actValue;

}

else

{

actValue =0;

}

myDataSourceList[ bm.Position ].Age = (decimal ) actValue;

break;

case2:// reset Name

m_dataSource.Name ="";

// update DataSourceList

myDataSourceList[ bm.Position ].Name ="";

break;

case3:// previous item in DataSourceList

if( bm.Position ==0 )

{

//

}

else

{

bm.Position -=1;

}

break;

case4:// next item in DataSourceList

if( bm.Position == bm.Count -1 )

{

//

}

else

{

bm.Position +=1;

}

break;

default:

break;

}

}

}

///<summary>

/// Update left rich text box, because a property in m_dataSource changed.

///</summary>

///<param name="sender"></param>

///<param name="e"></param>

privatevoid updateDataSourceRichText(object sender, System.EventArgs e)

{

if( m_dataSource.Name.Length >0 )

{

this.richTextBox1.Text ="Name: " + m_dataSource.Name +", Age: " + m_dataSource.Age.ToString();

}

else

{

this.richTextBox1.Text ="Age: " + m_dataSource.Age.ToString();

}

}

///<summary>

/// Update right rich text box, because a property in myDataSourceList changed.

///</summary>

///<param name="sender"></param>

///<param name="e"></param>

privatevoid updateDataSourceListRichText(object sender, System.EventArgs e)

{

mySource src = myDataSourceList[ BindingContext[myDataSourceList].Position ];

if( src.Name.Length >0 )

{

this.richTextBox2.Text ="Name: " + src.Name +", Age: " + src.Age.ToString();

}

else

{

this.richTextBox2.Text ="Age: " + src.Age.ToString();

}

}

///<summary>

/// Update right rich text box, because binding position in myDataSourceList′s BindingManager changed.

///</summary>

///<param name="sender"></param>

///<param name="e"></param>

privatevoid bm_PositionChanged(object sender, EventArgs e)

{

updateDataSourceListRichText( sender, e );

}

}

}

[69427 byte] By [stoffelix] at [2007-12-16]
# 1

Wow - that's a lot of code.

I'm not positive this is the issue, but the likely source of the problem is your list is not providing change notification to the TextBox (textBox4). In other words, textBox4 only updates its value from the data source if the data source tells it that value has changed. In order for this to work your list needs to support the IBindingList interface. The best way to do this is to use the generic IBindingList in VS 2005: System.ComponentModel.BindingList<T> (replace your list with this).

Joe Stegman
The Windows Forms Team
Microsoft Corp.

This posting is provided "AS IS" with no warranties, and confers no rights.

JoeStegman at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...
# 2

Hi Joe,

thanks for your reply. I tried your problem solution with VS 2005. It still does not work. Same behaviour.
I replaced the list

private mySource[] myDataSourceList;

with

private BindingList< mySource> myDataSourceList = new BindingList< mySource>;


filling the list is now like:



for( int index = 0; index < 10; index++ )
{
myDataSourceList.AddNew();
myDataSourceList.EndNew(myDataSourceList.Count - 1);
myDataSourceList[myDataSourceList.Count - 1].Age = index + 1;
myDataSourceList[myDataSourceList.Count - 1].Name = "Foo " + index;
}

Rest of the code remains unchanged.
What else do I have to implement to get it working?
Thanks.

Thomas

stoffelix at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...
# 3

You also need to add the correct type of change notification to your business object (mySource). Rather than user the "property"Changed pattern, you need to support INotifyPropertyChanged - this works with BindingList<T> to provide correct list changed notification. I've tried this in your sample and it appears to work correctly. Here are the mySource changes (you will need to change your application to listen to PropertyChanged rather than "property"Changed events):


public class mySource : INotifyPropertyChanged
{
/* "property"Changed events where Controls can subscribe to. */
private string m_name = "";
private int m_age = 0;

/* property Age */
public decimal Age
{
get { return (decimal)m_age; }
set
{
if (System.Decimal.ToInt32(value) != m_age)
{
int old_value = m_age;
m_age = System.Decimal.ToInt32(value);

/* notify subscribers */
OnPropertyChanged<int>("Age", old_value);
}
}
}

/* property Name */
public string Name
{
get { return m_name; }
set
{
if (value != m_name)
{
string oldName = "";
oldName = m_name.ToString();
m_name = value;

/* notify subscribers */
OnPropertyChanged<string>("Name", oldName);
}
}
}

#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged<T>(string propertyName, T oldValue)
{
OnPropertyChanged(new TypedPropertyChangedEventArgs<T>(propertyName, oldValue));
}

protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
if (null != this.PropertyChanged)
{
PropertyChanged(this, args);
}
}
#endregion
}

#region TypedPropertyChangedEventArgs
public class TypedPropertyChangedEventArgs<T> : System.ComponentModel.PropertyChangedEventArgs
{
private T m_value;

public TypedPropertyChangedEventArgs(string propertyName, T t_value) : base(propertyName)
{
m_value = t_value;
}

public T Content
{
get { return m_value; }
}
}
#endregion

Joe Stegman
The Windows Forms Team
Microsoft Corp.

This posting is provided "AS IS" with no warranties, and confers no rights.

JoeStegman at 2007-9-9 > top of Msdn Tech,Windows Forms,Windows Forms Data Controls and Databinding...