ComboBox ItemData?

  • Thread starter Thread starter Tim Johnson
  • Start date Start date
T

Tim Johnson

I'm coming from MFC-land where a combobox had an ItemDataPtr property,
whereby you could stash away a ptr to some arbitrary object. Then when the
user selected an item, you could fetch the object back based on the selected
index. Is there anything comparable in C#? The only samples I've seen show
them managing their own separate arraylist corresponding to the items in the
combobox - yechh!
 
You can bind the ComboBox to any custom object collection which exposes at
least the IEnumerable interface. You can then retrieve one of these custom
objects using the SelectedValue property. Another alternative is to use a
DataTable which you have filled from a database. You can specify which
property/field is used to display in the ComboBox as by default it will use
the objects ToString method. For a Customer you might use
comboBox1.DisplayMember = "CustomerName";
comboBox1.DataSource = customers;

Peter

--
Peter Foot
Windows Embedded MVP
www.inthehand.com | www.opennetcf.org

Do have an opinion on the effectiveness of Microsoft Windows Mobile and
Embedded newsgroups? Let us know!
https://www.windowsembeddedeval.com/community/newsgroups
 
In CF you need to approach this from the other end. Instead of adding
strings, add objects (or structures). The Combobox will use .ToString() on
each item to display it. You can create a structure, overrride its
ToString() and use it to populate combobox.
Alternatively an ArrayList of objects/strctures can be used as DataSource.
In this case the DisplayMember and ValueMember will be property names to
retrieve from the element
Finally use Combobox.SelectedItem to retrieve the selected item
 
Tim,

I use the ArrayList of objects approach described by Alex. The objects I use
like this all have a property called DataObject that looks like this:

public Customer DataObject
{ get{return tnis;}}

so I can just set the ValueMember of the combo box to DataObject and get
back the data object itself.
 
Thanks for all the replies. From all your answers I went for a simpler
approach (to me anyway) of loading my existing objects into the combobox and
overriding the class's ToString to give back an existing string field that
identies the object to the user:

//uses newdevice's ToString override to get display text
cboDevices.Items.Add(newdevice);

Then in the SelectedIndexChanged method I do this:

myDevice = (MyDevice) cboDevices.SelectedItem;

So I'm not using the SelectedValue as suggested, nor the DataObject idea,
but this seems to work fine.

--
Tim Johnson
High Point Software
www.high-point.com
(503) 312-8625
 
Ginny, if you use the ArrayList of objects method, do you have any trouble
with removing items from the list?

I Add a bunch of objects to my list and set the DataSource of the combobox
to the list. All items are displayed (according to the DisplayMember of the
combo). If I select the _last_ item in the list, and remove it. The combobox
will change to SelectedIndex -1.

private void btnDelete_Click(object sender, System.EventArgs e)
{
if( cmbBoxItems.SelectedIndex != -1 )
{
lst.Remove( (MyClass)cmbBoxItems.SelectedItem );
ResetDataBinding();
}
}

After this I need to reset the DataSource since my ArrayList doesn't
implement IBindingList and doesn't propagate the changes automatically to
the combobox. I set DataSource to null and then reset it to the list again.

The combo is updated and now contains 1 object less than before. If I now
select the last item... CRASH! ArgumentOutOfRangeException is thrown.

If I change the delete handler to the following, I don't get an exception
however:

private void btnDelete_Click(object sender, System.EventArgs e)
{
int indx = cmbBoxItems.SelectedIndex;
if( indx != -1 )
{
if( indx == cmbBoxItems.Items.Count - 1 && cmbBoxItems.Items.Count >
1 ) // Last obj
{
MyClass mc = (MyClass)cmbBoxItems.SelectedItem;
cmbBoxItems.SelectedIndex--;
lst.Remove( mc );
}
else
lst.Remove( (MyClass)cmbBoxItems.SelectedItem );

ResetDataBinding();
}
}

Is it neccessary to perform the actions showed in the second code snippet. I
prevent the SelectedIndex = -1, but that's about the only difference... I
guess this is a bug somewhere in either ComboBox or ArrayList...

regards,

Peter
 
Peter,

The business objects I use never get deleted, so I never ran into the
situation you describe. If I had though, I think I would have used the same
solution that you did. With data binding particularly, and not just .Net
data binding but every kind I've ever used, there always seems to be
somewhat of a trial-and-error phase until I see what it really does.

--
Ginny Caughey
..Net Compact Framework MVP
 
//uses newdevice's ToString override to get display text
cboDevices.Items.Add(newdevice);

Then in the SelectedIndexChanged method I do this:

myDevice = (MyDevice) cboDevices.SelectedItem;

So I'm not using the SelectedValue as suggested, nor the DataObject idea,
but this seems to work fine.

yes! That's what i do too! Instead of thinking of an item-string simply
think of an item-object (that can display itself as a String)

works very well and code is readable.

Boris
 
Hi Ginny!

You are lucky, I have found out that most of the trouble started when I
began to delete objects in the list. Especially when deleting the last
object. But finally I got it all working as well.

The declarations of the controls databinding is done in the following way:

m_lst = new ArrayList(); // List of all objects of type MyClass
this.cmbBoxItems.DisplayMember = "ID"; // Main combobox - ID is a property
of MyClass
this.cmbBoxItems.DataSource = m_lst; // Uses m_lst as datasource
// Child controls only displaying the data for each object selected in the
combobox
this.txtId.DataBindings.Add(new Binding("Text", m_lst, "ID"));
this.txtDesc.DataBindings.Add(new Binding("Text", m_lst, "Description"));
Description is a property of MyClass
this.comboBox1.DataBindings.Add(new Binding("Text", m_lst, "ID"));
this.comboBox2.DataBindings.Add(new Binding("Text", m_lst, "Description"));

Is this the correct way of binding your datasource and child-controls??

I find it somewhat "magical" how the child-controls "know" which object in
m_lst is the one selected in the main combobox as the databindings doesn't
connect anything to the main combobox... do you have an answer for this?

The extra textboxes and comboboxes could of course contain data of other
fields than ID and Description.

There are also a few things I have noticed:

1. Must create object in list before adding databindings
Before I can add databindings to the controls, and bind the cmbbox to the
ArrayList I need atleast 1 object in the list. I solve this by adding the
databinding after the first object is added to the list. This may not be so
strange, the binding need to know the object it binds to in the list, but
still annoying :-)

2. If all objects are removed from the list, databindings must be removed
before the list is cleared.
If I remove the last object in the list I need to call DataBindings.Clear()
on the controls bound to the list before I clear it. I can't call the
DataBindings.Clear() method after the list is emptied, which I need to do in
order to re-enable databinding when objects are added again. It also seems
like the last object in the Main combobox isn't removed (see 3.)

