How to create dynamic buttons based on an arraylist - c#

I'm having some trouble creating a foreach loop that creates buttons dynamically based on a List that is inside the NamesDA class.
I'm getting errors such as: Cannot convert type 'Program1.Names' to 'int'. I've tried what I know to fix the conversion error, but I don't know the correct way to do it.
Edit 1: allNames is an array list inside NamesDA that reads a csv file.
It returns a list of strings and int's, which then they are to be used to create the buttons and represent them.
Edit 2: The foreach loop problem is solved now, but I'm unable to get the values of column[0] for button text and column[1] for button tag.
The NamesDA class:
private const string path = "names.csv";
public static List<Names> GetNames()
{
StreamReader textIn = new StreamReader(new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read));
List<Names> allNames = new List<Names>();
while (textIn.Peek() != -1)
{
string row = textIn.ReadLine();
string[] columns = row.Split(',');
allNames.Add(new Names(columns[0].ToString(), Convert.ToInt16(columns[1])));
}
textIn.Close();
return allNames;
}
The form:
int startTop = 20;
int startLeft = 17;
allNames = NamesDA.GetNames(); //calling the method in the NamesDA class
foreach (int x in allNames) {
names[x] = new Button();
tempButton.Text = ""; //based on the list column[0]
tempButton.Tag = ""; //based on the list column[1]
names[x].Location = new System.Drawing.Point(startTop + (x * 95), startLeft);
listView.Controls.Add(names[x]);
}

From the Updates it is clear that allNames is a List<Names>, where Names is a class contains two properties/fields one is of type int(let it be _id) and the another one is of type string(let it be _name). So you have to re create the loop as like the following:
Updates : You can Set the button location as well, if you need that you have to define two integer properties in the class(let it be int positionX=10 and int PositionY=30) Now take a look at the updated code:
int nextLeft=30;
foreach (Names name in allNames)
{
Button tempButton = new Button();
tempButton.Name = name._id;
tempButton.Text = name._name;
tempButton.Location = new System.Drawing.Point(name.positionX + nextLeft,name.positionY);
listView.Controls.Add(tempButton);
nextLeft+=30;
}

Related

Filling <string> list or array to export element names to excel

The goal is to fill excel cells with elements names. Using EPPlus.
_elementsDB gives just "Autodesk.Revit.DB.Wall".
int col = 1;
for ( int row=2; row < _elementsDB.Count; row++ )
{
ws.Cells[row, col].Value = _elementsDB;
}
Tying to populate either an array or list. Nothing works.
FilteredElementCollector collector = new FilteredElementCollector(doc);
IList<Element> _elementsDB = collector.OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements();
List<Element> _elementsDB = collector.OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements();
//First Option
string[] _elementsNameArr = new string[] { };
foreach (Element e in _elementsDB)
{
_elementsNameArr = e.Name;
}
//Second Option
List <string> _elementsNameList = new List<string>;
foreach (Element e in _elementsDB)
{
_elementsNameList = e.Name;
}
Also tried to create a sorted list, didn't work either. Shows up an excxeption System.Argument.Exception "A record with such a key already exists".
SortedList<string, Element> _elementNameSorted = new SortedList<string, Element>();
foreach (Element e in _elementsDB)
{
_elementNameSorted.Add(e.Name,e);
}
When you use the method .ToElements() it returns a IList<Element> that you can convert it later, you cannot directly assign its result to List<Element>, you have to use LINQ to convert, using .ToElements().ToList()
If you don't have it yet, be sure to add using System.Linq; to the top of your code.
Anyway, there's no need to convert to List<Element> here, try the code below:
FilteredElementCollector collector = new FilteredElementCollector(doc);
IList<Element> _elementsDB = collector.OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements();
//List<Element> _elementsDB = collector.OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements(); // <---- IF YOU ALREADY DECLARED _elementsDB BEFORE, YOU CAN'T HAVE THIS HERE TOGETHER
List <string> _elementsNameList = new List<string>(); // <-- YOU'VE MISSED () HERE
foreach (Element e in _elementsDB)
{
_elementsNameList.Add(e.Name); // <-- HERE YOU HAVE TO ADD IT TO THE LIST, NOT ASSIGN TO THE LIST, YOU CANNOT ASSIGN A string TO A List<string>, YOU HAVE TO ADD
}
//Sorting your list would be
_elementsNameList = _elementsNameList.OrderBy(q => q).ToList();
//...... Write here if you have any code before writing the Excel
try
{
WriteXLS("YOU EXCEL FILE PATH HERE", "YOUR WORKSEET NAME HERE", _elementsNameList, 2); // <--- METHOD SOWN IN THE CODE BELOW
}
catch(Exception e)
{
TaskDialog.Show("Error", e.Message);
}
For writing to an existing Excel file you can use the method bellow:
private void WriteXLS(string filePath, string workSheetName, List<string> elementsNamesList, int startRow = 1, int col = 1)
{
FileInfo existingFile = new FileInfo(filePath);
using (ExcelPackage package = new ExcelPackage(existingFile))
{
ExcelWorksheet ws = GetWorkSheet(package, workSheetName);
int maxRows = elementsNamesList.Count;
for (int row = startRow; row <= maxRows; row++)
{
ws.Cells[row, col].Value = elementsNamesList[row];
}
}
}
And, before running it, be sure to have you Excel file closed, it doesn't work if the file is opened.

