C#, transfer or bind deserialized data into listbox within windowform

I have

 List<Configuration> config;     // This is List<T> 
config = DeserializeFromFile2<List<Configuration>>(filePath);

Open in new window


The code below worked in transferring to listbox
          
           listBox1.Items.Add(config[0].CompanyID);
           listBox1.Items.Add(config[0].CompanyString);
           listBox1.Items.Add(config[0].Test1);
            this.Show();

Open in new window


When I tried
listBox1.DataSource = ConfigData;

Open in new window


(1) It does not display data, just object name of some sort.... why?
(2) and how to make binding work correctly to list down data, so I can edit/modify.
(3) How to use addrange() for given List<Configuration> type, most of example I seen are int or string type.

For reference below is the class

    public class Configuration
    {
        public uint CompanyID { get; set; }
        public string CompanyString { get; set; }
        public uint Test1 { get; set; }
        public uint Test2 { get; set; }
        public uint Test3 { get; set; }

        public Configuration()
        {
            // Leave blank, do not install default data here.
        }

        public void ConfigurationDefault()
        {
            CompanyID = 10;
            CompanyString = "ZMDI";
            Test1 = 9678;
            Test2 = 34453;
            Test3 = 12981;
        }

    }

Open in new window

Richard PayneChief Technology EngineerAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

zephyr_hex (Megan)DeveloperCommented:
Can you please show how you assign ConfigData ?
1
it_saigeDeveloperCommented:
It's because the Datasource property expects an enumerable type (in other words something that can be enumerated over).  So you could select the config list as your datasource; e.g. - (Using my example configurations other question):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml.Serialization;

namespace EE_Q28741219
{
	public partial class Form1 : Form
	{
		List<ConfigurationX> configurations;

		public Form1()
		{
			InitializeComponent();
		}

		private void Form1_Load(object sender, EventArgs e)
		{
			if (File.Exists("Configurations.xml"))
			{
				configurations = DeserializeFromFile<List<ConfigurationX>>("Configurations.xml");
			}
			else
			{
				if (configurations == null)
				{
					configurations = new List<ConfigurationX>();
					for (int i = 0; i < 10; i++)
						configurations.Add(new ConfigurationX() { CompanyID = (uint)i, CompanyString = string.Format("ZMDI{0}", i) });
				}
				SerializeToFile<List<ConfigurationX>>("Configurations.xml");
			}

			if (configurations != null)
				listBox1.DataSource = configurations;
		}

		T DeserializeFromFile<T>(string fileName) where T : class
		{
			if (typeof(T).IsSerializable)
			{
				using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
				{
					XmlSerializer serializer = new XmlSerializer(typeof(T));
					return (T)serializer.Deserialize(stream);
				}
			}
			return default(T);
		}

		void SerializeToFile<T>(string fileName) where T : class
		{
			if (typeof(T).IsSerializable)
			{
				using (var stream = new FileStream(fileName, FileMode.CreateNew, FileAccess.ReadWrite))
				{
					XmlSerializer serializer = new XmlSerializer(typeof(T));
					serializer.Serialize(stream, configurations);
				}
			}
		}
	}

	[Serializable]
	public class ConfigurationX
	{
		public uint CompanyID { get; set; }
		public string CompanyString { get; set; }
		public uint Test1 { get; set; }
		public uint Test2 { get; set; }
		public uint Test3 { get; set; }

		public ConfigurationX()
		{
			// Leave blank, do not install default data here.
		}

		public void ConfigurationXDefault()
		{
			CompanyID = 10;
			CompanyString = "ZMDI";
			Test1 = 9678;
			Test2 = 34453;
			Test3 = 12981;
		}

		public override string ToString()
		{
			return string.Format("ID: {0}; String: {1}; Test1: {2}; Test2: {3}; Test4: {4}", CompanyID, CompanyString, Test1, Test2, Test3);
		}
	}
}

Open in new window

Produces the following output -Capture.JPG-saige-
1
it_saigeDeveloperCommented:
If in your case, ConfigData is a list of some sort, then you might be seeing something like:Capture.JPGIf this is the case, then you are experiencing this because the ListBox will display the one of two things.  1.  The string representation of the object (meaning if you do not override the ToString() method, then it uses the default object.ToString() method which simply returns the name of the complex type).
2.  The set DisplayMember; e.g. -
listBox1.DisplayMember = "CompanyString"

Open in new window

Which would produce the following output -Capture.JPG-saige-
1
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

Richard PayneChief Technology EngineerAuthor Commented:
Thank for prompt solution Saige, One more query before I closing this.

Is there alternative way to implement data transfer into listbox without using override tostring()?, for example for each loop and add or addrange?