3. Removing the last objects imposes a slight delay.
When the main ComboBox's DataSource property is set to a empty list there
seem to be a slight (but annoying) delay. I reset the DataSource by setting
it to null, and then pointing it back to the updated list
(removed/added/changed object in it). However it seems
like setting the DataSource to null doesn't clear the ComboBox's item list.
I solved this by manually clearing the combobox instead of setting the
datasource to the empty ArrayList to circumvent the annoying delay.

Since you never delete objects I suppose you never really noticed these
things. But could be good to know for future projects!

regards,

Peter


Here's the source for my test application if anyone would be interested:

using System;

using System.Drawing;

using System.Collections;

using System.Windows.Forms;

using System.Data;

namespace ComboBoxArrayListBindingTest

{

/// <summary>

/// Summary description for Form1.

/// </summary>

public class Form1 : System.Windows.Forms.Form

{

#region Members

private System.Windows.Forms.Button btnAdd;

private System.Windows.Forms.Button btnDelete;

private System.Windows.Forms.ComboBox cmbBoxItems;

private System.Windows.Forms.TextBox txtId;

private System.Windows.Forms.TextBox txtDesc;

private System.Windows.Forms.Button btnChange;

private System.Windows.Forms.MainMenu mainMenu1;

private System.Windows.Forms.ComboBox comboBox1;

private System.Windows.Forms.ComboBox comboBox2;

ArrayList m_lst;

string strDisplayMember = "ID";

#endregion

#region Constructor & Disposer

public Form1()

{

//

// Required for Windows Form Designer support

//

InitializeComponent();

m_lst = new ArrayList();

}

/// <summary>

/// Clean up any resources being used.

/// </summary>

protected override void Dispose( bool disposing )

{

base.Dispose( disposing );

}

#endregion

#region Windows Form Designer generated code

/// <summary>

/// Required method for Designer support - do not modify

/// the contents of this method with the code editor.

/// </summary>

private void InitializeComponent()

{

this.mainMenu1 = new System.Windows.Forms.MainMenu();

this.btnAdd = new System.Windows.Forms.Button();

this.cmbBoxItems = new System.Windows.Forms.ComboBox();

this.btnDelete = new System.Windows.Forms.Button();

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

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

this.btnChange = new System.Windows.Forms.Button();

this.comboBox1 = new System.Windows.Forms.ComboBox();

this.comboBox2 = new System.Windows.Forms.ComboBox();

//

// btnAdd

//

this.btnAdd.Location = new System.Drawing.Point(20, 176);

this.btnAdd.Size = new System.Drawing.Size(64, 20);

this.btnAdd.Text = "Add";

this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);

//

// cmbBoxItems

//

this.cmbBoxItems.Location = new System.Drawing.Point(40, 32);

this.cmbBoxItems.Size = new System.Drawing.Size(140, 21);

//

// btnDelete

//

this.btnDelete.Location = new System.Drawing.Point(164, 176);

this.btnDelete.Size = new System.Drawing.Size(64, 20);

this.btnDelete.Text = "Delete";

this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click);

//

// txtId

//

this.txtId.Location = new System.Drawing.Point(40, 72);

this.txtId.Text = "ID";

//

// txtDesc

//

this.txtDesc.Location = new System.Drawing.Point(40, 96);

this.txtDesc.Size = new System.Drawing.Size(184, 20);

this.txtDesc.Text = "Description";

//

// btnChange

//

this.btnChange.Location = new System.Drawing.Point(92, 176);

this.btnChange.Size = new System.Drawing.Size(64, 20);

this.btnChange.Text = "Change";

this.btnChange.Click += new System.EventHandler(this.btnChange_Click);

//

// comboBox1

//

this.comboBox1.Items.Add("001");

this.comboBox1.Items.Add("002");

this.comboBox1.Items.Add("003");

this.comboBox1.Items.Add("004");

this.comboBox1.Items.Add("005");

this.comboBox1.Items.Add("006");

this.comboBox1.Items.Add("007");

this.comboBox1.Items.Add("008");

this.comboBox1.Location = new System.Drawing.Point(40, 124);

this.comboBox1.Size = new System.Drawing.Size(188, 21);

//

// comboBox2

//

this.comboBox2.Items.Add("Detta är objekt 1");

this.comboBox2.Items.Add("Detta är objekt 2");

this.comboBox2.Items.Add("Detta är objekt 3");

this.comboBox2.Items.Add("Detta är objekt 4");

this.comboBox2.Items.Add("Detta är objekt 5");

this.comboBox2.Items.Add("Detta är objekt 6");

this.comboBox2.Items.Add("Detta är objekt 7");

this.comboBox2.Items.Add("Detta är objekt 8");

this.comboBox2.Location = new System.Drawing.Point(40, 148);

this.comboBox2.Size = new System.Drawing.Size(188, 21);

//

// Form1

//

this.Controls.Add(this.comboBox2);

this.Controls.Add(this.comboBox1);

this.Controls.Add(this.btnChange);

this.Controls.Add(this.txtDesc);

this.Controls.Add(this.txtId);

this.Controls.Add(this.btnDelete);

this.Controls.Add(this.cmbBoxItems);

this.Controls.Add(this.btnAdd);

this.Menu = this.mainMenu1;

this.Text = "Form1";

}

#endregion

/// <summary>

/// The main entry point for the application.

/// </summary>

static void Main()

{

Application.Run(new Form1());

}



#region Main Methods

private void AddObject()

{

int iNewId;

string strNewId;

if( m_lst.Count == 0 )

{

iNewId = 1;

strNewId = "001";

}

else

{

iNewId = int.Parse(((MyClass)m_lst[m_lst.Count-1]).ID) + 1;

strNewId = iNewId.ToString().PadLeft(3, '0');

}

MyClass mc = new MyClass();

mc.ID = strNewId;

mc.Description = "Detta är objekt " + iNewId.ToString();

m_lst.Add(mc);

if( m_lst.Count == 1 ) // First object added

{

// We need to add databindings

AddBindings();

}

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

// Select the last(new) object

this.cmbBoxItems.SelectedIndex = this.cmbBoxItems.Items.Count - 1;

}

private void DeleteObject()

{

DisableButtons();

int indx = cmbBoxItems.SelectedIndex;

if( indx != -1 ) // List not empty

{

MyClass mc = (MyClass)cmbBoxItems.SelectedItem;

if( cmbBoxItems.Items.Count == 1 ) //Last object

{

// Must remove bindings before we remove last item

RemoveBindings();

m_lst.Remove( mc );

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

this.txtId.Text = string.Empty;

this.txtDesc.Text = string.Empty;

this.comboBox1.SelectedIndex = -1;

this.comboBox2.SelectedIndex = -1;

}

else if( indx == 0 ) //First obj

{

m_lst.Remove( mc );

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

mc = (MyClass)cmbBoxItems.SelectedItem;

this.txtId.Text = mc.ID;

this.txtDesc.Text = mc.Description;

}

else

{

cmbBoxItems.SelectedIndex--;

m_lst.Remove( mc );

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

}

}

EnableButtons();

}