Adding an Object of class to an array of the same class C#

Hello all
I try to convert a group of ŲStrings to a class, and then add these elements to an array or list of the same class
problem
Everything is fine, only when one element is added does it change all the values in the array to the same values as the last element
TxtCookie.Text :
1=|257|9.5|1|true|true|true|true|1-From Web, 2=|259|11.5|7|false|false|false|false|232-From Web, 3=|261|9.5|5|true|false|true|true|-From Web, 4=|267|9.5|1|true|true|true|true|-From Web
This code :
//Get The Value from Text Box To list of Strings
string[] lst = TxtCookie.Text.Split(',');
//Divide each element into a set of values
var D = (from a in lst select a.Split('|')).ToList();
//Define an object from the class
TblInvoiceContent tblInvoiceContent = new TblInvoiceContent();
//Define an List from the class
List<TblInvoiceContent> TBLIC = new List<TblInvoiceContent>();
//Here I take the values and configure them according to the class structure
foreach (var item in D)
{
tblInvoiceContent.ItremID = Convert.ToInt32(item[1]);
tblInvoiceContent.SilingPrice = Convert.ToDouble(item[2]);
tblInvoiceContent.Quantity = Convert.ToInt32(item[3]);
tblInvoiceContent.mayonnaise = Convert.ToBoolean(item[4]);
tblInvoiceContent.ketchup = Convert.ToBoolean(item[5]);
tblInvoiceContent.Hot = Convert.ToBoolean(item[6]);
tblInvoiceContent.garlic = Convert.ToBoolean(item[7]);
tblInvoiceContent.Reqomindition = item[8].ToString();
//Here I add the item to the list
TBLIC.Add(tblInvoiceContent);
}
//Here I am displaying the list items
GridView1.DataSource = TBLIC;
GridView1.DataBind();
Result :
Because you only ever create one instance of your object:
TblInvoiceContent tblInvoiceContent = new TblInvoiceContent();
Then in the loop you modify the instance each time and re-add it to the list again.
Move the instance creation into the loop:
foreach (var item in D)
{
TblInvoiceContent tblInvoiceContent = new TblInvoiceContent();
tblInvoiceContent.ItremID = Convert.ToInt32(item[1]);
tblInvoiceContent.SilingPrice = Convert.ToDouble(item[2]);
tblInvoiceContent.Quantity = Convert.ToInt32(item[3]);
tblInvoiceContent.mayonnaise = Convert.ToBoolean(item[4]);
tblInvoiceContent.ketchup = Convert.ToBoolean(item[5]);
tblInvoiceContent.Hot = Convert.ToBoolean(item[6]);
tblInvoiceContent.garlic = Convert.ToBoolean(item[7]);
tblInvoiceContent.Reqomindition = item[8].ToString();
TBLIC.Add(tblInvoiceContent);
}
That way each time the loop iterates you would create a new instance of the object.
//Get The Value from Text Box To list of Strings
string[] lst = TxtCookie.Text.Split(',');
//Divide each element into a set of values
var D = (from a in lst select a.Split('|')).ToList();
//Define an List from the class
List<TblInvoiceContent> TBLIC = new List<TblInvoiceContent>();
//Here I take the values and configure them according to the class structure
foreach (var item in D)
{
//CALL IT HERE Define an object from the class
TblInvoiceContent tblInvoiceContent = new TblInvoiceContent();
tblInvoiceContent.ItremID = Convert.ToInt32(item[1]);
tblInvoiceContent.SilingPrice = Convert.ToDouble(item[2]);
tblInvoiceContent.Quantity = Convert.ToInt32(item[3]);
tblInvoiceContent.mayonnaise = Convert.ToBoolean(item[4]);
tblInvoiceContent.ketchup = Convert.ToBoolean(item[5]);
tblInvoiceContent.Hot = Convert.ToBoolean(item[6]);
tblInvoiceContent.garlic = Convert.ToBoolean(item[7]);
tblInvoiceContent.Reqomindition = item[8].ToString();
//Here I add the item to the list
TBLIC.Add(tblInvoiceContent);
}
//Here I am displaying the list items
GridView1.DataSource = TBLIC;
GridView1.DataBind();

