Richard Payne
asked on
C#, transfer or bind deserialized data into listbox within windowform
I have
The code below worked in transferring to listbox
When I tried
(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
List<Configuration> config; // This is List<T>
config = DeserializeFromFile2<List<Configuration>>(filePath);
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();
When I tried
listBox1.DataSource = ConfigData;
(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;
}
}
Can you please show how you assign ConfigData ?
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);
}
}
}
Produces the following output --saige-
If in your case, ConfigData is a list of some sort, then you might be seeing something like:If 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. -
2. The set DisplayMember; e.g. -
listBox1.DisplayMember = "CompanyString"
Which would produce the following output --saige-
ASKER
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.
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.
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-
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-
ASKER
Shall I accept Solution now or wait bit longer for your reply. Thx.
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);
}
}
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));
}
}
Which produces the following output --saige-
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.A ddRange method definitions. So without further ado -
-saige-
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);
}
}
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());
}
}
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());
}
}
The output and retrieval is the same as above.-saige-
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 -
-saige-
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;
}
}
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());
}
}
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));
}
}
}
Which again produces the same output as above.-saige-
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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.
There so much in .net framework.
Not a problem. Glad I was able to help out.
-saige-
-saige-
ASKER
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.SelectedIndexChan ged -= OnSelectedIndexChanged;
listBox1.DataSource = config;
listBox1.DisplayMember = "CompanyString";
listBox1.SelectedIndexChan ged += OnSelectedIndexChanged;
}
So I modify the code to disable the event handler, this is quick fix.
What you think?
if (config != null)
{
listBox1.SelectedIndexChan
listBox1.DataSource = config;
listBox1.DisplayMember = "CompanyString";
listBox1.SelectedIndexChan
}
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:
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));
}
}
}