private void SaveObject()

{

if( this.cmbBoxItems.SelectedIndex != -1 )

{

DisableButtons();


MyClass mc = (MyClass)cmbBoxItems.SelectedItem;

mc.ID = this.txtId.Text;

mc.Description = this.txtDesc.Text;

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);


EnableButtons();

}

}

private void ResetDataSource( ComboBox cbx, IList lst, string strDispMem )

{

// As ArrayList doesn't implement IBindingList it doesn't propagate

// changes to the controls that is bound to it. We need to reset the

// combobox DataSource property for each change to the list.


cbx.Visible = false; // No flickering please


cbx.DataSource = null;

if( lst.Count != 0 )

{

cbx.DisplayMember = strDispMem;

cbx.DataSource = lst;

}

else

cbx.Items.Clear(); // Force manual clearing of list


cbx.Visible = true;

}

private void AddBindings()

{

this.txtId.DataBindings.Add(new Binding("Text", m_lst, "ID"));

this.txtDesc.DataBindings.Add(new Binding("Text", m_lst, "Description"));

this.comboBox1.DataBindings.Add(new Binding("Text", m_lst, "ID"));

this.comboBox2.DataBindings.Add(new Binding("Text", m_lst, "Description"));

}

private void RemoveBindings()

{

this.txtId.DataBindings.Clear();

this.txtDesc.DataBindings.Clear();

this.comboBox1.DataBindings.Clear();

this.comboBox2.DataBindings.Clear();

}

private void DisableButtons()

{

this.btnAdd.Enabled = false;

this.btnChange.Enabled = false;

this.btnDelete.Enabled = false;

}

private void EnableButtons()

{

this.btnAdd.Enabled = true;

this.btnChange.Enabled = true;

this.btnDelete.Enabled = true;

}


#endregion

#region EventHandlers

private void btnAdd_Click(object sender, System.EventArgs e)

{

DisableButtons();

AddObject();

EnableButtons();

}

private void btnDelete_Click(object sender, System.EventArgs e)

{

DeleteObject();

}

private void btnChange_Click(object sender, System.EventArgs e)

{

SaveObject();

}

#endregion

}

public class MyClass

{

string m_id;

string m_desc;

public string ID

{

get{ return m_id; }

set{ m_id = value; }

}

public string Description

{

get{ return m_desc; }

set{ m_desc = value; }

}

}

}
 
Peter,

The only thing I saw in your code is that you are using "simple" databinding
for two of the comboboxes. This would be correct for textboxes (and your
code for the textboxes looks fine), but I have always used "complex"
databinding for comboboxes since they contain multiple (complex) data just
as you did for the cmbBoxItems combobox:

this.comboBox1.DataSource = m_lst;
this.comboBox1.DisplayMember = "ID";
this.comboBox2.DataSource = m_lst;
this.comboBox2.DisplayMember = "Description";

As long as all three comboBoxes use the same object for the DataSource, they
should stay in sync, and so should the textboxes which are also bound to
m_lst as the data source.

--
Ginny Caughey
..Net Compact Framework MVP



PeterB said:
Hi Ginny!

You are lucky, I have found out that most of the trouble started when I
began to delete objects in the list. Especially when deleting the last
object. But finally I got it all working as well.

The declarations of the controls databinding is done in the following way:

m_lst = new ArrayList(); // List of all objects of type MyClass
this.cmbBoxItems.DisplayMember = "ID"; // Main combobox - ID is a property
of MyClass
this.cmbBoxItems.DataSource = m_lst; // Uses m_lst as datasource
// Child controls only displaying the data for each object selected in the
combobox
this.txtId.DataBindings.Add(new Binding("Text", m_lst, "ID"));
this.txtDesc.DataBindings.Add(new Binding("Text", m_lst, "Description"));
Description is a property of MyClass
this.comboBox1.DataBindings.Add(new Binding("Text", m_lst, "ID"));
this.comboBox2.DataBindings.Add(new Binding("Text", m_lst,
"Description"));

Is this the correct way of binding your datasource and child-controls??

I find it somewhat "magical" how the child-controls "know" which object in
m_lst is the one selected in the main combobox as the databindings doesn't
connect anything to the main combobox... do you have an answer for this?

The extra textboxes and comboboxes could of course contain data of other
fields than ID and Description.

There are also a few things I have noticed:

1. Must create object in list before adding databindings
Before I can add databindings to the controls, and bind the cmbbox to the
ArrayList I need atleast 1 object in the list. I solve this by adding the
databinding after the first object is added to the list. This may not be
so strange, the binding need to know the object it binds to in the list,
but still annoying :-)

2. If all objects are removed from the list, databindings must be removed
before the list is cleared.
If I remove the last object in the list I need to call
DataBindings.Clear() on the controls bound to the list before I clear it.
I can't call the DataBindings.Clear() method after the list is emptied,
which I need to do in order to re-enable databinding when objects are
added again. It also seems like the last object in the Main combobox isn't
removed (see 3.)

3. Removing the last objects imposes a slight delay.
When the main ComboBox's DataSource property is set to a empty list there
seem to be a slight (but annoying) delay. I reset the DataSource by
setting it to null, and then pointing it back to the updated list
(removed/added/changed object in it). However it seems
like setting the DataSource to null doesn't clear the ComboBox's item
list. I solved this by manually clearing the combobox instead of setting
the datasource to the empty ArrayList to circumvent the annoying delay.

Since you never delete objects I suppose you never really noticed these
things. But could be good to know for future projects!

regards,

Peter


Here's the source for my test application if anyone would be interested:

using System;

using System.Drawing;

using System.Collections;

using System.Windows.Forms;

using System.Data;

namespace ComboBoxArrayListBindingTest