C# Reading CSV to DataTable and Invoke Rows/Columns

i am currently working on a small Project and i got stuck with a Problem i currently can not manage to solve...
I have multiple ".CSV" Files i want to read, they all have the same Data just with different Values.
Header1;Value1;Info1
Header2;Value2;Info2
Header3;Value3;Info3
While reading the first File i Need to Create the Headers. The Problem is they are not splited in Columns but in rows (as you can see above Header1-Header3).
Then it Needs to read the Value 1 - Value 3 (they are listed in the 2nd Column) and on top of that i Need to create another Header -> Header4 with the data of "Info2" which is always placed in Column 3 and Row 2 (the other values of Column 3 i can ignore).
So the Outcome after the first File should look like this:
Header1;Header2;Header3;Header4;
Value1;Value2;Value3;Info2;
And after multiple files it sohuld be like this:
Header1;Header2;Header3;Header4;
Value1;Value2;Value3;Value4;
Value1b;Value2b;Value3b;Value4b;
Value1c;Value2c;Value3c;Value4c;
I tried it with OleDB but i get the Error "missing ISAM" which i cant mange to fix. The Code i Used is the following:
public DataTable ReadCsv(string fileName)
{
DataTable dt = new DataTable("Data");
/* using (OleDbConnection cn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=\"" +
Path.GetDirectoryName(fileName) + "\";Extendet Properties ='text;HDR=yes;FMT=Delimited(,)';"))
*/
using (OleDbConnection cn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
Path.GetDirectoryName(fileName) + ";Extendet Properties ='text;HDR=yes;FMT=Delimited(,)';"))
{
using(OleDbCommand cmd = new OleDbCommand(string.Format("select *from [{0}]", new FileInfo(fileName).Name,cn)))
{
cn.Open();
using(OleDbDataAdapter adapter = new OleDbDataAdapter(cmd))
{
adapter.Fill(dt);
}
}
}
return dt;
}
Another attempt i did was using StreamReader. But the Headers are in the wrong place and i dont know how to Change this + do this for every file. the Code i tried is the following:
public static DataTable ReadCsvFilee(string path)
{
DataTable oDataTable = new DataTable();
var fileNames = Directory.GetFiles(path);
foreach (var fileName in fileNames)
{
//initialising a StreamReader type variable and will pass the file location
StreamReader oStreamReader = new StreamReader(fileName);
// CONTROLS WHETHER WE SKIP A ROW OR NOT
int RowCount = 0;
// CONTROLS WHETHER WE CREATE COLUMNS OR NOT
bool hasColumns = false;
string[] ColumnNames = null;
string[] oStreamDataValues = null;
//using while loop read the stream data till end
while (!oStreamReader.EndOfStream)
{
String oStreamRowData = oStreamReader.ReadLine().Trim();
if (oStreamRowData.Length > 0)
{
oStreamDataValues = oStreamRowData.Split(';');
//Bcoz the first row contains column names, we will poluate
//the column name by
//reading the first row and RowCount-0 will be true only once
// CHANGE TO CHECK FOR COLUMNS CREATED
if (!hasColumns)
{
ColumnNames = oStreamRowData.Split(';');
//using foreach looping through all the column names
foreach (string csvcolumn in ColumnNames)
{
DataColumn oDataColumn = new DataColumn(csvcolumn.ToUpper(), typeof(string));
//setting the default value of empty.string to newly created column
oDataColumn.DefaultValue = string.Empty;
//adding the newly created column to the table
oDataTable.Columns.Add(oDataColumn);
}
// SET COLUMNS CREATED
hasColumns = true;
// SET RowCount TO 0 SO WE KNOW TO SKIP COLUMNS LINE
RowCount = 0;
}
else
{
// IF RowCount IS 0 THEN SKIP COLUMN LINE
if (RowCount++ == 0) continue;
//creates a new DataRow with the same schema as of the oDataTable
DataRow oDataRow = oDataTable.NewRow();
//using foreach looping through all the column names
for (int i = 0; i < ColumnNames.Length; i++)
{
oDataRow[ColumnNames[i]] = oStreamDataValues[i] == null ? string.Empty : oStreamDataValues[i].ToString();
}
//adding the newly created row with data to the oDataTable
oDataTable.Rows.Add(oDataRow);
}
}
}
//close the oStreamReader object
oStreamReader.Close();
//release all the resources used by the oStreamReader object
oStreamReader.Dispose();
}
return oDataTable;
}
I am thankful for everyone who is willing to help. And Thanks for reading this far!
Sincerely yours
If I understood you right, there is a strict parsing there like this:
string OpenAndParse(string filename, bool firstFile=false)
{
var lines = File.ReadAllLines(filename);
var parsed = lines.Select(l => l.Split(';')).ToArray();
var header = $"{parsed[0][0]};{parsed[1][0]};{parsed[2][0]};{parsed[1][0]}\n";
var data = $"{parsed[0][1]};{parsed[1][1]};{parsed[2][1]};{parsed[1][2]}\n";
return firstFile
? $"{header}{data}"
: $"{data}";
}
Where it would return - if first file:
Header1;Header2;Header3;Header2
Value1;Value2;Value3;Value4
if not first file:
Value1;Value2;Value3;Value4
If I am correct, rest is about running this against a list file of files and joining the results in an output file.
EDIT: Against a directory:
void ProcessFiles(string folderName, string outputFileName)
{
bool firstFile = true;
foreach (var f in Directory.GetFiles(folderName))
{
File.AppendAllText(outputFileName, OpenAndParse(f, firstFile));
firstFile = false;
}
}
Note: I missed you want a DataTable and not an output file. Then you could simply create a list and put the results into that list making the list the datasource for your datatable (then why would you use semicolons in there? Probably all you need is to simply attach the array values to a list).
(Adding as another answer just to make it uncluttered)
void ProcessMyFiles(string folderName)
{
List<MyData> d = new List<MyData>();
var files = Directory.GetFiles(folderName);
foreach (var file in files)
{
OpenAndParse(file, d);
}
string[] headers = GetHeaders(files[0]);
DataGridView dgv = new DataGridView {Dock=DockStyle.Fill};
dgv.DataSource = d;
dgv.ColumnAdded += (sender, e) => {e.Column.HeaderText = headers[e.Column.Index];};
Form f = new Form();
f.Controls.Add(dgv);
f.Show();
}
string[] GetHeaders(string filename)
{
var lines = File.ReadAllLines(filename);
var parsed = lines.Select(l => l.Split(';')).ToArray();
return new string[] { parsed[0][0], parsed[1][0], parsed[2][0], parsed[1][0] };
}
void OpenAndParse(string filename, List<MyData> d)
{
var lines = File.ReadAllLines(filename);
var parsed = lines.Select(l => l.Split(';')).ToArray();
var data = new MyData
{
Col1 = parsed[0][1],
Col2 = parsed[1][1],
Col3 = parsed[2][1],
Col4 = parsed[1][2]
};
d.Add(data);
}
public class MyData
{
public string Col1 { get; set; }
public string Col2 { get; set; }
public string Col3 { get; set; }
public string Col4 { get; set; }
}
I don't know if this is the best way to do this. But what i would have done in your case, is to rewrite the CSV's the conventionnal way while reading all the files, then create a stream containing the new CSV created.
It would look like something like this :
var csv = new StringBuilder();
csv.AppendLine("Header1;Header2;Header3;Header4");
foreach (var item in file)
{
var newLine = string.Format("{0},{1},{2},{3}", item.value1, item.value2, item.value3, item.value4);
csv.AppendLine(newLine);
}
//Create Stream
MemoryStream stream = new MemoryStream();
StreamReader reader = new StreamReader(stream);
//Fill your data table here with your values
Hope this will help.

