Reading items from list and display it as string - c#

I have an list with an object datatype which i'm trying to fill and then read out to a string. The list should hold data for each column in a DataGridView:
[Serializable]
public sealed class ColumnOrderItem
{
public int DisplayIndex { get; set; }
public int Width { get; set; }
public bool Visible { get; set; }
public int ColumnIndex { get; set; }
}
private void test()
{
List<ColumnOrderItem> columnOrder = new List<ColumnOrderItem>();
DataGridViewColumnCollection columns = dsgDataGrid.Columns;
for (int i = 0; i < columns.Count; i++)
{
columnOrder.Add(new ColumnOrderItem
{
ColumnIndex = i,
DisplayIndex = columns[i].DisplayIndex,
Visible = columns[i].Visible,
Width = columns[i].Width
});
}
colData = string.Join(",", columnOrder);
MessageBox.Show(colData);
}
what I'm expecting is the MessageBox should display each item value in sequence like DisplayIndex, Width, Visible, ColumnIndex, DisplayIndex, Width, Visible, ColumnIndex...and so on. The problem is that all i get read back is 0,1,2 which seems to be just the ColumnIndex for the 3 columns under the dsgDataGrid which is a DataGridView. Does anyone have any idea how i might output all the data to a single string?
Thanks.

The best option for you is override the ToString method Hence your class definition will be like the following:
[Serializable]
public sealed class ColumnOrderItem
{
public int DisplayIndex { get; set; }
public int Width { get; set; }
public bool Visible { get; set; }
public int ColumnIndex { get; set; }
public override string ToString()
{
StringBuilder overRideString = new StringBuilder();
overRideString.AppendLine("DisplayIndex" + DisplayIndex.ToString());
overRideString.AppendLine("Width" + Width.ToString());
overRideString.AppendLine("Visible" + Visible.ToString());
overRideString.AppendLine("ColumnIndex" + ColumnIndex.ToString());
return overRideString.ToString();
}
}
How to use:
StringBuilder details = new StringBuilder();
foreach (ColumnOrderItem item in columnOrder)
{
details.AppendLine(item.ToString());
}
details.ToString(); // this will give you the required result

You can define ToString for ColumnOrderItem and use
THX for informing me about the implict calling of ToString by String.join #Neijwiert
Eg:
public string ToString()
{
return string.Format("{0},{1},{2},{3}",
this.ColumnIndex,
this.DisplayIndex,
this.Visible,
this.Width);
}
When you call String.join(spliter,enums:IEnumerable<T>)
It works the same as String.join(spliter,enums.Select(i->i.toString()))
Join(String, IEnumerable) is a convenience method that lets you concatenate each member of an IEnumerable collection without first converting them to strings. The string representation of each object in the IEnumerable collection is derived by calling that object's ToString method.
String.Join Method (String, IEnumerable)-MSDN

you might have to add a ToString override in your ColumnOrderItem Class like :
public override string ToString()
{
return string.Format(
"[DisplayIndex: {0}, Width: {1}, Visible: {2}, ColumnIndex: {3}]\n",
DisplayIndex,
Wi`enter code here`dth,
Visible,
ColumnIndex);
}
then when you do var colData = string.Join(",", columnOrder); this should give you the output you wanted for example

Related

Using csvHelper to read cell content into List or Array

I have a csv file of the type:
name,numbersA,numbersB
Bob,"1,2,3,4","6,7,8,9"
Brabra,"9,3","4,8,5,7,2"
So arrays of ints are given by the numbers in between the " ".
I want to map that to the following class
public class Names
{
[Name("name")]
public string Name { get; set; }
[Name("numbersA")]
public List<int> NumbersA{ get; set; }
[Name("numbersB")]
public List<int> NumbersB{ get; set; }
}
I don't specifically mind if the NumbersA/B is of type int[] or List. So far I managed to read the data by specifying the type of NumbersA/B as string and that works correctly (I get the string between the " "). Can this be done automatically in csvHelper or do I have to parse the string myself later on.
void Main()
{
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = csv.GetRecords<Names>();
List<Names> NameList = records.ToList();
}
}
Any help would be very much appreciated.
SOLUTION
Credit goes to #Oguz Ozgul (see below). I made a couple of minor changes.
public class Names
{
[Name("name")]
public string Name { get; set; }
[Name("numbersA")]
[TypeConverter(typeof(ToIntArrayConverter))]
public List<int> NumbersA{ get; set; }
[Name("numbersB")]
[TypeConverter(typeof(ToIntArrayConverter))]
public List<int> NumbersB{ get; set; }
}
public class ToIntArrayConverter : TypeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
if (text == "") return new List<int>();
string[] allElements = text.Split(',');
int[] elementsAsInteger = allElements.Select(s => int.Parse(s)).ToArray();
return new List<int>(elementsAsInteger);
}
public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
{
return string.Join(",", ((List<int>)value).ToArray());
}
}
You can implement a custom type converter and use it during conversion (both directions) for your two properties.
The TypeConverter class is under namespace CsvHelper.TypeConversion
The TypeConverterAttribute is under namespace CsvHelper.Configuration.Attributes
public class ToIntArrayConverter : TypeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
string[] allElements = text.Split(',');
int[] elementsAsInteger = allElements.Select(s => int.Parse(s)).ToArray();
return new List<int>(elementsAsInteger);
}
public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
{
return string.Join(',', ((List<int>)value).ToArray());
}
}
To use this converter, simply add the following TypeConverterAttribute annotations on top of your properties:
public class Names
{
[Name("name")]
public string Name { get; set; }
[Name("numbersA")]
[TypeConverter(typeof(ToIntArrayConverter))]
public List<int> NumbersA { get; set; }
[Name("numbersB")]
[TypeConverter(typeof(ToIntArrayConverter))]
public List<int> NumbersB { get; set; }
}