{

/// <summary>

/// Summary description for Form1.

/// </summary>

public class Form1 : System.Windows.Forms.Form

{

#region Members

private System.Windows.Forms.Button btnAdd;

private System.Windows.Forms.Button btnDelete;

private System.Windows.Forms.ComboBox cmbBoxItems;

private System.Windows.Forms.TextBox txtId;

private System.Windows.Forms.TextBox txtDesc;

private System.Windows.Forms.Button btnChange;

private System.Windows.Forms.MainMenu mainMenu1;

private System.Windows.Forms.ComboBox comboBox1;

private System.Windows.Forms.ComboBox comboBox2;

ArrayList m_lst;

string strDisplayMember = "ID";

#endregion

#region Constructor & Disposer

public Form1()

{

//

// Required for Windows Form Designer support

//

InitializeComponent();

m_lst = new ArrayList();

}

/// <summary>

/// Clean up any resources being used.

/// </summary>

protected override void Dispose( bool disposing )

{

base.Dispose( disposing );

}

#endregion

#region Windows Form Designer generated code

/// <summary>

/// Required method for Designer support - do not modify

/// the contents of this method with the code editor.

/// </summary>

private void InitializeComponent()

{

this.mainMenu1 = new System.Windows.Forms.MainMenu();

this.btnAdd = new System.Windows.Forms.Button();

this.cmbBoxItems = new System.Windows.Forms.ComboBox();

this.btnDelete = new System.Windows.Forms.Button();

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

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

this.btnChange = new System.Windows.Forms.Button();

this.comboBox1 = new System.Windows.Forms.ComboBox();

this.comboBox2 = new System.Windows.Forms.ComboBox();

//

// btnAdd

//

this.btnAdd.Location = new System.Drawing.Point(20, 176);

this.btnAdd.Size = new System.Drawing.Size(64, 20);

this.btnAdd.Text = "Add";

this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);

//

// cmbBoxItems

//

this.cmbBoxItems.Location = new System.Drawing.Point(40, 32);

this.cmbBoxItems.Size = new System.Drawing.Size(140, 21);

//

// btnDelete

//

this.btnDelete.Location = new System.Drawing.Point(164, 176);

this.btnDelete.Size = new System.Drawing.Size(64, 20);

this.btnDelete.Text = "Delete";

this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click);

//

// txtId

//

this.txtId.Location = new System.Drawing.Point(40, 72);

this.txtId.Text = "ID";

//

// txtDesc

//

this.txtDesc.Location = new System.Drawing.Point(40, 96);

this.txtDesc.Size = new System.Drawing.Size(184, 20);

this.txtDesc.Text = "Description";

//

// btnChange

//

this.btnChange.Location = new System.Drawing.Point(92, 176);

this.btnChange.Size = new System.Drawing.Size(64, 20);

this.btnChange.Text = "Change";

this.btnChange.Click += new System.EventHandler(this.btnChange_Click);

//

// comboBox1

//

this.comboBox1.Items.Add("001");

this.comboBox1.Items.Add("002");

this.comboBox1.Items.Add("003");

this.comboBox1.Items.Add("004");

this.comboBox1.Items.Add("005");

this.comboBox1.Items.Add("006");

this.comboBox1.Items.Add("007");

this.comboBox1.Items.Add("008");

this.comboBox1.Location = new System.Drawing.Point(40, 124);

this.comboBox1.Size = new System.Drawing.Size(188, 21);

//

// comboBox2

//

this.comboBox2.Items.Add("Detta är objekt 1");

this.comboBox2.Items.Add("Detta är objekt 2");

this.comboBox2.Items.Add("Detta är objekt 3");

this.comboBox2.Items.Add("Detta är objekt 4");

this.comboBox2.Items.Add("Detta är objekt 5");

this.comboBox2.Items.Add("Detta är objekt 6");

this.comboBox2.Items.Add("Detta är objekt 7");

this.comboBox2.Items.Add("Detta är objekt 8");

this.comboBox2.Location = new System.Drawing.Point(40, 148);

this.comboBox2.Size = new System.Drawing.Size(188, 21);

//

// Form1

//

this.Controls.Add(this.comboBox2);

this.Controls.Add(this.comboBox1);

this.Controls.Add(this.btnChange);

this.Controls.Add(this.txtDesc);

this.Controls.Add(this.txtId);

this.Controls.Add(this.btnDelete);

this.Controls.Add(this.cmbBoxItems);

this.Controls.Add(this.btnAdd);

this.Menu = this.mainMenu1;

this.Text = "Form1";

}

#endregion

/// <summary>

/// The main entry point for the application.

/// </summary>

static void Main()

{

Application.Run(new Form1());

}



#region Main Methods

private void AddObject()

{

int iNewId;

string strNewId;

if( m_lst.Count == 0 )

{

iNewId = 1;

strNewId = "001";

}

else

{

iNewId = int.Parse(((MyClass)m_lst[m_lst.Count-1]).ID) + 1;

strNewId = iNewId.ToString().PadLeft(3, '0');

}

MyClass mc = new MyClass();

mc.ID = strNewId;

mc.Description = "Detta är objekt " + iNewId.ToString();

m_lst.Add(mc);

if( m_lst.Count == 1 ) // First object added

{

// We need to add databindings

AddBindings();

}

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

// Select the last(new) object

this.cmbBoxItems.SelectedIndex = this.cmbBoxItems.Items.Count - 1;

}

private void DeleteObject()

{

DisableButtons();

int indx = cmbBoxItems.SelectedIndex;

if( indx != -1 ) // List not empty

{

MyClass mc = (MyClass)cmbBoxItems.SelectedItem;

if( cmbBoxItems.Items.Count == 1 ) //Last object

{

// Must remove bindings before we remove last item

RemoveBindings();

m_lst.Remove( mc );

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

this.txtId.Text = string.Empty;

this.txtDesc.Text = string.Empty;

this.comboBox1.SelectedIndex = -1;

this.comboBox2.SelectedIndex = -1;

}

else if( indx == 0 ) //First obj

{

m_lst.Remove( mc );

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

mc = (MyClass)cmbBoxItems.SelectedItem;

this.txtId.Text = mc.ID;

this.txtDesc.Text = mc.Description;

}

else

{

cmbBoxItems.SelectedIndex--;

m_lst.Remove( mc );

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

}

}

EnableButtons();

}

private void SaveObject()

{

if( this.cmbBoxItems.SelectedIndex != -1 )

{

DisableButtons();


MyClass mc = (MyClass)cmbBoxItems.SelectedItem;

mc.ID = this.txtId.Text;

mc.Description = this.txtDesc.Text;

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);


EnableButtons();

}

}

private void ResetDataSource( ComboBox cbx, IList lst, string strDispMem )

{

// As ArrayList doesn't implement IBindingList it doesn't propagate

// changes to the controls that is bound to it. We need to reset the

// combobox DataSource property for each change to the list.


cbx.Visible = false; // No flickering please


cbx.DataSource = null;

if( lst.Count != 0 )

{

cbx.DisplayMember = strDispMem;

cbx.DataSource = lst;

}

else

cbx.Items.Clear(); // Force manual clearing of list


cbx.Visible = true;

}

private void AddBindings()

{

this.txtId.DataBindings.Add(new Binding("Text", m_lst, "ID"));

this.txtDesc.DataBindings.Add(new Binding("Text", m_lst, "Description"));

this.comboBox1.DataBindings.Add(new Binding("Text", m_lst, "ID"));

this.comboBox2.DataBindings.Add(new Binding("Text", m_lst,
"Description"));

}

private void RemoveBindings()

{

this.txtId.DataBindings.Clear();

this.txtDesc.DataBindings.Clear();

this.comboBox1.DataBindings.Clear();

this.comboBox2.DataBindings.Clear();

}

private void DisableButtons()

{

this.btnAdd.Enabled = false;

this.btnChange.Enabled = false;

this.btnDelete.Enabled = false;

}

private void EnableButtons()

{

this.btnAdd.Enabled = true;

this.btnChange.Enabled = true;

this.btnDelete.Enabled = true;

}


#endregion

#region EventHandlers

