In my project I have to monitor the status of a decentralized periphery. For this I created a class that mirrors a single data point with name, address etc. Now I want to insert all my single objects of the class DataPoint into a SortedList . The DataPoint objects should be initialized via a DataTable. My problem is that in my Foreach loop I always have the first two of the DataTable. So that I create a SortedList with 39 times the same entry in this example. If the classes DataPoint and DpPeriphyData Class contain the SortedList, the program runs without problems.
private void Cmd_Fbd_Data_List_Click(object sender, EventArgs e)
{
ImportExportExcelToDataGridClass excel = new ImportExportExcelToDataGridClass();
DataTable dataPointList = excel.ImportExceltoDataGrid();
Dgv_Data_List.DataSource = dataPointList;
Dgv_Data_List.ReadOnly = true;
DpPeripheryData InputData = new DpPeripheryData();
foreach (DataRow dataRow in dataPointList.Rows)
{
DataPoint dataPoint = new DataPoint(dataRow); // making a new objekt
InputData.AddDataPoint(dataPoint); // add this class to the Sorted List
}
}
Related
private void ListViewAddMethod(string fItem, string sItem, string tItem, string foItem)
{
List<ReportList> alstNames = new List<ReportList>();
alstNames.Add(new ReportList(fItem, sItem, tItem, foItem));
DataTable dt = new DataTable();
dt.Columns.Add("Particulars", typeof(string));
dt.Columns.Add("Amount", typeof(string));
dt.Columns.Add("Particulars1", typeof(string));
dt.Columns.Add("Amount1", typeof(string));
foreach (var lstNames in alstNames)
{
// Add new Row - inside the foreach loop - to enable creating new row for each object.
DataRow row = dt.NewRow();
row["Amount"] = fItem;
row["Particulars"] = sItem;
row["Particulars1"] = tItem;
row["Amount1"] = foItem;
dt.Rows.Add(row);
dgvProfitAndLoss.DataSource = alstNames;
dgvProfitAndLoss.DataBind();
}
}
This returns only the most recent row added and ignores the others. How do I create an array from parameters passed to a method and bind them to a gridview?
Your code does not look good. First, you are doing a loop over the alstNames and binding it into a gridview over each iteration of this loop. Second, you do not need a DataTable to bind it into a grid and your code you are just creating a DataTable on the heap, adding some Rows and keep it to Garbage Collector remove it, you are not even using it. Third, considering you are doing a method to add a new item on the GRidView, remove the initilization of your list to the scope of your class and keep it a valid instance (or static one if it is the case):
// declare the list output of the method
private private List<ReportList> alstNames = new List<ReportList>();
private void ListViewAddMethod(string fItem, string sItem, string tItem, string foItem)
{
// add the ReportList on the list
alstNames.Add(new ReportList(fItem, sItem, tItem, foItem));
// bind it on the gridView
dgvProfitAndLoss.DataSource = alstNames;
dgvProfitAndLoss.DataBind();
}
I want to display my List<string> in a datatable, from another form. The data from the List<string> is from textbox and combobox. However, the data from textbox is never repeated but the combobox might be similar with previous data displayed. And if this happened, the data displayed in datatable will be “repeated” (I’m unsure on how to describe it). Here is the actual outcome. I would like to see my displayed data to be like this. Below is my code:
Transavestate - class that hold my List
public static List<string> transnumber_list = new List<string>();
public static List<string> combos_list = new List<string>();
Form1 - form that user input values of textbox and combobox
private void button1_Click(object sender, EventArgs e)
{
//Save values in the List
Transavestate.transnumber_list.Add(Textbox1.Text)
Transavestate.combos_list.Add(comboBox2.SelectedItem.ToString());
//Go to Form 2
this.Hide();
Form2 f2 = new Form2 ();
f2.Show();
}
Form2 - form to display values of textbox and combobox
private DataSet ds;
private DataTable dt;
//Method to insert data into dtg1
private void CreateDataSet()
{
ds = new DataSet();
dt = new DataTable("Vehicle Number");
dt.Columns.Add("Column 1", typeof(string));
dt.Columns.Add("Column 2", typeof(string));
foreach (var item in Transavestate.transnumber_list)
{
foreach (var items in Transavestate.combos_list)
{
dt.Rows.Add(item, items);
}
}
ds.Tables.Add(dt);
this.dataGridView1.DataSource = dt;
dataGridView1.AllowUserToAddRows = false;
}
//To run the method
private void dataGridView1_VisibleChanged(object sender, EventArgs e)
{
CreateDataSet();
}
//Go back to Form1
private void button2_Click(object sender, EventArgs e)
{
this.Hide();
Form1 f1 = new Form1();
f1.Show();
}
It seems to me that you have two sequences of strings: a sequence of transNumbers and a sequence of comboItems (in fact they are your transnumber_list and your combos_list, but I don't want to limit myself to the fact that they are Lists, and the names are inconsistent (the first is non-plural, the 2nd is plural?)
Anyway, you say that your sequence of transNumbers contains only unique items, but that your comboItems might contain duplicates (based on some string equality comparer)
Something like this:
IEnumerable<string> transNumbers = new string[] {"1", "2"};
IEnumerable<string> comboItems = new string[] {"A", "A", "A", "B", "A", "B"};
As a result you want something like:
TransNumber ComboItem
"1" "A"
"1" "B"
"2" "A"
"2" "B"
So you want to combine every TransNumber, with every unique comboItem.The order seems not to be important.
With LINQ This is fairly easy. We'll use Enumerable.SelectMany to combine the two sequences and we'll use Enumerable.Distinct to get rid of your duplicates
// SelectMany:
// parameter source: transNumbers
// parameter collectionSelector: comboItems without Duplicates
var transNumberCombiItemCombinations = transNumbers.SelectMany(
// collectionSelector: comboItems without duplicates
combiItems.Distinct(),
// ResultSelector, take a transNumber from the source,
// and a comboItem from collectionSelector to make a new object
(transNumber, comboItem => new
{
Column1 = transNumber,
Column2 = comboItem,
});
If you remove all comments, you'll see that it is really a small piece of code.
Distinct will remove the duplicates. If you consider "myname" and "MYNAME" to be equal, you'll need to provide an IEqualityComparer<string>, for instance StringComparer.OrdinalIgnoreCase.
SelectMany" will do your foreach inside foreach.
The resultSelector will take one transNumber and one comboItem as input to create the output you want. In this case, one object that has two properties: Column1 and Column2.
The result is an IEnumerable. All you have to do is enumerate it into a list or something and add the result to your rows collection:
dt.Rows.AddRange(transNumberCombiItemCombinations.ToList());
I am getting an error related to foreach which states:
Foreach statement cannot operate on type ' ' because it does not contain a public definition for 'GetEnumerator'.
I am not getting what to do with this error. Please suggest the changes to be made to improve and correct the error in the code.
public partial class Form1 : Form
{
store store = new store();
public List<item> insert= new List<item>();//this is the list of second list items
BindingSource itemslist = new BindingSource();
BindingSource selectitem2 = new BindingSource();
public Form1()
{
InitializeComponent();
setupdata();
itemslist.DataSource = store.items;
item.DataSource = itemslist;//linked between the two
// what to print inside
item.DisplayMember = "Display";
item.ValueMember = "Display";
//put the data to the selected item list
selectitem2.DataSource=insert;
selected_item.DataSource = selectitem2;
selected_item.DisplayMember = "Display";
selected_item.ValueMember = "Display";
}
private void purchase_Click(object sender, EventArgs e)
{
foreach (item item in selected_item)
{
}
}
}
you cannot iterate through a control (ListBox).
It has no enumerator as the error message tells you. You need to iterate through the underlying data collection.
In your case it would be insert
so change the loop line into this:
foreach (item item in insert)
you can though of course iterate through the Items of the ListBox:
foreach (item item in selected_item.Items)
EDIT:
Also your naming pattern is frankly rather confusing. selected_item does not sound as if it would represent a collection of things but rather one entity.
I am trying to write a program that prints out (in a string variable) the following information about an mdb database:
Table Name
Total number of columns of the table
List of columns as follows:
Column Name:
Column Data Type:
To accomplish this I used two custom types (public classes) and of course, lists. Here is the code I have so far (which by the way has been adjusted not in small part thanks to questions and answers gathered here):
Here are the classes I created to define the two new types I am using:
public class ClmnInfo
{
public string strColumnName { get; set; }
public string strColumnType { get; set; }
}
public class TblInfo
{
public string strTableName { get; set; }
public int intColumnsQty { get; set; }
public List<ClmnInfo> ColumnList { get; set; }
}
Here is the code that actually gets the data. Keep in mind that I am using OleDB to connect to the actual data and everything works fine, except for the problem I will describe below.
As a sample, I am currently testing this code with a simple 1 table db, containing 12 columns of type string save for 1 int32 (Long Int in Access).
//Here I declare and Initialize all relevant variables and Lists
TblInfo CurrentTableInfo = new TblInfo();
ClmnInfo CurrentColumnInfo = new ClmnInfo();
List<TblInfo> AllTablesInfo = new List<TblInfo>();
//This loop iterates through each table obtained and imported previously in the program
int i = 0;
foreach (DataTable dt in dtImportedTables.Tables)
{
CurrentTableInfo.strTableName = Globals.tblSchemaTable.Rows[i][2].ToString(); //Gets the name of the current table
CurrentTableInfo.intColumnsQty = dt.Columns.Count; //Gets the total number of columns in the current table
CurrentTableInfo.ColumnList = new List<ClmnInfo>(); //Initializes the list which will house all of the columns
//This loop iterates through each column in the current table
foreach (DataColumn dc in dt.Columns)
{
CurrentColumnInfo.ColumnName = dc.ColumnName; // Gets the current column name
CurrentColumnInfo.ColumnType = dc.DataType.Name; // Gets the current column data type
CurrentTableInfo.ColumnList.Add(CurrentColumnInfo); // adds the information just obtained as a member of the columns list contained in CurrentColumnInfo
}
//BAD INSTRUCTION FOLLOWS:
AllTablesInfo.Add(CurrentTableInfo); //This SHOULD add The collection of column_names and column_types in a "master" list containing the table name, the number of columns, and the list of columns
}
I debugged the code and watched all variables. It works great (the table name and column quantity gets registered correctly, as well as the list of column_names, column_types for that table), but when the "bad" instruction gets executed, the contents of AllTablesInfo are not at all what they should be.
The table name is correct, as well as the number of columns, and the columns list even has 12 members as it should have, but each member of the list is the same, namely the LAST column of the database I am examining. Can anyone explain to me why CurrentTableInfo gets overwritten in this manner when it is added to the AllTablesInfo list?
You're creating a single TblInfo object, and then changing the properties on each iteration. Your list contains lots of references to the same object. Just move this line:
TblInfo CurrentTableInfo = new TblInfo();
to the inside of the first loop, and this line:
ClmnInfo CurrentColumnInfo = new ClmnInfo();
inside the nested foreach loop, so that you're creating new instances on each iteration.
Next:
Important
Make sure you understand why it was failing before. Read my article on references if you're not sure how objects and references (and value types) work in C#
Use camelCased names instead of CamelCased ones for local variables
Consider using an object initializer for the ClmnInfo
Change your type names to avoid unnecessary abbreviation (TableInfo, ColumnInfo)
Change your property names to avoid pseudo-Hungarian notation, and make them PascalCased
Consider rewriting the whole thing as a LINQ query (relatively advanced)
The pre-LINQ changes would leave your code looking something like this:
List<TableInfo> tables = new List<TableInfo>();
int i = 0;
foreach (DataTable dt in dtImportedTables.Tables)
{
TableInfo table = new TableInfo
{
Name = Globals.tblSchemaTable.Rows[i][2].ToString(),
// Do you really need this? Won't it be the same as Columns.Count?
ColumnCount = dt.Columns.Count,
Columns = new List<ColumnInfo>()
};
foreach (DataColumn dc in dt.Columns)
{
table.Columns.Add(new ColumnInfo {
Name = dc.ColumnName,
Type = dc.DataType.Name
});
}
tables.Add(table);
// I assume you meant to include this?
i++;
}
With LINQ:
List<TableInfo> tables =
dtImportedTables.Tables.Zip(Globals.tblSchemaTable.Rows.AsEnumerable(),
(table, schemaRow) => new TableInfo {
Name = schemaRow[2].ToString(),
// Again, only if you really need it
ColumnCount = table.Columns.Count,
Columns = table.Columns.Select(column => new ColumnInfo {
Name = column.ColumnName,
Type = column.DataType.Name
}).ToList()
}
}).ToList();
You have only created one instance of TblInfo.
It's because you only have a single instance of TblInfo, which you keep updating in your loop and then add another reference to it to the List. Thus your list has many references to the same object in memory.
Move the creation of the CurrentTableInfo instance inside the for loop.
I'm reading a text file line by line, and inserting it into an array.
I then have this list called custIndex, which contains certain indices, indices of the items array that I'm testing to see if they are valid codes. (for example, custIndex[0]=7, so I check the value in items[7-1] to see if its valid, in the two dictionaries I have here). Then, if there's an invalid code, I add the line (the items array) to dataGridView1.
The thing is, some of the columns in dataGridView1 are Combo Box Columns, so the user can select a correct value. When I try adding the items array, I get an exception: "The following exception occurred in the DataGridView: System.ArgumentException: DataGridViewComboBoxCell value is not valid."
I know the combo box was added correctly with the correct data source, since if I just add a few items in the items array to the dataGridView1, like just items[0], the combo box shows up fine and there's no exception thrown. I guess the problem is when I try adding the incorrect value in the items array to the dataGridView1 row.
I'm not sure how to deal with this. Is there a way I can add all of the items in items except for that value? Or can I add the value from items and have it show up in the combo box cell, along with the populated drop down items?
if(choosenFile.Contains("Cust"))
{
var lines = File.ReadAllLines(path+"\\"+ choosenFile);
foreach (string line in lines)
{
errorCounter = 0;
string[] items = line.Split('\t').ToArray();
for (int i = 0; i <custIndex.Count; i++)
{
int index = custIndex[i];
/*Get the state and country codes from the files using the correct indices*/
Globals.Code = items[index - 1].ToUpper();
if (!CountryList.ContainsKey(Globals.Code) && !StateList.ContainsKey(Globals.Code))
{
errorCounter++;
dataGridView1.Rows.Add(items);
}
}//inner for
if (errorCounter == 0)
dataGridView2.Rows.Add(items);
}//inner for each
}//if file is a customer file
Say your text file contains:
Australia PNG, India Africa
Austria Bali Indonisia
France England,Scotland,Ireland Greenland
Germany Bahama Hawaii
Greece Columbia,Mexico,Peru Argentina
New Zealand Russia USA
And lets say your DataGridView is setup with 3 columns, the 2nd being a combobox.
When you populate the grid and incorrectly populate the combobox column you will get the error.
The way to solve it is by "handling/declaring explicitly" the DataError event and more importantly populating the combobox column correctly.
private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
//Cancelling doesn't make a difference, specifying the event avoids the prompt
e.Cancel = true;
}
private void dataGridView2_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
e.Cancel = true;
}
So imagine the 2nd column contained a dropdownlist of countries and the 1st & 3rd column contained text fields.
For the 1st and 3rd columns they are just strings so I create a class to represent each row:
public class CountryData
{
public string FirstCountry { get; set; }
public string ThirdCountry { get; set; }
}
For the 2nd column "Countries" combobox cell's I have created a separate class because I will bind it to the 2nd columns datasource.
public class MultiCountryData
{
public string[] SeceondCountryOption { get; set; }
}
Populating the grid with combobox columns and the like as shown here: https://stackoverflow.com/a/1292847/495455 is not good practice. You want to separate your business logic from your presentation for a more encapsulated, polymorphic and abstract approach that will ease unit testing and maintenance. Hence the DataBinding.
Here is the code:
namespace BusLogic
{
public class ProcessFiles
{
internal List<CountryData> CountryDataList = new List<CountryData>();
internal List<MultiCountryData> MultiCountryDataList = new List<MultiCountryData>();
internal void foo(string path,string choosenFile)
{
var custIndex = new List<int>();
//if (choosenFile.Contains("Cust"))
//{
var lines = File.ReadAllLines(path + "\\" + choosenFile);
foreach (string line in lines)
{
int errorCounter = 0;
string[] items = line.Split('\t');
//Put all your logic back here...
if (errorCounter == 0)
{
var countryData = new CountryData()
{
FirstCountry = items[0],
ThirdCountry = items[2]
};
countryDataList.Add(countryData);
multiCountryDataList.Add( new MultiCountryData() { SeceondCountryOption = items[1].Split(',')});
}
//}
}
}
}
In your presentation project here is the button click code:
imports BusLogic;
private void button1_Click(object sender, EventArgs e)
{
var pf = new ProcessFiles();
pf.foo(#"C:\temp","countries.txt");
dataGridView2.AutoGenerateColumns = false;
dataGridView2.DataSource = pf.CountryDataList;
multiCountryDataBindingSource.DataSource = pf.MultiCountryDataList;
}
I set dataGridView2.AutoGenerateColumns = false; because I have added the 3 columns during design time; 1st text column, 2nd combobox column and 3rd text column.
The trick with binding the 2nd combobox column is a BindingSource. In design time > right click on the DataGridView > choose Edit Columns > select the second column > choose DataSource > click Add Project DataSource > choose Object > then tick the multiCountry class and click Finish.
Also set the 1st column's DataPropertyName to FirstCountry and the 3rd column's DataPropertyName to ThirdCountry, so when you bind the data the mapping is done automatically.
Finally, dont forget to set the BindingSource's DataMember property to the multiCountry class's SeceondCountryOption member.
Here is a code demo http://temp-share.com/show/HKdPSzU1A