c# How to access elements of a list of objects consisting of int and string in foreach

I have a list of objects of a class, made of a string and an int.
public class PatternInfo
{
private string prpatternname;
private int prpatterntier;
public PatternInfo(string patternname, int patterntier)
{
prpatternname = patternname;
prpatterntier = patterntier;
}
}
This is in a seperate file.
I have a list of such objects:
public List<PatternInfo> patternlist;
Now for each obejct of this class in a list, I want to set values of variables to those two values:
foreach (PatternInfo x in patternlist)
{
string a = patternname;
int b = patterntier;
}
for some reason I get "the name patternname/patterntier does not exist in current context". I have tried playing with some solutions, but can't get it to work, please help :)
To align with OP:
public class PatternInfo
{
public string prpatternname { get; }
public int prpatterntier { get; }
public PatternInfo(string patternname, int patterntier)
{
prpatternname = patternname;
prpatterntier = patterntier;
}
}
As you haven't provided how you declare patternlist, I made up the following:
public static void Main()
{
List<PatternInfo> patternlist = null;
for (int i = 0; i < 10; i++)
patternlist.Add(new PatternInfo(i.ToString(), i));
foreach(PatternInfo x in patternlist)
{
string a = x.prpatternname;
int b = x.prpatterntier;
}
}
This is because the attributes are private and attributes of x. Add this to a PatternInfo:
public string name { get;}
public int tier {get;}
Then, in the loop, you can have the following:
string a = x.name;
int b = x.tier;

c# petapoco object to datagridview, change column order

I have a bussiness object like this:
[PetaPoco.TableName("cars")]
[PetaPoco.PrimaryKey("id")]
public class Cars : CarObject
{
[DefaultValue(null)]
[DisplayName("Column Name")]
public string Color { get; set; }
[DefaultValue(null)]
public string Engine { get; set; }
[DefaultValue(null)]
public string BHP { get; set; }
[DefaultValue(null)]
public string Year { get; set; }
}
And I display in DataGridView like this:
List<Cars> ret = db.Query<Cars>("select * from cars").ToList();
if(ret != null)
dgv.DataSource = ret; // .OrderBy(o => o.Year).ToList();
However, seems that DGV put columns like in object (design time) order. I could alter this by using a loop with DGV DisplayIndex property but there is a simple solution, some attribute decorate?
Thanks in advance,
PS. I tried also using System.ComponentModel.DataAnnotations but seems that for WinForms isn't working. The DGV isn't able to bind that attributes.
[Display(Name = "Custom Name", Order = 2)]
Any hack? Thanks very much.
Thanks to Tim Van Wassenhove's elegant solution, I managed to do exactly what I wanted.
http://www.timvw.be/2007/02/04/control-the-order-of-properties-in-your-class/
It requires a modified BindingList<> class (I modified a bit more)
[AttributeUsage(AttributeTargets.Property)]
public class PropertyOrderAttribute : Attribute
{
private int order;
public PropertyOrderAttribute(int order)
{
this.order = order;
}
public int Order
{
get { return this.order; }
}
}
class PropertyOrderBindingList<T> : BindingList<T>, ITypedList
{
public PropertyOrderBindingList(List<T> list)
: base(list)
{
//
}
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
PropertyDescriptorCollection typePropertiesCollection = TypeDescriptor.GetProperties(typeof(T));
return typePropertiesCollection.Sort(new PropertyDescriptorComparer());
}
public string GetListName(PropertyDescriptor[] listAccessors)
{
return string.Format("A list with Properties for {0}", typeof(T).Name);
}
}
class PropertyDescriptorComparer : IComparer
{
public int Compare(object x, object y)
{
if (x == y) return 0;
if (x == null) return 1;
if (y == null) return -1;
PropertyDescriptor propertyDescriptorX = x as PropertyDescriptor;
PropertyDescriptor propertyDescriptorY = y as PropertyDescriptor;
PropertyOrderAttribute propertyOrderAttributeX = propertyDescriptorX.Attributes[typeof(PropertyOrderAttribute)] as PropertyOrderAttribute;
PropertyOrderAttribute propertyOrderAttributeY = propertyDescriptorY.Attributes[typeof(PropertyOrderAttribute)] as PropertyOrderAttribute;
if (propertyOrderAttributeX == propertyOrderAttributeY) return 0;
if (propertyOrderAttributeX == null) return 1;
if (propertyOrderAttributeY == null) return -1;
return propertyOrderAttributeX.Order.CompareTo(propertyOrderAttributeY.Order);
}
}
Now, just decorate the poco object attributes with order:
[DefaultValue(null)]
[DisplayName("Column Name")]
[PropertyOrder(3)]
public string Color { get; set; }
[DefaultValue(null)]
[PropertyOrder(1)]
public string Engine { get; set; }
[DefaultValue(null)]
[PropertyOrder(0)]
public string BHP { get; set; }
[DefaultValue(null)]
[PropertyOrder(2)]
public string Year { get; set; }
And query like this
List<Cars> ret2 = db.Query<Cars>("select * from cars").ToList();
PropertyOrderBindingList<Cars> ret = new PropertyOrderBindingList<Cars>(ret2);
The properties will be in custom order.