private void btnAdd_Click(object sender, System.EventArgs e)

{

DisableButtons();

AddObject();

EnableButtons();

}

private void btnDelete_Click(object sender, System.EventArgs e)

{

DeleteObject();

}

private void btnChange_Click(object sender, System.EventArgs e)

{

SaveObject();

}

#endregion

}

public class MyClass

{

string m_id;

string m_desc;

public string ID

{

get{ return m_id; }

set{ m_id = value; }

}

public string Description

{

get{ return m_desc; }

set{ m_desc = value; }

}

}

}
 
Hi Ginny!

The idea with the comboboxes was to use them as lists of choosable items.
I.e. the possible values the property bound to a combobox could have, should
be locked. I don't want the comboboxes to be filled with the data from the
datasource (i.e. ArrayList). They should just display the current value of
the property.

For instance, in the code I sent, if you change the text in the textboxes
and press change, the underlying object in the list will change with
whatever you wrote in the textbox. The idea is that you could use the two
comboboxes in a similar fashion but you have a set of selectable choices
(instead of own typed text).

The child comboboxes must of course be pre-filled with selectable data
(matching the data in the objects) before the datasource is applied to the
main combobox.

regards,

Peter


Ginny Caughey said:
Peter,

The only thing I saw in your code is that you are using "simple" databinding
for two of the comboboxes. This would be correct for textboxes (and your
code for the textboxes looks fine), but I have always used "complex"
databinding for comboboxes since they contain multiple (complex) data just
as you did for the cmbBoxItems combobox:

this.comboBox1.DataSource = m_lst;
this.comboBox1.DisplayMember = "ID";
this.comboBox2.DataSource = m_lst;
this.comboBox2.DisplayMember = "Description";

As long as all three comboBoxes use the same object for the DataSource, they
should stay in sync, and so should the textboxes which are also bound to
m_lst as the data source.

--
Ginny Caughey
.Net Compact Framework MVP



PeterB said:
Hi Ginny!

You are lucky, I have found out that most of the trouble started when I
began to delete objects in the list. Especially when deleting the last
object. But finally I got it all working as well.

The declarations of the controls databinding is done in the following way:

m_lst = new ArrayList(); // List of all objects of type MyClass
this.cmbBoxItems.DisplayMember = "ID"; // Main combobox - ID is a property
of MyClass
this.cmbBoxItems.DataSource = m_lst; // Uses m_lst as datasource
// Child controls only displaying the data for each object selected in the
combobox
this.txtId.DataBindings.Add(new Binding("Text", m_lst, "ID"));
this.txtDesc.DataBindings.Add(new Binding("Text", m_lst, "Description"));
Description is a property of MyClass
this.comboBox1.DataBindings.Add(new Binding("Text", m_lst, "ID"));
this.comboBox2.DataBindings.Add(new Binding("Text", m_lst,
"Description"));

Is this the correct way of binding your datasource and child-controls??

I find it somewhat "magical" how the child-controls "know" which object in
m_lst is the one selected in the main combobox as the databindings doesn't
connect anything to the main combobox... do you have an answer for this?

The extra textboxes and comboboxes could of course contain data of other
fields than ID and Description.

There are also a few things I have noticed:

1. Must create object in list before adding databindings
Before I can add databindings to the controls, and bind the cmbbox to the
ArrayList I need atleast 1 object in the list. I solve this by adding the
databinding after the first object is added to the list. This may not be
so strange, the binding need to know the object it binds to in the list,
but still annoying :-)

2. If all objects are removed from the list, databindings must be removed
before the list is cleared.
If I remove the last object in the list I need to call
DataBindings.Clear() on the controls bound to the list before I clear it.
I can't call the DataBindings.Clear() method after the list is emptied,
which I need to do in order to re-enable databinding when objects are
added again. It also seems like the last object in the Main combobox isn't
removed (see 3.)

3. Removing the last objects imposes a slight delay.
When the main ComboBox's DataSource property is set to a empty list there
seem to be a slight (but annoying) delay. I reset the DataSource by
setting it to null, and then pointing it back to the updated list
(removed/added/changed object in it). However it seems
like setting the DataSource to null doesn't clear the ComboBox's item
list. I solved this by manually clearing the combobox instead of setting
the datasource to the empty ArrayList to circumvent the annoying delay.

Since you never delete objects I suppose you never really noticed these
things. But could be good to know for future projects!

regards,

Peter


Here's the source for my test application if anyone would be interested:

using System;

using System.Drawing;

using System.Collections;

using System.Windows.Forms;

using System.Data;

namespace ComboBoxArrayListBindingTest

{

/// <summary>

/// Summary description for Form1.

/// </summary>

public class Form1 : System.Windows.Forms.Form

{

#region Members

private System.Windows.Forms.Button btnAdd;

private System.Windows.Forms.Button btnDelete;

private System.Windows.Forms.ComboBox cmbBoxItems;

private System.Windows.Forms.TextBox txtId;

private System.Windows.Forms.TextBox txtDesc;

private System.Windows.Forms.Button btnChange;

private System.Windows.Forms.MainMenu mainMenu1;

private System.Windows.Forms.ComboBox comboBox1;

private System.Windows.Forms.ComboBox comboBox2;

ArrayList m_lst;

string strDisplayMember = "ID";

#endregion

#region Constructor & Disposer

public Form1()

{

//

// Required for Windows Form Designer support

//

InitializeComponent();

m_lst = new ArrayList();

}

/// <summary>

/// Clean up any resources being used.

/// </summary>

protected override void Dispose( bool disposing )

{

base.Dispose( disposing );

}

#endregion

#region Windows Form Designer generated code

/// <summary>

/// Required method for Designer support - do not modify

/// the contents of this method with the code editor.

/// </summary>

private void InitializeComponent()

{

this.mainMenu1 = new System.Windows.Forms.MainMenu();

this.btnAdd = new System.Windows.Forms.Button();

this.cmbBoxItems = new System.Windows.Forms.ComboBox();

this.btnDelete = new System.Windows.Forms.Button();

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

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

this.btnChange = new System.Windows.Forms.Button();

this.comboBox1 = new System.Windows.Forms.ComboBox();

this.comboBox2 = new System.Windows.Forms.ComboBox();

//

// btnAdd

//

this.btnAdd.Location = new System.Drawing.Point(20, 176);

this.btnAdd.Size = new System.Drawing.Size(64, 20);

this.btnAdd.Text = "Add";

this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);

//

// cmbBoxItems

//

this.cmbBoxItems.Location = new System.Drawing.Point(40, 32);

this.cmbBoxItems.Size = new System.Drawing.Size(140, 21);

//

// btnDelete

//

this.btnDelete.Location = new System.Drawing.Point(164, 176);

this.btnDelete.Size = new System.Drawing.Size(64, 20);

this.btnDelete.Text = "Delete";

this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click);

//

// txtId

//

this.txtId.Location = new System.Drawing.Point(40, 72);

this.txtId.Text = "ID";

//

// txtDesc

//

this.txtDesc.Location = new System.Drawing.Point(40, 96);

this.txtDesc.Size = new System.Drawing.Size(184, 20);

this.txtDesc.Text = "Description";

//

// btnChange

//

this.btnChange.Location = new System.Drawing.Point(92, 176);

this.btnChange.Size = new System.Drawing.Size(64, 20);

this.btnChange.Text = "Change";

this.btnChange.Click += new System.EventHandler(this.btnChange_Click);

//

// comboBox1

//

this.comboBox1.Items.Add("001");

this.comboBox1.Items.Add("002");

this.comboBox1.Items.Add("003");

this.comboBox1.Items.Add("004");

this.comboBox1.Items.Add("005");

this.comboBox1.Items.Add("006");

this.comboBox1.Items.Add("007");

this.comboBox1.Items.Add("008");

this.comboBox1.Location = new System.Drawing.Point(40, 124);

this.comboBox1.Size = new System.Drawing.Size(188, 21);

//

// comboBox2

//

this.comboBox2.Items.Add("Detta är objekt 1");

this.comboBox2.Items.Add("Detta är objekt 2");

this.comboBox2.Items.Add("Detta är objekt 3");

this.comboBox2.Items.Add("Detta är objekt 4");

this.comboBox2.Items.Add("Detta är objekt 5");

this.comboBox2.Items.Add("Detta är objekt 6");

this.comboBox2.Items.Add("Detta är objekt 7");

this.comboBox2.Items.Add("Detta är objekt 8");

this.comboBox2.Location = new System.Drawing.Point(40, 148);

this.comboBox2.Size = new System.Drawing.Size(188, 21);

//

// Form1

//

this.Controls.Add(this.comboBox2);

this.Controls.Add(this.comboBox1);

this.Controls.Add(this.btnChange);

this.Controls.Add(this.txtDesc);

this.Controls.Add(this.txtId);

this.Controls.Add(this.btnDelete);

this.Controls.Add(this.cmbBoxItems);

this.Controls.Add(this.btnAdd);

this.Menu = this.mainMenu1;

this.Text = "Form1";

}

