I am relatively new to posting on here but have been stuck on this loop for quite sometime. In the commented section of the code I have successfully written a for loop that does what it is intended to. I have even gone as far as to make sure that I was successfully getting the filtered row count and number of groups from the textbox that the users fills in. However, I need a loop that iterates through the filtered datatable and assigns each row to a group that is set by the user and only if supported based on row count divided by the min number that a team can hold and not exceeding the max number that can be on a team. Finally the results with the added column showing what group the row is in needs to populate another datagridview in this case datagridview2. All of this takes place on a button click. Any help or advice is greatly appreciated. Thanks....
private void btnAssign_Click(object sender, EventArgs e)
{
Random rnd = new Random();
const double maxTeam = 16.00;
const double minTeam = 6.00;
double peopleCnt = 0.00;
int numGroups = Convert.ToInt32(txtNumGroups.Text);
int j = 1;
double totPeopleCnt = Convert.ToDouble(fdt.Rows.Count);
//MessageBox.Show(totPeopleCnt.ToString()+"&"+numGroups.ToString());
//fdt.Columns.Add(new DataColumn("RandomNumber", Type.GetType("System.Int32")));
// for (int i = 0; i < fdt.Rows.Count; i++)
// {
// fdt.Rows[i]["RandomNumber"] = rnd.Next();
// }
// DataView fdv = new DataView(fdt);
// fdv.Sort = "RandomNumber";
// dataGridView2.DataSource = fdv.ToTable();
//fdt.Columns.Add(new DataColumn("RandomNumber", Type.GetType("System.Int32")));
if (totPeopleCnt / minTeam < Convert.ToDouble(numGroups) || totPeopleCnt / maxTeam > Convert.ToDouble(numGroups))
if (totPeopleCnt / minTeam < Convert.ToDouble(numGroups))
{
MessageBox.Show("Number of Players entered will not meet team minimums for " + txtNumGroups.Text + " Teams.");
}
else
{
for (int i = 0; i < totPeopleCnt; i++)
{
peopleCnt++;
fdt.Rows[i][j] = rnd.Next();
j = (j == numGroups ? 1 : j + 1);
}
DataView fdv = new DataView(fdt);
fdv.Sort = numGroups.ToString();
dataGridView2.DataSource = fdv.ToTable();
}
}
Related
I want to use for loop to get average of datatable columns and rows. What I want to do is that what if there are 100 ~ 1000 columns and rows, I can't keep on adding them in the code. is there one simple code that can get average of automatically as I add columns and rows?
here is my code, I am stuck I don't know what to write in ?? area below and this code gets me error please help...
private void button1_Click(object sender, EventArgs e)
{
DataTable dtGrid = gridData.DataSource as DataTable;
DataTable dtResult = new DataTable();
Math columnIndex = new Math();
List<double> avgList = new List<double>();
for (int i = 0; i < dtGrid.Columns.Count; i++)
{
for (int k = 1; k < dtGrid.Rows.Count; k++)
{
// ??
avgList.Add(Convert.ToDouble(dtGrid.Rows[i].ToString()));
}
}
//this is from other class name Math
/* public double getAverageValue(List<double> avgList)
{
double averageList = 0;
averageList = MathNet.Numerics.Statistics.Statistics.Mean(avgList.ToList());
return averageList;
}*/
double averageX1 = columnIndex.getAverageValue(avgList);
List<Math> list = new List<Math>();
//using get; set from other class
list.Add(new Math { Result = "Average", X1 = averageX1.ToString() });
gridData2.DataSource = list;
}
}
}
It looks like your loop is inside out. Try this:
DataTable dtGrid = gridData.DataSource as DataTable;
DataTable dtResult = new DataTable();
Math columnIndex = new Math();
List<double> avgList = new List<double>();
for (int k = 1; k < dtGrid.Rows.Count; k++)
{
for (int i = 0; i < dtGrid.Columns.Count; i++)
{
// ??
avgList.Add(Convert.ToDouble(dtGrid.Rows[k].Columns[i].ToString()));
}
}
This logic averages all columns in a row together. If you need,, you can create a Dictionary and average each column separately. Something like thisL
Dictionary<int, List<double>> AvgColumnList = new Dictionary<int, System.Collections.Generic.List<double>>();
This uses a dictionary that contains a list for each column in the row. If there are 100 columns, then there will be 100 entries in the dictionary with index 0 - 99. Each dictionary item will contain a list of doubles.
for (int k = 1; k < dtGrid.Rows.Count; k++)
{
for (int i = 0; i < dtGrid.Columns.Count; i++)
{
if (!AvgColumnList.Keys.Contains(i))
AvgColumnList.Add(i, new List<double>());
AvgColumnList[i].Add(Convert.ToDouble(dtGrid.Rows[k].Columns[i].ToString()));
}
}
DataTable is zero index based, in your code row count started from 1 it should be 0, also dtGrid.Rows[i] is a row not the cell value. Use below code to loop through each cell of a DataTable
Update : Code updated as OP want to save each column data separately and irrespective of column numbers.
List<List<double>> perColumnAvg = new List<List<double>>();
for (int i = 0; i < dtGrid.Columns.Count; i++)
{
avgList = new List<double>();
for (int k = 0; k < dtGrid.Rows.Count; k++)
{
// ??
avgList.Add(Convert.ToDouble(dtGrid.Rows[k][i].ToString()));
}
perColumnAvg.Add(avgList);
}
Now you can compute individual column average as
foreach (var columnList in perColumnAvg)
{
// place your logic here.
columnIndex.getAverageValue(columnList);
}
And can compute avg across table using.
double tableAvg = columnIndex.getAverageValue(perColumnAvg.SelectMany(s=>s));
Currently I am using the below segement of code to get a row with a value int in the 3rd column.
private int getRowByRDS(int id)
{
int bestfit = -1;
Boolean foundOne = false;
for (int i = 2; i < oSheet.Rows.Count; i++)
{
string val = getValueOfCell(i, 3);
if (val == null)
continue;
int rds = int.Parse(val);
if (rds == id)
{
bestfit = i;
foundOne = true;
}
else
if (foundOne)
return bestfit;
}
return bestfit;
}
The issues is that this is pathetically show at large amount of rows.
Can someone suggest a better way of searching col 3 for a int and returning the last row # that it is in.
For Joe:
public void inputRowData(string[] data, int rds)
{
int bestRow = getRowByRDS_a(rds);
string[] formatedData = formatOutput(bestRow, data);
string val = getValueOfCell(bestRow, 6);
if (val != null)
{
shiftRows(bestRow, data.Length);
bestRow++;
}
else
shiftRows(bestRow, data.Length - 1);
// transform formated data into string[,]
string[][] splitedData = formatedData.Select(s => s.Split('\t')).ToArray();
var colCount = splitedData.Max(r => r.Length);
var excelData = new string[splitedData.Length, colCount];
for (int i = 0; i < splitedData.Length; i++)
{
for (int j = 0; j < splitedData[i].Length; j++)
{
excelData[i, j] = splitedData[i][j];
}
}
oSheet.get_Range("A" + bestRow.ToString()).Resize[splitedData.Length, colCount].Value = excelData;
MainWindow.mainWindowDispacter.BeginInvoke(new System.Action(() => MainWindow.mainWindow.debugTextBox.AppendText("Done with " + rds + " input!" + Environment.NewLine)));
}
private void shiftRows(int from, int numberof)
{
from++;
Range r = oXL.get_Range("A" + from.ToString(), "A" + from.ToString()).EntireRow;
for (int i = 0; i < numberof; i++)
r.Insert(Microsoft.Office.Interop.Excel.XlInsertShiftDirection.xlShiftDown);
}
Here's how I'd do it:
Get a Range corresponding to the column you're interested in
Get the UsedRange of the sheet you're interested in
Get a Range that is the intersection of the above two ranges
Get the value of this Range, which will be an array of values from the column you're interested in
You can then iterate through this array to find the value you want, then use its index to derive the row number.
The number of calls to the Excel is O(1) with the above method, as opposed to O(n) in your version.
You could also use the Find method of the Excel.Range object. This method returns a Excel.Range object if a match is found or null. Follow the first three steps that Joe described to create the search range. If the Find method returns a valid Range, you can then use it's Row Property.
P.S.: Sorry Joe I wanted to upvote your answer, but I am not allowed to do so yet.
I am creating a calculator where the user enters a number into a textbox specifing how many inputs (textboxes) the user wants to have (Code not shown). I have used a textbox array to create these textboxes. The problem comes when I want to get the text from these textboxes to perform the calculations, the code I have written so far for this is shown below:
int n;
TextBox[] textBoxes;
Label[] labels;
double[] values;
public void GetValue()
{
n = Convert.ToInt16(txtInputFields.Text);
values = new double[n];
textBoxes = new TextBox[n];
for (int i = 0; i < n; i++)
{
}
}
I am unsure what to put in the for loop for this; I have tried the following:
values[n] = Convert.toDouble(textBoxes[n].Text);
but it gives me the error: Index was outside the bounds of the array.
I am new to C# and programming in general so any help would be much appreciated.
Thanks.
EDIT: Code to create textboxes is shown here:
public void InstantiateTextFields()
{
n = Convert.ToInt16(txtInputFields.Text);
int posLeft = 100;
textBoxes = new TextBox[n];
labels = new Label[n];
// Creates number of inputs and labels as specified in txtInputFields (n).
for (int i = 0; i < n; i++)
{
textBoxes[i] = new TextBox();
textBoxes[i].Top = 100 + (i * 30);
textBoxes[i].Left = posLeft;
textBoxes[i].Name = "txtInput" + (i + 1);
labels[i] = new Label();
labels[i].Top = 100 + (i * 30);
labels[i].Left = posLeft - 50;
labels[i].Text = "Input " + (i + 1);
labels[i].Name = "lblInput" + (i + 1);
}
for (int i = 0; i < n; i++)
{
this.Controls.Add(textBoxes[i]);
this.Controls.Add(labels[i]);
}
}
Your code in the GetValue method recreates the array of textboxes and doing so destroys the orginal content (the textboxes dynamically created InstantiateTextFields).
In this way your loop fails with Object Reference not set.
You just need to use the global variable without reinitiaizing it
public void GetValue()
{
n = Convert.ToInt16(txtInputFields.Text);
values = new double[n];
// textBoxes = new TextBox[n];
for (int i = 0; i < n; i++)
{
values[i] = Convert.ToDouble(textBoxes[i].Text);
}
}
There is something to be said about reading the input text and converting it to double without checks. If your user types something that cannot be converted to a double your code will crash on the Convert.ToDouble line. Use instead
double temp;
for (int i = 0; i < n; i++)
{
if(double.TryParse(textBoxes[i].Text, out temp)
values[i] = temp;
else
{
// Not a double value....
// A message to your user ?
// fill the array with 0 ?
// Your choice....
}
}
values[n] = Convert.toDouble(textBoxes[n].Text); gives you error because n is outside of the array. You allocate an array with the size of n which is zero indexed aka the last element is at position n-1.
for (int i = 0; i < n; i++)
{
values[i] = Convert.toDouble(textBoxes[i].Text);
}
Hello every one I am working on my college project I am trying to make an windows application using sql server 2008 and visual c#. I am using grid view for input. Problem is when a new row is added the string does not increment. I mean my empid is at row[i] and cell[0] when i go on to next row the empid is same as in previous row according to my code it should increase by 1 from initial value The result which i want is
row[0]cell[0] = emp6;
row[1]cell[0] = emp7;
row[2]cell[0] = emp8;
row[3]cell[0] = emp9;
and yes i don't want to use row count (digit) as a Id integer because of duplicacy of employeeID in database so to avoid this i am using SQL COUNT to get initial value for ID.
here is my code please help me soon
void emp_id_generator(DataGridView dataGridView1)
{
if (dataGridView1 != null)
{
for (int count = 0; (count <= (dataGridView1.Rows.Count - 2)); count++)
{
int j = 6; //this is what i want to increment
Label l = new Label();
l.Text = "EmpID" + (j);
dataGridView1.Rows[count].Cells[0].Value = l.Text;
j=j+1;
}
}
}
Try this way.
J must be initialized outside the loop.
Have a nice day.
void emp_id_generator(DataGridView dataGridView1)
{
if (dataGridView1 != null)
{
int j = 6; //this is what i want to increment
for (int count = 0; (count <= (dataGridView1.Rows.Count - 2)); count++)
{
Label l = new Label();
l.Text = "EmpID" + (j);
dataGridView1.Rows[count].Cells[0].Value = l.Text;
j=j+1;
}
}
}
I'm trying to add very large number of columns into datagridview and I faced a challenging problem. When column count becomes too large the code below adds columns very slowly. This depends upon size value - when it less than 10000 I get more or less good result(2-4 seconds to add this number of columns), but when size grows up to 15000 or more the time of adding is not proportional at all, for 30000 of columns it can reach 2 minutes instead of 20-30 seconds as I expect. So my question is it possible to optimize this procedure somehow?
datagridview.SuspendLayout();
int size = 10000;
var columns = new DataGridViewColumn[size];
for (int i = 0; i < size; i++)
{
columns[i] = new DataGridViewTextBoxColumn();
columns[i].Name = "col" + i;
columns[i].HeaderText = "col" + i;
columns[i].FillWeight = 0.00001f;
}
datagridview.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
Array.ForEach(columns, item => datagridview.Columns.Add(item));
datagridview.ResumeLayout();
And this is what I've done:
int visibleColumns = 20;
string[] headers;
DataGridViewColumn[] columns;
HScrollBar hbar = new HScrollBar();
public Constructor(){
...
int sizeDezired = 15000;
int size = Math.Min(sizeDezired, visibleColumns);
columns = new DataGridViewColumn[size];
headers = new string[sizeDezired];
for (int i = 0; i < size; i++)
{
columns[i] = new DataGridViewTextBoxColumn();
columns[i].Name = "col" + i;
columns[i].HeaderText = "col" + i;
columns[i].FillWeight = 0.00001f;
}
for (int i = 0; i < sizeDezired;i++ )
{
headers[i] = "col" + i;
}
if (sizeDezired > size)
{
hbar.Maximum = sizeDezired - size;
hbar.Minimum = 0;
hbar.Value = 0;
}
hbar.Scroll += hbar_Scroll;
...
}
void hbar_Scroll(object sender, ScrollEventArgs e)
{
for (int i = 0; i < datagridview.ColumnCount; i++)
{
datagridview.Columns[i].HeaderText = headers[i + e.NewValue];
}
}
I added this code:
int visibleColumns = 20;// columns that are in data grid view
string[] headers;// headers for all desired columns
DataGridViewColumn[] columns;
HScrollBar hbar = new HScrollBar();
public Constructor(){
...
int sizeDesired = 15000;
int size = Math.Min(sizeDesired, visibleColumns);
columns = new DataGridViewColumn[size];
headers = new string[sizeDesired];
for (int i = 0; i < size; i++)
{
columns[i] = new DataGridViewTextBoxColumn();
columns[i].Name = "col" + i;
columns[i].HeaderText = "col" + i;
columns[i].FillWeight = 0.00001f;
}
for (int i = 0; i < sizeDesired;i++ )
{
headers[i] = "col" + i;
}
if (sizeDesired > size)
{
hbar.Maximum = sizeDesired - size;
hbar.Minimum = 0;
hbar.Value = 0;
}
hbar.Scroll += hbar_Scroll;
...
}
void hbar_Scroll(object sender, ScrollEventArgs e)
{
for (int i = 0; i < datagridview.ColumnCount; i++)
{
datagridview.Columns[i].HeaderText = headers[i + e.NewValue];
}
}
Here horizontal scroll bar added to cycle through all invisible columns and shift column headers to visually "scroll" through all columns(15000 in this example) but in reality only 20 columns are present. This code does not use any databinding, only headers are changing so you need to modify hbar_Scroll handler to show relevant data in cells.
The technique you are looking for is called pagination. Look at the following reference
"Paging is a great way to limit the amount of data displayed to the
user at one time, but is also a very good way of stopping lots of data
being transmitted across the network, being held in memory or big
queries on databases... It solves many problems.
The most common solution is (just like on a Google search) is you get
shown a list of pages and you can navigate around pages either going
up / down a page at a time or clicking on a page number.
Another way is to make it seem like there is actually one big list but
page behind the scenes so the user feels like they are actually
viewing one big list. An example where you see this done is in TFS.
You can do this with a windows forms data grid (DataGridView).