Once again thanks for solution, it appreciated.
0
it_saigeDeveloperCommented:
You mean besides using the DisplayMember property?

There are many different ways to populate the listbox, but any method that does not set the datasource to the collection of objects or assign the object as the item in the listbox collection, only adds a layer of complexity to the data access.

This is in part because you now have to match the selection from the listbox to it's parent object in the collection.

If this is confusing, I will be posting a few examples shortly.  These exampls will use the CompanyString property to display the items, and makes the assumption that the CompanyString is unique.

-saige-
1
Richard PayneChief Technology EngineerAuthor Commented:
Shall I accept Solution now or wait bit longer for your reply. Thx.
0
it_saigeDeveloperCommented:
The Add method:
private void Form1_Load(object sender, EventArgs e)
{
	if (File.Exists("Configurations.xml"))
	{
		configurations = DeserializeFromFile<List<ConfigurationX>>("Configurations.xml");
	}
	else
	{
		if (configurations == null)
		{
			configurations = new List<ConfigurationX>();
			for (int i = 0; i < 10; i++)
				configurations.Add(new ConfigurationX() { CompanyID = (uint)i, CompanyString = string.Format("ZMDI{0}", i) });
		}
		SerializeToFile<List<ConfigurationX>>("Configurations.xml");
	}

	if (configurations != null)
	{
		for (int i = 0; i < configurations.Count; i++)
			listBox1.Items.Add(configurations[i].CompanyString);

		// You could also use a for each loop
		//foreach (var config in configurations)
		//     listBox1.Items.Add(config.CompanyString);
	}
}

Open in new window

Now if you wanted to retrieve the item, you would have to retrieve the selected item and match it to the item in the configurations collection; e.g. -
private void OnSelectedIndexChanged(object sender, EventArgs e)
{
	if (sender is ListBox)
	{
		ListBox lb = sender as ListBox;
		for (int i = 0; i < configurations.Count; i++)
		{
			if (configurations[i].CompanyString.Equals(lb.SelectedItem))
				MessageBox.Show(string.Format("You have selected - CompanyID: {0}", configurations[i].CompanyID));
		}

		// You could also use a For Each loop
		//foreach (var config in configurations)
		//{
		//     if (config.CompanyString.Equals(lb.SelectedItem))
		//          MessageBox.Show(string.Format("You have selected - CompanyID: {0}", configurations[i].CompanyID));
		//}

		// Or even linq
		//var selected = (from config in configurations where config.CompanyString.Equals(lb.SelectedItem) select config).FirstOrDefault();
		//if (selected != null)
		//     MessageBox.Show(string.Format("You have selected - CompanyID: {0}", configurations[i].CompanyID));
	}
}

Open in new window

Which produces the following output -Capture.JPG-saige-
0
it_saigeDeveloperCommented:
The AddRange method.  The AddRange method is similar to the add method except you are adding your entire selection at once.  This means you need either an array or an ObjectCollection, in the case of the ListBox.ObjectCollection.AddRange method definitions.  So without further ado -
private void Form1_Load(object sender, EventArgs e)
{
	if (File.Exists("Configurations.xml"))
	{
		configurations = DeserializeFromFile<List<ConfigurationX>>("Configurations.xml");
	}
	else
	{
		if (configurations == null)
		{
			configurations = new List<ConfigurationX>();
			for (int i = 0; i < 10; i++)
				configurations.Add(new ConfigurationX() { CompanyID = (uint)i, CompanyString = string.Format("ZMDI{0}", i) });
		}
		SerializeToFile<List<ConfigurationX>>("Configurations.xml");
	}

	if (configurations != null)
	{
		// Seed array for the addrange method.
		object[] items = new object[configurations.Count];

		for (int i = 0; i < configurations.Count; i++)
			items[i] = configurations[i].CompanyString;

		// You could also use a for each loop
		//int index = 0;
		//foreach (var config in configurations)
		//{
		//     items[index] = config.CompanyString;
		//     index++;
		//}

		// You could also use a linq statement
		// items = (from config in configurations select config.CompanyString).ToArray();

		listBox1.Items.AddRange(items);
	}
}

Open in new window