#endregion

/// <summary>

/// The main entry point for the application.

/// </summary>

static void Main()

{

Application.Run(new Form1());

}



#region Main Methods

private void AddObject()

{

int iNewId;

string strNewId;

if( m_lst.Count == 0 )

{

iNewId = 1;

strNewId = "001";

}

else

{

iNewId = int.Parse(((MyClass)m_lst[m_lst.Count-1]).ID) + 1;

strNewId = iNewId.ToString().PadLeft(3, '0');

}

MyClass mc = new MyClass();

mc.ID = strNewId;

mc.Description = "Detta är objekt " + iNewId.ToString();

m_lst.Add(mc);

if( m_lst.Count == 1 ) // First object added

{

// We need to add databindings

AddBindings();

}

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

// Select the last(new) object

this.cmbBoxItems.SelectedIndex = this.cmbBoxItems.Items.Count - 1;

}

private void DeleteObject()

{

DisableButtons();

int indx = cmbBoxItems.SelectedIndex;

if( indx != -1 ) // List not empty

{

MyClass mc = (MyClass)cmbBoxItems.SelectedItem;

if( cmbBoxItems.Items.Count == 1 ) //Last object

{

// Must remove bindings before we remove last item

RemoveBindings();

m_lst.Remove( mc );

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

this.txtId.Text = string.Empty;

this.txtDesc.Text = string.Empty;

this.comboBox1.SelectedIndex = -1;

this.comboBox2.SelectedIndex = -1;

}

else if( indx == 0 ) //First obj

{

m_lst.Remove( mc );

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

mc = (MyClass)cmbBoxItems.SelectedItem;

this.txtId.Text = mc.ID;

this.txtDesc.Text = mc.Description;

}

else

{

cmbBoxItems.SelectedIndex--;

m_lst.Remove( mc );

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

}

}

EnableButtons();

}

private void SaveObject()

{

if( this.cmbBoxItems.SelectedIndex != -1 )

{

DisableButtons();


MyClass mc = (MyClass)cmbBoxItems.SelectedItem;

mc.ID = this.txtId.Text;

mc.Description = this.txtDesc.Text;

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);


EnableButtons();

}

}

private void ResetDataSource( ComboBox cbx, IList lst, string strDispMem )

{

// As ArrayList doesn't implement IBindingList it doesn't propagate

// changes to the controls that is bound to it. We need to reset the

// combobox DataSource property for each change to the list.


cbx.Visible = false; // No flickering please


cbx.DataSource = null;

if( lst.Count != 0 )

{

cbx.DisplayMember = strDispMem;

cbx.DataSource = lst;

}

else

cbx.Items.Clear(); // Force manual clearing of list


cbx.Visible = true;

}

private void AddBindings()

{

this.txtId.DataBindings.Add(new Binding("Text", m_lst, "ID"));

this.txtDesc.DataBindings.Add(new Binding("Text", m_lst, "Description"));

this.comboBox1.DataBindings.Add(new Binding("Text", m_lst, "ID"));

this.comboBox2.DataBindings.Add(new Binding("Text", m_lst,
"Description"));

}

private void RemoveBindings()

{

this.txtId.DataBindings.Clear();

this.txtDesc.DataBindings.Clear();

this.comboBox1.DataBindings.Clear();

this.comboBox2.DataBindings.Clear();

}

private void DisableButtons()

{

this.btnAdd.Enabled = false;

this.btnChange.Enabled = false;

this.btnDelete.Enabled = false;

}

private void EnableButtons()

{

this.btnAdd.Enabled = true;

this.btnChange.Enabled = true;

this.btnDelete.Enabled = true;

}


#endregion

#region EventHandlers

private void btnAdd_Click(object sender, System.EventArgs e)

{

DisableButtons();

AddObject();

EnableButtons();

}

private void btnDelete_Click(object sender, System.EventArgs e)

{

DeleteObject();

}

private void btnChange_Click(object sender, System.EventArgs e)

{

SaveObject();

}

#endregion

}

public class MyClass

{

string m_id;

string m_desc;

public string ID

{

get{ return m_id; }

set{ m_id = value; }

}

public string Description

{

get{ return m_desc; }

set{ m_desc = value; }

}

}

}
 
Peter,

I'm guessing that this arrangement may have been what caused the problem you
were having with deleting items, but since you've got a workaround for that,
it wouldn't really matter. It's just an unusual way (to me) to use data
binding with combo boxes.

--
Ginny Caughey
..Net Compact Framework MVP



PeterB said:
Hi Ginny!

The idea with the comboboxes was to use them as lists of choosable items.
I.e. the possible values the property bound to a combobox could have,
should
be locked. I don't want the comboboxes to be filled with the data from the
datasource (i.e. ArrayList). They should just display the current value of
the property.