How do I make a combobox display a list of dictionaries and act upon the selected value?

C# / Winforms program.
I have the following class which contains my dictionaries:
public class DictionaryInit
{
public Dictionary<int, DictionaryCheckup> C = new Dictionary<int, DictionaryCheckup>()
{
{1000, new DictionaryCheckup {theGrouping="C"}},
{100, new DictionaryCheckup {theGrouping="C"}},
};
}
Where DictionaryCheckup is a class that get;sets; a string theGrouping.
In the class, I would have letters from C to T, and I wanted to display their values within a combo box. This is what I've tried:
var theDictionaries = new DictionaryInit();
List<Dictionary<int, DictionaryCheckup>> Dictionaries = new List<Dictionary<int, DictionaryCheckup>> { theDictionaries.C, etc };
cmbDictionList.DataSource = new BindingSource(Dictionaries, null);
Running this fills the box with [Collection].
The process and desired outcome:
The idea is that, the user first selects a dictionary (C-T) from the combo box and the value gets saved to a variable. I then have the following code that will make use of this:
OFD.ShowDialog();
var theDict = new DictionaryInit();
if (OFD.FileName != null)
{
using (var stream = new StreamReader(File.OpenRead(OFD.FileName)))
{
// Read our JSON from the file
var json = stream.ReadToEnd();
theDict.E = JsonConvert.DeserializeObject<Dictionary<int, DictionaryCheckup>>(json);
var files = new Dictionary<string, Dictionary<int, DictionaryCheckup>>();
}
}
As you can see in my current process, I am explicitly declaring theDict.E. I wish to be able to replace it with a variable I picked up from the combo box earlier, so that I may choose which dictionary I serialize / deserialize.
I want to be able to somehow use my DictionaryInit class as the datasource of the combo box.
The value selected will determine the dictionary I will serialize in a later method.
If all DictionaryCheckup inside DictionaryInit.C have the same letter I would do it like this:
Add Letter property to DictionaryInit
Bind ComboBox to List
Set ComboBox's DisplayMember = "Letter"
Code:
public class DictionaryInit
{
public string Letter { get; private set; }
public DictionaryInit(string letter)
{
this.Letter = letter;
C = new Dictionary<int, DictionaryCheckup>()
{
{1000, new DictionaryCheckup {theGrouping=letter}},
{100, new DictionaryCheckup {theGrouping=letter}},
};
}
public Dictionary<int, DictionaryCheckup> C { get; private set; }
}
var list = new List<DictionaryInit>();
list.AddRange(new[]{new DictionaryInit("C"), new DictionaryInit("D")});
cmbDictionList.DataSource = list;
cmbDictionList.DisplayMember = "Letter";