But using an array is so archaic compared to using an enumerable [or a collection], i.e. - A list:
private void Form1_Load(object sender, EventArgs e)
{
	if (File.Exists("Configurations.xml"))
	{
		configurations = DeserializeFromFile<List<ConfigurationX>>("Configurations.xml");
	}
	else
	{
		if (configurations == null)
		{
			configurations = new List<ConfigurationX>();
			for (int i = 0; i < 10; i++)
				configurations.Add(new ConfigurationX() { CompanyID = (uint)i, CompanyString = string.Format("ZMDI{0}", i) });
		}
		SerializeToFile<List<ConfigurationX>>("Configurations.xml");
	}

	if (configurations != null)
	{
		// Intermediary collection for the addrange method.
		List<object> items = new List<object>();

		for (int i = 0; i < configurations.Count; i++)
			items.Add(configurations[i].CompanyString);

		// You could also use a for each loop
		//foreach (var config in configurations)
		//     items.Add(config.CompanyString);

		// You could also use a linq statement
		// items = (from config in configurations select config.CompanyString);

		listBox1.Items.AddRange(items.ToArray());
	}
}

Open in new window

But by far the simplest AddRange methodolgy recognizes when an intermediary is not needed and disposes of it all together:
private void Form1_Load(object sender, EventArgs e)
{
	if (File.Exists("Configurations.xml"))
	{
		configurations = DeserializeFromFile<List<ConfigurationX>>("Configurations.xml");
	}
	else
	{
		if (configurations == null)
		{
			configurations = new List<ConfigurationX>();
			for (int i = 0; i < 10; i++)
				configurations.Add(new ConfigurationX() { CompanyID = (uint)i, CompanyString = string.Format("ZMDI{0}", i) });
		}
		SerializeToFile<List<ConfigurationX>>("Configurations.xml");
	}

	if (configurations != null)
	{
		// We don't need to use an intermediary.
		listBox1.Items.AddRange((from config in configurations select config.CompanyString).ToArray());
	}
}

Open in new window

The output and retrieval is the same as above.

-saige-
0
it_saigeDeveloperCommented:
But the most complex to implement but easiest to retrieve the correct data from is the use of a specialized Helper class.  Essentially this class is used in order to bind a displayed item with it's associated complex data object without needing to match it.  And it goes like this.

A simple, generic, ListItemData class implementation -
class ListItemData<T>
{
	public T ValueMember { get; private set; }
	public string DisplayMember { get; private set; }

	private ListItemData() { ;}

	public ListItemData(string displayMember, T valueMember)
	{
		DisplayMember = displayMember;
		ValueMember = valueMember;
	}

	public override string ToString()
	{
		return DisplayMember;
	}
}

Open in new window

Now we can use any method that adds the object in the ListBox as a ListItemData object:
private void Form1_Load(object sender, EventArgs e)
{
	if (File.Exists("Configurations.xml"))
	{
		configurations = DeserializeFromFile<List<ConfigurationX>>("Configurations.xml");
	}
	else
	{
		if (configurations == null)
		{
			configurations = new List<ConfigurationX>();
			for (int i = 0; i < 10; i++)
				configurations.Add(new ConfigurationX() { CompanyID = (uint)i, CompanyString = string.Format("ZMDI{0}", i) });
		}
		SerializeToFile<List<ConfigurationX>>("Configurations.xml");
	}

	if (configurations != null)
	{
		// **************************************************  Using the Add Methods **************************************************
		//for (int i = 0; i < configurations.Count; i++)
		//     listBox1.Items.Add(new ListItemData<ConfigurationX>(configurations[i].CompanyString, configurations[i]));

		// You could also use a for each loop
		//foreach (var config in configurations)
		//     listBox1.Items.Add(new ListItemData<ConfigurationX>(config.CompanyString, config));
		// ****************************************************************************************************************************

		// ***************************************** Using the AddRange Methods with an Array *****************************************
		// Seed array for the addrange method.
		//object[] items = new object[configurations.Count];

		//for (int i = 0; i < configurations.Count; i++)
		//     items[i] = new ListItemData<ConfigurationX>(configurations[i].CompanyString, configurations[i]);

		// You could also use a for each loop
		//int index = 0;
		//foreach (var config in configurations)
		//{
		//     items[index] = new ListItemData<ConfigurationX>(config.CompanyString, config);
		//     index++;
		//}

		// You could also use a linq statement
		// items = (from config in configurations select new ListItemData<ConfigurationX>(config.CompanyString, config)).ToArray();
		// ****************************************************************************************************************************

		// ************************************* Using the AddRange Methods with an Intermediary **************************************
		// Intermediary collection for the addrange method.
		//List<ListItemData> items = new List<ListItemData>();

		//for (int i = 0; i < configurations.Count; i++)
		//     items.Add(new ListItemData<ConfigurationX>(configurations[i].CompanyString, configurations[i]));

		// You could also use a for each loop
		//foreach (var config in configurations)
		//     items.Add(new ListItemData<ConfigurationX>(config.CompanyString, config));

		// You could also use a linq statement
		// items = (from config in configurations select new ListItemData<ConfigurationX>(config.CompanyString, config));

		//listBox1.Items.AddRange(items.ToArray());
		// ****************************************************************************************************************************

		// But again we do not need to use an intermediary
		listBox1.Items.AddRange((from config in configurations select new ListItemData<ConfigurationX>(config.CompanyString, config)).ToArray());
	}
}