For instance, in the code I sent, if you change the text in the textboxes
and press change, the underlying object in the list will change with
whatever you wrote in the textbox. The idea is that you could use the two
comboboxes in a similar fashion but you have a set of selectable choices
(instead of own typed text).

The child comboboxes must of course be pre-filled with selectable data
(matching the data in the objects) before the datasource is applied to the
main combobox.

regards,

Peter


message
Peter,

The only thing I saw in your code is that you are using "simple" databinding
for two of the comboboxes. This would be correct for textboxes (and your
code for the textboxes looks fine), but I have always used "complex"
databinding for comboboxes since they contain multiple (complex) data
just
as you did for the cmbBoxItems combobox:

this.comboBox1.DataSource = m_lst;
this.comboBox1.DisplayMember = "ID";
this.comboBox2.DataSource = m_lst;
this.comboBox2.DisplayMember = "Description";

As long as all three comboBoxes use the same object for the DataSource, they
should stay in sync, and so should the textboxes which are also bound to
m_lst as the data source.

--
Ginny Caughey
.Net Compact Framework MVP



PeterB said:
Hi Ginny!

You are lucky, I have found out that most of the trouble started when I
began to delete objects in the list. Especially when deleting the last
object. But finally I got it all working as well.

The declarations of the controls databinding is done in the following way:

m_lst = new ArrayList(); // List of all objects of type MyClass
this.cmbBoxItems.DisplayMember = "ID"; // Main combobox - ID is a property
of MyClass
this.cmbBoxItems.DataSource = m_lst; // Uses m_lst as datasource
// Child controls only displaying the data for each object selected in the
combobox
this.txtId.DataBindings.Add(new Binding("Text", m_lst, "ID"));
this.txtDesc.DataBindings.Add(new Binding("Text", m_lst, "Description"));
Description is a property of MyClass
this.comboBox1.DataBindings.Add(new Binding("Text", m_lst, "ID"));
this.comboBox2.DataBindings.Add(new Binding("Text", m_lst,
"Description"));

Is this the correct way of binding your datasource and child-controls??

I find it somewhat "magical" how the child-controls "know" which object in
m_lst is the one selected in the main combobox as the databindings doesn't
connect anything to the main combobox... do you have an answer for
this?

The extra textboxes and comboboxes could of course contain data of
other
fields than ID and Description.

There are also a few things I have noticed:

1. Must create object in list before adding databindings
Before I can add databindings to the controls, and bind the cmbbox to the
ArrayList I need atleast 1 object in the list. I solve this by adding the
databinding after the first object is added to the list. This may not
be
so strange, the binding need to know the object it binds to in the
list,
but still annoying :-)

2. If all objects are removed from the list, databindings must be removed
before the list is cleared.
If I remove the last object in the list I need to call
DataBindings.Clear() on the controls bound to the list before I clear it.
I can't call the DataBindings.Clear() method after the list is emptied,
which I need to do in order to re-enable databinding when objects are
added again. It also seems like the last object in the Main combobox isn't
removed (see 3.)

3. Removing the last objects imposes a slight delay.
When the main ComboBox's DataSource property is set to a empty list there
seem to be a slight (but annoying) delay. I reset the DataSource by
setting it to null, and then pointing it back to the updated list
(removed/added/changed object in it). However it seems
like setting the DataSource to null doesn't clear the ComboBox's item
list. I solved this by manually clearing the combobox instead of
setting
the datasource to the empty ArrayList to circumvent the annoying delay.

Since you never delete objects I suppose you never really noticed these
things. But could be good to know for future projects!

regards,

Peter


Here's the source for my test application if anyone would be
interested:

using System;

using System.Drawing;

using System.Collections;

using System.Windows.Forms;

using System.Data;

namespace ComboBoxArrayListBindingTest

{

/// <summary>

/// Summary description for Form1.

/// </summary>

public class Form1 : System.Windows.Forms.Form

{

#region Members

private System.Windows.Forms.Button btnAdd;

private System.Windows.Forms.Button btnDelete;

private System.Windows.Forms.ComboBox cmbBoxItems;

private System.Windows.Forms.TextBox txtId;

private System.Windows.Forms.TextBox txtDesc;

private System.Windows.Forms.Button btnChange;

private System.Windows.Forms.MainMenu mainMenu1;

private System.Windows.Forms.ComboBox comboBox1;

private System.Windows.Forms.ComboBox comboBox2;

ArrayList m_lst;

string strDisplayMember = "ID";

#endregion

#region Constructor & Disposer

public Form1()

{

//

// Required for Windows Form Designer support

//

InitializeComponent();

m_lst = new ArrayList();

}

/// <summary>

/// Clean up any resources being used.

/// </summary>

protected override void Dispose( bool disposing )

{

base.Dispose( disposing );

}

#endregion

#region Windows Form Designer generated code

/// <summary>

/// Required method for Designer support - do not modify

/// the contents of this method with the code editor.

/// </summary>

private void InitializeComponent()

{

this.mainMenu1 = new System.Windows.Forms.MainMenu();

this.btnAdd = new System.Windows.Forms.Button();

this.cmbBoxItems = new System.Windows.Forms.ComboBox();

this.btnDelete = new System.Windows.Forms.Button();

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

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

this.btnChange = new System.Windows.Forms.Button();

this.comboBox1 = new System.Windows.Forms.ComboBox();

this.comboBox2 = new System.Windows.Forms.ComboBox();

//

// btnAdd

//

this.btnAdd.Location = new System.Drawing.Point(20, 176);

this.btnAdd.Size = new System.Drawing.Size(64, 20);

this.btnAdd.Text = "Add";

this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);

//

// cmbBoxItems

//

this.cmbBoxItems.Location = new System.Drawing.Point(40, 32);

this.cmbBoxItems.Size = new System.Drawing.Size(140, 21);

//

// btnDelete

//

this.btnDelete.Location = new System.Drawing.Point(164, 176);

this.btnDelete.Size = new System.Drawing.Size(64, 20);

this.btnDelete.Text = "Delete";

this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click);

//

// txtId

//

this.txtId.Location = new System.Drawing.Point(40, 72);

this.txtId.Text = "ID";

//

// txtDesc

//

this.txtDesc.Location = new System.Drawing.Point(40, 96);

this.txtDesc.Size = new System.Drawing.Size(184, 20);

this.txtDesc.Text = "Description";

//

// btnChange

//

this.btnChange.Location = new System.Drawing.Point(92, 176);

this.btnChange.Size = new System.Drawing.Size(64, 20);

this.btnChange.Text = "Change";

this.btnChange.Click += new System.EventHandler(this.btnChange_Click);

//

// comboBox1

//

this.comboBox1.Items.Add("001");

this.comboBox1.Items.Add("002");

this.comboBox1.Items.Add("003");

this.comboBox1.Items.Add("004");

this.comboBox1.Items.Add("005");

this.comboBox1.Items.Add("006");

this.comboBox1.Items.Add("007");

this.comboBox1.Items.Add("008");

this.comboBox1.Location = new System.Drawing.Point(40, 124);

this.comboBox1.Size = new System.Drawing.Size(188, 21);

//

// comboBox2

//

this.comboBox2.Items.Add("Detta är objekt 1");

this.comboBox2.Items.Add("Detta är objekt 2");

this.comboBox2.Items.Add("Detta är objekt 3");

this.comboBox2.Items.Add("Detta är objekt 4");

this.comboBox2.Items.Add("Detta är objekt 5");

this.comboBox2.Items.Add("Detta är objekt 6");

this.comboBox2.Items.Add("Detta är objekt 7");

this.comboBox2.Items.Add("Detta är objekt 8");

this.comboBox2.Location = new System.Drawing.Point(40, 148);

this.comboBox2.Size = new System.Drawing.Size(188, 21);

//

// Form1

//

this.Controls.Add(this.comboBox2);

this.Controls.Add(this.comboBox1);

this.Controls.Add(this.btnChange);

this.Controls.Add(this.txtDesc);

this.Controls.Add(this.txtId);

this.Controls.Add(this.btnDelete);

this.Controls.Add(this.cmbBoxItems);

this.Controls.Add(this.btnAdd);

this.Menu = this.mainMenu1;

this.Text = "Form1";

}