CheckedListBox datasource not displaying correctly

I have the following code
clbCodes.DisplayMember = "Name";
clbCodes.ValueMember = "Id";
checkboxItemList = new List<CheckBoxItem>();
foreach (var uagCode in codes)
{
var checkboxItem = new CheckBoxItem
{
Id = uagCode.Code,
Name = uagCode.UAGLabel
};
checkboxItemList.Add(checkboxItem);
}
clbCodes.DataSource = checkboxItemList;
public class CheckBoxItem
{
public string Name { get; set; }
public string Id { get; set; }
}
However, when I run this, instead of seeing the "Name" of my item, e.g.,
"Card"
"Toy"
"Table"
I see
WindowsApplication1.CheckBoxItem
WindowsApplication1.CheckBoxItem
WindowsApplication1.CheckBoxItem
populated in my listbox
What did I do wrong?
Since it appears that you are adding to the CheckListBox a collection of custom objects, you should have as part of that class an override of ToString() that can return the Name that you are wanting to display.
public override String ToString()
{
return this.Name;
}
You need to override ToString:
public class CheckBoxItem
{
public string Name { get; set; }
public string Id { get; set; }
public override String ToString()
{
return Name;
}
}

ordering generic list by size property

Hi I have had to use interfaces before but ive been told i need to implement icomparable in this instance. see below:
internal class doorItem : IComparable
{
public int CompareTo(doorItem other)
{
// The temperature comparison depends on the comparison of the
// the underlying Double values. Because the CompareTo method is
// strongly typed, it is not necessary to test for the correct
// object type.
return GetNumber(productSize).CompareTo(GetNumber(other.productSize));
}
public string variations { get; set; }
public double pricerange { get; set; }
public string viewDetailsLink { get; set; }
public string height { get; set; }
public string width { get; set; }
public string productSize { get; set; }
public string productImage { get; set; }
public int countItemsOnSale { get; set; }
public string optionFor35Product { get; set; }
private int GetNumber(string str)
{
//this method gets the int out of the string
int length = str.Length;
string output = String.Empty;
int test = 0;
bool err = false;
for (int i = 0; i <= length; i++)
{
try
{
test = Convert.ToInt32(str.Substring(i, 1));
}
catch
{
err = true;
}
if (!err)
output += str.Substring(i, 1);
else
break;
}
return Convert.ToInt32(output);
}
}
above is the class i have created, door sizes are returned like this: 4dr, 5dr, 6dr etc.. then the getnumber method gets the int out of the string.
i have a generic list in of my custom class in the main method like this:
List<doorItem> d = new List<doorItem>();
i cant work out how to order this list by door size.... PLEASE HELP
It's easiest to do this using LINQ. Then you don't even need to implement IComparable.
var sortedList = doorList.OrderBy( d => d.GetNumber(d.productSize ).ToList();
And make GetNumber public inside the doorItem class.
I don't know if performance is important, but that method for getting the number is pretty horrible, exceptions should only be used in exceptional circumstances! Suggest something like this
StringBuilder sb = new StringBuilder();
foreach (char c in str)
{
if (Char.IsNumber(c))
{
sb.append(c);
}
}
return Convert.ToInt32(sb.ToString());
For sorting you can do what stecya has suggested, or you could convert this method to a property and sort directly.
public int Size
{
get
{
return GetNumber(this.productSize);
}
}
...
d.OrderBy(x=>x.Size);

Categories

Resources