Open in new window

And our selection method becomes more simplified:
private void OnSelectedIndexChanged(object sender, EventArgs e)
{
	if (sender is ListBox)
	{
		ListBox lb = sender as ListBox;
		if (lb.SelectedItem is ListItemData<ConfigurationX>)
		{
			ListItemData<ConfigurationX> lid = lb.SelectedItem as ListItemData<ConfigurationX>;
			MessageBox.Show(string.Format("You have selected - CompanyID: {0}", lid.ValueMember.CompanyID));
		}
	}
}

Open in new window

Which again produces the same output as above.

-saige-
0
it_saigeDeveloperCommented:
However as I stated in the beginning, by far the simplest method is the use of the DataSource property and either:
A.  Overloading the ToString method of your Complex datatype
B.  Specifying the DisplayMember property of the ListBox control.

How simple?  Well, this simple:

Setting the datasource -
private void Form1_Load(object sender, EventArgs e)
{
	if (File.Exists("Configurations.xml"))
	{
		configurations = DeserializeFromFile<List<ConfigurationX>>("Configurations.xml");
	}
	else
	{
		if (configurations == null)
		{
			configurations = new List<ConfigurationX>();
			for (int i = 0; i < 10; i++)
				configurations.Add(new ConfigurationX() { CompanyID = (uint)i, CompanyString = string.Format("ZMDI{0}", i) });
		}
		SerializeToFile<List<ConfigurationX>>("Configurations.xml");
	}

	if (configurations != null)
		listBox1.DataSource = configurations;
	listBox1.DisplayMember = "CompanyString";
}

Open in new window

Retrieving the datamember -
private void OnSelectedIndexChanged(object sender, EventArgs e)
{
	if (sender is ListBox)
	{
		ListBox lb = sender as ListBox;
		if (lb.SelectedItem is ConfigurationX)
		{
			ConfigurationX config = lb.SelectedItem as ConfigurationX;
			MessageBox.Show(string.Format("You have selected - CompanyID: {0}", config.CompanyID));
		}
	}
}

Open in new window

Which again, produces the exact same output as any of the previous examples.

-saige-
1

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Richard PayneChief Technology EngineerAuthor Commented:
Thank you so much, fantastic tutorial approach, I took example code structure bit by bit and test it out. Wow I learn a lot there.

There so much in .net framework.
1
it_saigeDeveloperCommented:
Not a problem.  Glad I was able to help out.

-saige-
0
Richard PayneChief Technology EngineerAuthor Commented:
For some reason, it firing the OnSelectedIndexChanged (3 times), why it happen is not clear to me

So I modify the code to disable the event handler, this is quick fix.

What you think?

            if (config != null)
            {
                listBox1.SelectedIndexChanged -= OnSelectedIndexChanged;
                listBox1.DataSource = config;
                listBox1.DisplayMember = "CompanyString";
                listBox1.SelectedIndexChanged += OnSelectedIndexChanged;
            }
0
it_saigeDeveloperCommented:
There could be a few reasons why the event fires multiple times.  Probably the best solution is to add a boolean value, e.g. - isLoading; and have the OnSelectedIndexChanged event check to see if this value is false.  If it is then the OnSelectedChangedEvent fires, otherwise, you disregard the event.

Something like:
private bool _isLoading = true;

private void Form1_Load(object sender, EventArgs e)
{
	if (File.Exists("Configurations.xml"))
	{
		configurations = DeserializeFromFile<List<ConfigurationX>>("Configurations.xml");
	}
	else
	{
		if (configurations == null)
		{
			configurations = new List<ConfigurationX>();
			for (int i = 0; i < 10; i++)
				configurations.Add(new ConfigurationX() { CompanyID = (uint)i, CompanyString = string.Format("ZMDI{0}", i) });
		}
		SerializeToFile<List<ConfigurationX>>("Configurations.xml");
	}

	if (configurations != null)
		listBox1.DataSource = configurations;
	listBox1.DisplayMember = "CompanyString";

	_isLoading = false;
}

private void OnSelectedIndexChanged(object sender, EventArgs e)
{
	if (!_isLoading && sender is ListBox)
	{
		ListBox lb = sender as ListBox;
		if (lb.SelectedItem is ConfigurationX)
		{
			ConfigurationX config = lb.SelectedItem as ConfigurationX;
			MessageBox.Show(string.Format("You have selected - CompanyID: {0}", config.CompanyID));
		}
	}
}

Open in new window

1
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Databases

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.