#endregion

/// <summary>

/// The main entry point for the application.

/// </summary>

static void Main()

{

Application.Run(new Form1());

}



#region Main Methods

private void AddObject()

{

int iNewId;

string strNewId;

if( m_lst.Count == 0 )

{

iNewId = 1;

strNewId = "001";

}

else

{

iNewId = int.Parse(((MyClass)m_lst[m_lst.Count-1]).ID) + 1;

strNewId = iNewId.ToString().PadLeft(3, '0');

}

MyClass mc = new MyClass();

mc.ID = strNewId;

mc.Description = "Detta är objekt " + iNewId.ToString();

m_lst.Add(mc);

if( m_lst.Count == 1 ) // First object added

{

// We need to add databindings

AddBindings();

}

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

// Select the last(new) object

this.cmbBoxItems.SelectedIndex = this.cmbBoxItems.Items.Count - 1;

}

private void DeleteObject()

{

DisableButtons();

int indx = cmbBoxItems.SelectedIndex;

if( indx != -1 ) // List not empty

{

MyClass mc = (MyClass)cmbBoxItems.SelectedItem;

if( cmbBoxItems.Items.Count == 1 ) //Last object

{

// Must remove bindings before we remove last item

RemoveBindings();

m_lst.Remove( mc );

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

this.txtId.Text = string.Empty;

this.txtDesc.Text = string.Empty;

this.comboBox1.SelectedIndex = -1;

this.comboBox2.SelectedIndex = -1;

}

else if( indx == 0 ) //First obj

{

m_lst.Remove( mc );

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

mc = (MyClass)cmbBoxItems.SelectedItem;

this.txtId.Text = mc.ID;

this.txtDesc.Text = mc.Description;

}

else

{

cmbBoxItems.SelectedIndex--;

m_lst.Remove( mc );

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);

}

}

EnableButtons();

}

private void SaveObject()

{

if( this.cmbBoxItems.SelectedIndex != -1 )

{

DisableButtons();


MyClass mc = (MyClass)cmbBoxItems.SelectedItem;

mc.ID = this.txtId.Text;

mc.Description = this.txtDesc.Text;

ResetDataSource( this.cmbBoxItems, this.m_lst, strDisplayMember);


EnableButtons();

}

}

private void ResetDataSource( ComboBox cbx, IList lst, string strDispMem )

{

// As ArrayList doesn't implement IBindingList it doesn't propagate

// changes to the controls that is bound to it. We need to reset the

// combobox DataSource property for each change to the list.


cbx.Visible = false; // No flickering please


cbx.DataSource = null;

if( lst.Count != 0 )

{

cbx.DisplayMember = strDispMem;

cbx.DataSource = lst;

}

else

cbx.Items.Clear(); // Force manual clearing of list


cbx.Visible = true;

}

private void AddBindings()

{

this.txtId.DataBindings.Add(new Binding("Text", m_lst, "ID"));

this.txtDesc.DataBindings.Add(new Binding("Text", m_lst, "Description"));

this.comboBox1.DataBindings.Add(new Binding("Text", m_lst, "ID"));

this.comboBox2.DataBindings.Add(new Binding("Text", m_lst,
"Description"));

}

private void RemoveBindings()

{

this.txtId.DataBindings.Clear();

this.txtDesc.DataBindings.Clear();

this.comboBox1.DataBindings.Clear();

this.comboBox2.DataBindings.Clear();

}

private void DisableButtons()

{

this.btnAdd.Enabled = false;

this.btnChange.Enabled = false;

this.btnDelete.Enabled = false;

}

private void EnableButtons()

{

this.btnAdd.Enabled = true;

this.btnChange.Enabled = true;

this.btnDelete.Enabled = true;

}


#endregion

#region EventHandlers

private void btnAdd_Click(object sender, System.EventArgs e)

{

DisableButtons();

AddObject();

EnableButtons();

}

private void btnDelete_Click(object sender, System.EventArgs e)

{

DeleteObject();

}

private void btnChange_Click(object sender, System.EventArgs e)

{

SaveObject();

}

#endregion

}

public class MyClass

{

string m_id;

string m_desc;

public string ID

{

get{ return m_id; }

set{ m_id = value; }

}

public string Description

{

get{ return m_desc; }

set{ m_desc = value; }

}

}

}
 
Hi Ginny!

Thanks for your patience! And yes, you understand exactly what I wanted to
do, it's the simple solution you described (with a pre-defined set of
colors), and this is just a test to accomplish some databinding to different
controls (simple and complex).

I got it all working as I wanted now, however you notes about different
color schemes (or whatever the values of the properties are) made me think a
bit further. But that's a bite I'll chew on on another day.

As you understood I am using complex databinding to the "ID"-combo and
simple databinding to all the other controls (used to display/change propery
values), and there are a BUNCH of odd things I have noticed that you'd have
to think about.

As you also mention you could always do this on your own (catching the index
changes etc.), but I wanted to really get the data binding working and it
proved to be more difficult than I could have anticipated. I understand why
many choose to do it themselves...

It all started with a question for a MCP 70-316 test exam where the
"solution" was to use complex and simple data binding, while there actually
was an alternative where SelectedIndex changes was used....

Anyway, I might post a message/an article (the number of lines in my
"ThoughtsAboutDataBindings.txt" is actually close to "article-length" :) )
sometime to share my findings...

have a nice day/evening/weekend!

best regards,

Peter
 
Peter,

I hope you do publish your aritcle. It sounds like it could save other
people a lot of time figuring things out for themselves.

--
Ginny Caughey
..Net Compact Framework MVP
 
Back
Top