I'm working on a chart in asp.net that is dinamically created from a DataTable. Basically I've got a DataTable like this:
So, what I want in my chart is a column for each differente location, and for each diferent Type I want to have a "portion" of the column, meaning I would like to have a column per location divided per type.
I have managed to do something like this, but not quite what I would like:
Now I'll try to explain my code.
First, i add the different locations in my datatable to a list of strings
string loc = "";
for (int i = 0; i < dtTipos.Rows.Count; i++)
{
if (loc != dtTipos.Rows[i]["Location"].ToString())
listaLocais.Add(dtTipos.Rows[i]["Location"].ToString());
loc = dtTipos.Rows[i]["Location"].ToString();
}
After, for each different location, I select the rows to the corresponding location and create a series for each one of them. I proceed to create a point with y=0 and one with y=total
for (int i = 0; i < listaLocais.Count; i++)
{
DataRow[] lRow = dtTipos.Select("Location = '" + listaLocais[i] + "'");
foreach (DataRow dr in lRow)
{
string serie = dr["Location"].ToString().Substring(0, 2) + "_" + dr["Type"].ToString() + " - " + dr["Total"].ToString();
Series s = new Series();
s.Name = serie;
s.Label = dr["Location"].ToString();
s.ChartType = SeriesChartType.StackedColumn;
chartTipo1.Series.Add(s);
s.Points.AddXY(i + 1, 0);
s.Points[0].Label = " ";
s.Points.AddXY(i + 1,Convert.ToInt32(dr["Total"].ToString()));
s.Points[1].Label = " ";// dtTipos.Rows[i]["Total"].ToString();
}
}
The thing is: I think that because I create a serie for each row, everytime I use a diferent column, the y axis starts from where I left it.
Is there any way so I can get this to work?
I'll appreciate any help.
You're adding your series and data points in the same loop. First add your series, then your data points:
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
DataTable dt = new MyDataTable();
//first add your series
foreach (DataRow row in dt.DefaultView.ToTable(true, new string[] { "Type" }).Rows)
{
Series series = new Series();
series.Name = (string)row["Type"];
series.ChartType = SeriesChartType.StackedColumn;
Chart1.Series.Add(series);
}
// then add your points;
foreach (DataRow row in dt.Rows)
Chart1.Series[(string)row["Type"]].Points.AddXY(row["Location"], new object[] { row["Total"] });
}
}
Related
I have this code that copy selected rows from one grid to another
private void btnAddEmployee_Click(object sender, EventArgs e)
{
LayoutControl lc = new LayoutControl();
lc.Dock = DockStyle.Top;
LookUpEdit userShift = new LookUpEdit();
userShift.Properties.TextEditStyle = TextEditStyles.DisableTextEditor;
userShift.Properties.DataSource = paint.GetShiftTime();
userShift.Properties.DisplayMember = "ShiftTime";
userShift.Properties.ValueMember = "id";
userShift.Properties.ShowHeader = false;
var date = DateTime.Now;
if (8 < date.Hour && date.Hour < 16)
{
userShift.EditValue = 1;
}
else if (16 < date.Hour && date.Hour < 24)
{
userShift.EditValue = 2;
}
else
{
userShift.EditValue = 3;
}
lc.AddItem(Resources.workingHours, userShift).TextVisible = true;
lc.Height = 50;
this.Controls.Add(lc);
this.Dock = DockStyle.Top;
int[] selectedRows = gridView4.GetSelectedRows();
for(int n=0;n< selectedRows.Length;n++)
//foreach (int index in selectedRows)
{
if (DevExpress.XtraEditors.XtraDialog.Show(lc, Resources.options, MessageBoxButtons.OKCancel) == DialogResult.OK)
{
//Prevent duplicate data
for (int i = 0; i < gridView5.RowCount; i++)
{
if (gridView4.GetRowCellValue(gridView4.FocusedRowHandle, "Matricule").ToString() == gridView5.GetRowCellValue(i, "Matricule").ToString())
{
XtraMessageBox.Show(Resources.employeeAlreadyAdded, Resources.error, MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
}
DataRow r = EmplDT.NewRow();
r[0] = gridView4.GetRowCellValue(gridView4.FocusedRowHandle, "Matricule").ToString();
r[1] = gridView4.GetRowCellValue(gridView4.FocusedRowHandle, "Employé").ToString();
r[2] = userShift.Text;
r[3] = userShift.EditValue;
r[4] = txtDate.EditValue;
EmplDT.Rows.Add(r);
this is my code to create Columns in gridview 5
DataTable EmplDT = new DataTable();
void CreateEmployeeTable()
{
EmplDT.Columns.Add("Matricule");
EmplDT.Columns.Add("Employé");
EmplDT.Columns.Add("Heure");
EmplDT.Columns.Add("idShiftTime", typeof(Int32));
EmplDT.Columns.Add("Date", typeof(DateTime));
gridControl5.DataSource = EmplDT;
gridView5.Columns["idShiftTime"].Visible = false;
gridView5.Columns["Date"].Visible = false;
}
i have two problems in this code:
the first one is when i run the code it only add the first record and then i get error message of duplicate .
the second one i want to show layout control only the first time.
thanks in advance and sorry for my english.
Looping thru selected rows from a devexpress gridview and getting a row can be much simpler like this
int[] selectedRows = gridView4.GetSelectedRows();
for (int i = 0; i < selectedRows.Length; i++)
{
// Get a DataRow and fill it with all values from the this selected row
// This is where you went wrong, you kept using only the first selected row
DataRow rowGridView4 = (gridView4.GetRow(selectedRows[i]) as DataRowView).Row;
// Do a check for doubles here
DataRow[] doubles = EmplDT.Select("Matricule = '" + rowGridView4[0].ToString() +"'");
if (doubles.Length > 0)
{
XtraMessageBox.Show(Resources.employeeAlreadyAdded, Resources.error, MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
// fix for error "This row already belongs to another table"
DataRox row = EmplDT.NewRow();
row[0] = rowGridView4[0];
row[1] = rowGridView4[1];
row[2] = userShift.Text;
row[3] = userShift.EditValue;
row[4] = txtDate.EditValue;
EmplDT.Rows.Add(row);
}
Please note that with testing for doubles at this place will cause all records to be copied until a duplicate is found. So after your error message there might be some records copied and some not.
Is that how you intended this ?
I would leave out the error message and just skip duplicate records. You can still show a message with howmany records where copied if you want.
int[] selectedRows = gridView4.GetSelectedRows();
for (int i = 0; i < selectedRows.Length; i++)
{
// Get a DataRow and fill it with all values from the this selected row
// This is where you went wrong, you kept using only the first selected row
DataRow rowGridView4 = (gridView4.GetRow(selectedRows[i]) as DataRowView).Row;
// Do a check for doubles here
DataRow[] doubles = EmplDT.Select("Matricule = '" + rowGridView4[0].ToString() + "'");
if (doubles.Length == 0)
{
// fix for error "This row already belongs to another table"
DataRox row = EmplDT.NewRow();
row[0] = rowGridView4[0];
row[1] = rowGridView4[1];
row[2] = userShift.Text;
row[3] = userShift.EditValue;
row[4] = txtDate.EditValue;
EmplDT.Rows.Add(row);
}
}
Ive been working at this for hours and cant seem to figure out how to correctly display the data in a table
using (TextFieldParser csvParser = new TextFieldParser(path)) {
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
csvParser.ReadLine();
int pointX = 30;
int pointY = 40;
while (!csvParser.EndOfData) {
string[] fields = csvParser.ReadFields();
int rowNums = fields.Length;
int index = 0;
for(index = 0; index < rowNums;index++) {
string Name = fields[index];
TextBox n = new TextBox();
n.Text = Name;
n.Location = new Point(pointX, pointY);
panel2.Controls.Add(n);
panel2.Show();
pointY += 20;
if(index != 0) {
pointX += 100;
}
}
}
}
Whats happening so far is im grabbing a csv file stored in the path variable and reading it the output is accessible through fields[] This works fine then I am trying to create textbox to put the data into based on rows however what i currently have comes out look like this
Program Display
I would like to display the column names and rows correctly in order here is an example image of what it looks like in notepad
Notepad Display
In notepad you will see each new line is a row and every , dictates a new entry in the row and i wanna display it this way in my program but in textbox
Also note that not all csv files that this program will be opening are short most will be large files with thousands or rows or more so theres no way that it could be simply putting fields[0] hard coded
You are much better off using a DataGridView to display this type of data in a table format.
From the toolbox add the DataGridView control to your form. You will need to build a DataTable that will bind to your DataGridview.
Below is what you can use(I commented out where you are skipping the header in your CSV file, and am using that line to get the column headers to be used in the datagrid)
var dt = new DataTable();
var lineNo = 0;
using (var csvParser = new TextFieldParser(path))
{
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
//csvParser.ReadLine();
while (!csvParser.EndOfData)
{
var fields = csvParser.ReadFields();
var rowNums = fields.Length;
var row = dt.NewRow();
lineNo += 1;
int index = 0;
for (index = 0; index < rowNums; index++)
{
if (lineNo==1)
{
dt.Columns.Add(fields[index]);
}
else
{
row[index] = fields[index];
}
}
if (lineNo == 1) continue;
dt.Rows.Add(row);
dt.AcceptChanges();
}
}
dataGridView1.DataSource = dt;
I think the controls are overlapping, so you can not see them. That they are overlapping like chairs is the problem. You are not resetting your coordinates.
Here an improvement:
using (TextFieldParser csvParser = new TextFieldParser(path)) {
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
csvParser.ReadLine();
int offsetX = 30;
int offsetY = 40;
int counter;
while (!csvParser.EndOfData) {
int pointerY = ++counter * offsetY; // first counter increments by one, then counter times offsetY occurs
int pointerX;
string[] fields = csvParser.ReadFields();
int rowNums = fields.Length;
for(int index = 0; index < rowNums;index++) {
pointerX = (index + 1) * offsetX;
string name = fields[index];
TextBox n = new TextBox() { Text = name, Location = new Point(pointerX, pointerY) };
panel2.Controls.Add(n);
panel2.Show(); // should be unnecessary
}
}
}
I am building an Asp.net Stacked Column chart.
Here is how it looks :
Here is how it should look :
Ignore the numbers on the chart but look at the X axis - Why does it give me 1148,1153, 1163 when they do not appear in the data.
Here is my Data :
Here is the code:
Dim chart As New Chart
chart.ID = "Chart1"
Dim chartareas As New ChartArea
chart.ChartAreas.Add(chartareas)
chart.DataBindCrossTable(DtFinalRecords.DefaultView, "OutcomeScore", "TermID", "RecordsPerGroup", "Label=RecordsPerGroup")
chart.ChartAreas(0).AxisX.MajorGrid.Enabled = False
chart.ChartAreas(0).AxisY.MajorGrid.Enabled = False
For Each cs As Series In chart.Series
cs.ChartType = SeriesChartType.StackedColumn
Next
pnlcharts.Controls.Add(chart)
Any help would be appreciated. Thank you!
DataBindCrossTable does the best job it can with minimal coding effort required from you. But if you are not happy with the default behavior then you have to explicitly customize it. In your particular case, you want to assign custom labels to your data points:
protected void Page_Load(object sender, EventArgs e)
{
Chart1.Palette = ChartColorPalette.None;
Chart1.PaletteCustomColors = new Color[] { ColorTranslator.FromHtml("#DF5B59"), ColorTranslator.FromHtml("#E0D773 "), ColorTranslator.FromHtml("#8AAC53"), ColorTranslator.FromHtml("#6A843F") };
Chart1.ChartAreas[0].AxisY.MajorGrid.Enabled = false;
Chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
Chart1.ChartAreas[0].AxisX.Interval = 1;
var rows = from row in dt.AsEnumerable() select row.Field<int>("OutcomeScore");
Chart1.Series.Clear();
foreach (int i in rows.Distinct())
Chart1.Series.Add(new Series { Name = i.ToString(), ChartType = SeriesChartType.StackedColumn });
foreach (DataRow dr in dt.Rows)
{
DataPoint dp = new DataPoint();
dp.AxisLabel = dr["TermID"].ToString();
dp.Label = dr["RecordsPerGroup"].ToString();
dp.XValue = (int)dr["TermID"];
dp.YValues[0] = (int)dr["RecordsPerGroup"];
string name = dr["OutcomeScore"].ToString();
Chart1.Series[name].Points.Add(dp);
}
}
I am creating a Sudoku game with ASP.NET and C#. I am required to use classes and inheritance to build the structure entirely in the code-behind page (i.e. no asp:TextBox controls on the aspx page).
I am having an awful time trying to get my inputs to clear when I start a new game. I generate a new puzzle solution, but my previous boxes don't clear and I've tried everything I can think of.
Below are several chunks of code that relate to the problem.
The code that builds the puzzle and stores it in a Puzzle object.
private Puzzle newPuzzle(int[,] solution, int numbersVisible, int maxNumbersPerBox, int maxOccurancesPerNumber)
{
Puzzle newPuzzle = new Puzzle();
SudokuTextBox newTextbox;
Number newNumber;
Random randomRC = new Random();
//variable to hold the correct answer at the given location
int answer;
//variables to hold the location within the answers array
int rowLoc;
int colLoc;
//counter to count the number of times we while loop
int counter = 0;
//variables to hold the randomly-chosen rows & col values
int row;
int col;
//array to hold the positions of the numbers we are going to show
string[] show;
show = new string[numbersVisible];
while(counter < numbersVisible)
{
//generate random numbers that gives us the location of the numbers in the solution array that we are going to show
row = randomRC.Next(0, 9);
col = randomRC.Next(0, 9);
//if the random numbers are not already in the array
if (!show.Contains(row.ToString() + ":" + col.ToString()))
{
//add them to the array
show[counter] = row.ToString() + ":" + col.ToString();
//increase the counter
counter++;
} //end if...contains
} //end while
// BUILDING THE PUZZLE
//start looping through the puzzle rows
for (int pr = 0; pr < 3; pr++)
{
//another loop for puzzle columns
for (int pc = 0; pc < 3; pc++)
{
box = new Box(); //create a new Box object
//another loop for box rows
for (int br = 0; br < 3; br++)
{
//another loop for box columns
for (int bc = 0; bc < 3; bc++)
{
newTextbox = new SudokuTextBox();
newNumber = new Number();
//grab the answer to this particular SudokuTextBox from the solutions array
rowLoc = (pr + br + (2 * pr));
colLoc = (pc + bc + (2 * pc));
answer = solution[rowLoc, colLoc];
newNumber.setNumber(answer); //set the Number to the found answer
newTextbox.setTextBoxValue(newNumber); //fill in the textbox with Number
//if this SudokuTextBox is chosen to be given at the start of the puzzle
if (show.Contains((rowLoc + ":" + colLoc).ToString()))
{
//make this SudokuTextBox visible
newTextbox.setVisibility(true);
}
else {
newTextbox.setVisibility(false);
} //end if
box.setItem(newTextbox, br, bc); //add the SudokuTextBox to the correct position inside Box
} //end box column loop
} //end box row loop
newPuzzle.setItem(box, pr, pc); //add the Box to the correct position inside Puzzle
} //end puzzle column loop
} //end puzzle row loop
return newPuzzle;
} //end easy()
Storing the new puzzle in Session:
//when the Easy button is pressed
protected void btnEasy_Click(object sender, EventArgs e)
{
//generate a new random number
Random newRandomSoln = new Random();
//keep picking new solutions until we get one that's different than the last one
do
{
solution = chooseSolution(newRandomSoln.Next(1, 11));
}
while (solution == Session["solution"]);
//store the new solution
Session["solution"] = solution;
//generate a new puzzle
Session["puzzle"] = newPuzzle( (int[,])Session["solution"], 32, 4, 4 );
}
The code that builds the table structure, fills it with the answers stored in Puzzle, and adds it to the aspx page:
////////////////////////////////
// CREATING THE PUZZLE
///////////////////////////////
Table structure = new Table(); //table to be the outer structure of the puzzle
TableRow row; //row variable to make new rows
TableCell cell; //cell variable to make new cells
Table boxTable; //table that will hold individual Boxes
TableRow boxRow; //row that will hold 3 SudokuTextBoxes
TableCell boxCell; //cell that will hold a single SudokuTextBoxes
TextBox input; //textbox that will hold the textbox in SudokuTextBox
int answer; //int to hold the answer to a particular textbox
//start looping through the puzzle rows
for (int pr = 0; pr < 3; pr++)
{
row = new TableRow(); //create a new outer row
//another loop for puzzle columns
for (int pc = 0; pc < 3; pc++)
{
cell = new TableCell(); //create a new outer cell
boxTable = new Table(); //create a new inner table
box = new Box(); //create a new Box object
box = ((Puzzle)Session["puzzle"]).getItem(pr, pc); //find the box at the current location in the puzzle
//another loop for box rows
for (int br = 0; br < 3; br++)
{
boxRow = new TableRow(); //create a new inner row
//another loop for box columns
for(int bc = 0; bc < 3; bc++)
{
boxCell = new TableCell(); //create a new inner cell
textbox = new SudokuTextBox(); //create a new SudokuTextBox object
textbox = box.getItem(br, bc); //find the SudokuTextBox at the current location in the box
//grab the answer to this particular SudokuTextBox from the solutions array
answer = ((int[,])Session["solution"])[ (pr + br + (2 * pr)), (pc + bc + (2 * pc)) ];
input = textbox.getTextBox(); //grab the textbox inside SudokuTextBox and store it
input.MaxLength = 1; //only allow 1 character to be typed into the textbox
//give the textbox an ID so we can find it later
input.ID = ("tb" + (pr + br + (2 * pr)) + "_" + (pc + bc + (2 * pc))).ToString();
boxCell.Controls.Add(input); //add the textbox to the inner cell
boxRow.Controls.Add(boxCell); //add the inner cell to the inner row
} //end box column loop
boxTable.Controls.Add(boxRow); //add the inner row to the inner table
} //end box row loop
cell.Controls.Add(boxTable); //add the inner table to the outer cell
row.Controls.Add(cell); //add the outer cell to the outer row
} //end puzzle column loop
structure.Controls.Add(row); //add the outer row to the outer table
} //end puzzle row loop
pnlPuzzle.Controls.Add(structure);
////////////////////////////////
// end puzzle
///////////////////////////////
And the SudokuTextBox class code:
public class SudokuTextBox
{
private System.Web.UI.WebControls.TextBox textbox;
private bool visible;
private Number number;
public SudokuTextBox()
{
textbox = new System.Web.UI.WebControls.TextBox();
visible = false;
number = new Number();
} //end constructor
//function to make a new textbox
public System.Web.UI.WebControls.TextBox getTextBox()
{
return textbox;
}
//function to get the value of a textbox
public Number getTextBoxValue()
{
return number;
}
//????????????
public void setTextBoxValue(Number newNumber)
{
this.number.setNumber(newNumber.getNumber());
}
//function to get the visibility of a textbox
public bool getVisibility()
{
return visible;
}
//function to change the visibility of a textbox
public void setVisibility(bool newVisible)
{
if (newVisible)
{
//if the textbox is visible
//get the number
//and make it disabled
textbox.Text = number.getNumber().ToString();
textbox.ReadOnly = true;
textbox.BackColor = System.Drawing.Color.FromArgb(150, 148, 115);
} else
{
//if it is not visible
//hide the number
//and make it enabled
textbox.ReadOnly = false;
textbox.Text = "";
textbox.BackColor = System.Drawing.Color.White;
}
}
//function to change the color of the textbox if it is wrong
public void setWrongNumber()
{
textbox.BackColor = System.Drawing.Color.FromArgb(5, 156, 202, 252);
}
//function to change the color of the textbox if it is correct
public void setCorrectNumber()
{
//but don't change disable text boxes
if(textbox.ReadOnly != true)
{
textbox.BackColor = System.Drawing.Color.White;
}
}
//function to change the color of the textbox if it is blank
public void setBlankNumber()
{
//but don't change disable text boxes
if (textbox.ReadOnly != true)
{
textbox.BackColor = System.Drawing.Color.White;
}
}
//function to show the value of a textbox when clicking the "Hint" button
//also changes the color of the textbox so we know it was shown with a hint
public void setHint()
{
setVisibility(true);
}
} //end class
Thank you to all who gave your two cents. I just wanted to let everyone know that I have solved the issue:
I ended up separating out the code that builds the table structure, taking it out of Page_Load and putting it into its own function. I then called this function from Page_Load. I also called this function when I click the "new puzzle" button. I also added similar logic to that which I commented above to clear the previous table structure before building a new one:
foreach(Control c in pnlPuzzle.Controls){
pnlPuzzle.Controls.Remove(c);
}
I'm not exactly sure why this solve my problem, but it worked!
I have a task to develop Windows applications where paging is involved. If I perform any event like splitting date and time, it's applied only to the current page. I would like to apply that event to all pages in the Datagridview.
If I take a datatable/dataset and work on it, the UI is taking time to read the file as it again reads the whole file to data table. So, please suggest any other alternative to apply the events to all pages in the DataGridView.
I will post the code, or upload my code in any site or here, if required.
Please let me know if my question is unclear.
VARIABLES DECLARATION:
List<String> cmbList = new List<string>();
public String Replace;
public String Find;
public String Col;
public String NewColumn;
public String NewColumnValue;
public string MyFOrmat { get; set; }
int PageCount;
int maxRec;
int pageSize = 30;
int currentPage = 1;
int recNo = 0;
string FileName;
String[] datfile;
button1 = BROWSE BUTTON (Where i read the file):
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.InitialDirectory = "Desktop";
openFileDialog1.Filter = "dat files (*.DAT)|*.DAT|All files (*.*)|*.*";
openFileDialog1.FilterIndex = 2;
openFileDialog1.RestoreDirectory = true;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
FileName = openFileDialog1.FileName;
string text = System.IO.File.ReadAllText(FileName);
datfile = text.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
//Added on 2015-12-02
maxRec = datfile.Length - 1;
PageCount = maxRec / pageSize;
LoadPage(MyFOrmat);
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
}
LOADPAGE Code:
public void LoadPage(string Format, bool isFindAndReplace = false)
{
int startRec;
int endRec;
if (currentPage == PageCount)
{
endRec = maxRec;
}
else
{
endRec = pageSize * currentPage;
}
dataGridView1.Rows.Clear();
if (recNo == 0)
{
dataGridView1.Columns.Clear();
}
int rowindex = 0;
startRec = recNo;
for (int RowCount = startRec; RowCount <= endRec; RowCount++)
{
if (datfile[RowCount].ToString() != "" )
{
if (RowCount == 0)
{
string[] column = datfile[RowCount].Split('þ');
for (int i = 0; i < column.Length - 1; i++)
{
if (column[i].ToString() != "" && column[i].ToString() != "\u0014")
{
DataGridViewTextBoxColumn dgvtxtcountry = new DataGridViewTextBoxColumn();
dgvtxtcountry.HeaderText = column[i].ToString();
dgvtxtcountry.Name = column[i].ToString();
dataGridView1.Columns.Add(dgvtxtcountry);
cmbList.Add(column[i]);
i += 1;
}
}
}
if (RowCount != 0)
{
dataGridView1.Rows.Add();
string[] column = datfile[RowCount].Split('þ');
int index = 0;
for (int i = 1; i < column.Length - 1; i++)
{
if (column[i].ToString() != "\u0014")
{
if (i == 3)
{
dataGridView1.Rows[rowindex].Cells[index].Value = Convert.ToDateTime(column[i]).ToString(Format);
}
else
{ dataGridView1.Rows[rowindex].Cells[index].Value = column[i].Trim('þ'); }
index += 1;
i += 1;
}
}
rowindex += 1;
}
}
recNo += 1;
}
}
FIND and REPLACE Event:
private void btnFindandReplace_Click(object sender, EventArgs e)
{
Form2 f = new Form2();
f.cmbColumnCombo.DataSource = cmbList;
f.ShowDialog();
for (int i = 0; i <= dataGridView1.Rows.Count - 1; i++)
{
//dataGridView1.Rows[rowindex].Cells[index].Value = Convert.ToDateTime(column[i]).ToString(Format);
if (dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value.ToString().ToLower().Contains(f.txtfind.Text.ToLower()))
{
//dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value = dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value.ToString().ToLower().Replace(f.txtfind.Text.ToLower(), f.txtreplace.Text);
//bulidDataRow(i);
if (!string.IsNullOrEmpty(f.txtfind.Text))
{
dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value = dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value.ToString().Replace(f.txtfind.Text, f.txtreplace.Text);
#region Commented
//dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value = dataGridView1.Rows[i].Cells[f.cmbColumnCombo.Text].Value.ToString().Replace(f.txtfind.Text, f.txtreplace.Text);
//bulidDataRow(i);
#endregion
}
}
}
}
private void btnNext_Click(object sender, EventArgs e)
{
currentPage += 1;
if (currentPage > PageCount)
{
currentPage = PageCount;
//Check if you are already at the last page.
if (recNo == maxRec)
{
MessageBox.Show("You are at the Last Page!");
return;
}
}
LoadPage(MyFOrmat);
}
Please let me know if anything needs to be added.
To summarize your requirements:
You want to read largish data files of 10k - 500k records
You want to display them in chunks/pages in a DataGridView
You want allow the user to modify the data:
The user can merge columns
The user can use change&replace on the data
Date&time columns may be split
Possibly modified data shall be saved
The way I see it you have two approaches:
Either cache the data
Or cache the actions
Caching the actions is doable but clearly a lot more fuss, both in coding the caching and in keeping the data synchronized.
So caching the data would be my first choice.
Here is a sketch of how to break up the functionality:
A function to read in the whole data and load them into a DataTable
Functions for the initial display and for displaying a certain page
Functions for doing each of the changes on the list of rows.
After calling a changing function the current page display must be refreshed.
Keeping the total quantity of data in memory shouldn't really be a problem today; I notice that you are reading in all data as strings already in the datFile array. Reading it into a table will spare you to split it over and over..
A DataTable.DataRow also offers nice properies like HasErrors or RowState. And its Items can have a dedicated type to help with formatting..
Note however that DataRow doesn't have a (real) constructor; instead it must be created from a DataTable, so you will first have to create one from your columns!
The display code would use a pageSize and a currentFirstLine variable; it can clear and add the rows into the DGV or you could go for a binding solution with the DataTable you need anyway holding the DataRows and a filter on the table or rather on an BindingSource.
Of course you can also use a structure of your own, maybe a simple as a string[] or a List<string>to hold the row data..
If you are interested in the idea of caching the actions, you could create a ChangeAction class that holds:
the type
the parameters needed, ie, the column(s), the change&replace strings etc..
Then in a List<ChangeAction> you would store them as they happen and then apply them to each unchanged row. But here comes the first catch: You will need to know which row have been changed and maybe if a ChangeAction can be applied twice without screwing up the data.. More problems may or may not come later, depending on the details of you data and actions..
Here is an example of how to set up the binding using class level variables:
DataTable DT = new DataTable();
BindingSource BS = new BindingSource();
int pageSize = 0;
int firstLineVisible = 0;
After filling the table you can bind it and set the initial filer:
BS.DataSource = DT;
dataGridView1.DataSource = BS;
pageSize = (dataGridView1.ClientSize.Height - dataGridView1.ColumnHeadersHeight)
/ dataGridView1.Rows[0].Height;
int l1 = firstLineVisible; int l2 = firstLineVisible + pageSize;
BS.Filter = "Nr >= " + l1 + " and Nr < " + l2;
When scrolling you simply change the firstLineVisible and rest the Filter and the DataSource..
Now all your data modifications should work on the data in the DataTable using the SetField method!
Also note that you need one column in your data that holds a running number. If your data don't have one it is easy to include it by adding it to the data lines:
The column gets autogenerated in the DataGridView. For the DataTable we want to have it in the first data line; I use a separator string sep:
var lines = File.ReadAllLines(fileName).ToList();
..
string[] sep = { ";" };
var p0 = ("Nr" + sep[0] + lines[0]).Split(sep, StringSplitOptions.None );
DT.Columns.Clear();
for (int i = 0; i < p0.Length; i++) DT.Columns.Add(p0[i], typeof(string));
Adding it to the data is just as simple:
for (int l = 1; l < lines.Count; l++)
{
var p = (l + sep[0] + lines[l]).Split(sep, StringSplitOptions.None);
DT.Rows.Add(p);
}
You can hide the number column if you want to..:
dataGridView1.Columns["Nr"].Visible = false;
You should add that line right after setting the Filter.