Bug in C# System.Collections.Generic.List<T>?

I'm writing a simple code to read some data from a text file and storing in a C# List but having problems with it. Please help if the problem is at my side or is it the library. I've written the following function :
public List<EmpBO> ReadData()
{
EmpBO temp = new EmpBO();
List<EmpBO> lis = new List<EmpBO>(100);
string[] tokens;
string data;
StreamReader sw = new StreamReader(new FileStream("emp.txt",FileMode.OpenOrCreate));
int ind = 0;
while ((data = sw.ReadLine())!=null)
{
Console.WriteLine("Reading " + data);
tokens = data.Split(';');
temp.Id = int.Parse(tokens[0]);
temp.Name = tokens[1];
temp.Salary = double.Parse(tokens[2]);
temp.Br = double.Parse(tokens[3]);
temp.Tax = double.Parse(tokens[4]);
temp.Designation = tokens[5];
//lis.Add(temp);
lis.Insert(ind,temp);
ind++;
}
sw.Close();
Console.WriteLine("Read this material and returning list");
for (int i = 0; i < lis.Count; i++)
{
Console.WriteLine("" + (lis.ElementAt(i)).Name);
}
//foreach (EmpBO ob in lis)
//{
// Console.WriteLine("" + ob.Id + ob.Name);
//}
return lis;
}
File emp.txt Contains:
1;Ahmed;100000;20;1000;manager
2;Bilal;200000;15;2000;ceo
Now as you can see that in while loop, I've displayed what StreamReader has read and it does 2 iterations in this case and displays.
Reading 1;Ahmed;100000;20;1000;manager
Reading 2;Bilal;200000;15;2000;ceo
and as you can see i'm saving this info in temp and inserting in the list.
after the while loop is finished , when I traverse the list for knowing that what is stored in it then it displays:
Read this material and returning list
Bilal
BIlal
Well, the second record is stored in the list twice and 1st record is absent.. What seems to be the problem? I've used Add() method too , and foreach loop for traversing list as you can see it's commented out but the result was same.. Please help
Move this line
EmpBO temp = new EmpBO();
into the while-loop so that it looks like
while ((data = sw.ReadLine())!=null){
EmpBO temp = new EmpBO();
Console.WriteLine("Reading " + data);
tokens = data.Split(';');
temp.Id = int.Parse(tokens[0]);
temp.Name = tokens[1];
temp.Salary = double.Parse(tokens[2]);
temp.Br = double.Parse(tokens[3]);
temp.Tax = double.Parse(tokens[4]);
temp.Designation = tokens[5];
//lis.Add(temp);
lis.Insert(ind,temp);
ind++;
}
You are not creating a new EmpBO for each entry, but more overwriting the same object with the read values and adding it again to the List.
The effect is that you add the same object mutiple times to the List.
In your code you have created the EmpBO object only once. In the second iteration you are modified the value in the same object. you have to create instance for EmpBO inside the while loop like below.
while ((data = sw.ReadLine())!=null)
{
Console.WriteLine("Reading " + data);
tokens = data.Split(';');
EmpBO temp = new EmpBO();
temp.Id = int.Parse(tokens[0]);
temp.Name = tokens[1];
temp.Salary = double.Parse(tokens[2]);
temp.Br = double.Parse(tokens[3]);
temp.Tax = double.Parse(tokens[4]);
temp.Designation = tokens[5];
//lis.Add(temp);
lis.Insert(ind,temp);
ind++;
}
This isn't a direct answer to the question, but your code has other problems.
Both your FileStream and StreamReader should be disposed of after use.
Alternatively, you could write your code like this:
public List<EmpBO> ReadData()
{
return File
.ReadAllLines("emp.txt")
.Select(data =>
{
var tokens = data.Split(';');
return new EmpBO()
{
Id = int.Parse(tokens[0]),
Name = tokens[1],
Salary = double.Parse(tokens[2]),
Br = double.Parse(tokens[3]),
Tax = double.Parse(tokens[4]),
Designation = tokens[5],
};
})
.ToList();
}
That, hopefully, should be even easier.
You've inserted the same object twice. You have to create a new object in the loop otherwise you will override the attributes on each iteration and simply and a reference to the same object over and over again.It's safe to assume that standard operations on the BCL classes work correctly or as Eric Lippert put's it Maybe there's something wrong with the universe but probably not
you simply need to change the start of the loop to this:
while ((data = sw.ReadLine())!=null)
{
EmpBO temp = new EmpBO();
If you try to add same object twice in a list ,it will override values entered first time and will show only values from second object but twice
for example :Take a list ,add a object in it . modify that object ,again add it .
when you try to print values ,you will get values of last object
ob1.a=5;
list1.add(ob1);
// list1[0]-->a-->5
ob1.a=7;
list1.add(ob1);
// list1[0]--->a--->7 list1[1]--->a--->7

Categories

Resources