I am working with an HMI that displays samples taken remotely from a PLC. Due to the sampling rate(5 seconds), when the code runs the for loop it fills the row with the same value as it takes some time to change. I was trying to add some 'last state' conditions but my code never really worked out.
Below is the code that displays a datatable, but every column of a single row shows the same value.
public DataTable dt = new DataTable();
public void Bindgrid(DataTable dtnew)
{
if (dtnew != null)
{
dt = dtnew;
PicBox.Refresh();
}
}
public void loadgrid()
{
dt.Clear();
dt.Rows.Clear();
for (int i = 1; i <= 3; i++)
{
DataRow row = dt.NewRow();
row["NO"] = i.ToString();
for (int jval = 1; jval <= totalColumntoDisplay; jval++)
{
row[jval.ToString()] = measurement;
}
dt.Rows.Add(row);
}
dataGridView1.AutoResizeColumns();
dataGridView1.DataSource = dt;
dataGridView1.AutoResizeColumns();
}
public void loadGridColums()
{
dt.Columns.Add("No");
for (int jval = 1; jval <= totalColumntoDisplay; jval++)
{
dt.Columns.Add(jval.ToString());
}
}
private void timer2_Tick(object sender, EventArgs e)
{
measurement = "PLC sampling value";
loadgrid();
Bindgrid(dt);
lastmeasurement = measurement;
}
I changed the for loop with an if(lastmeasurement!=measurement)
if(lastmeasurement!=measurement)
{
row[jval.ToString()] = measurement;
jvalue++;
}
But the column value(jvalue) never changed.
The only thing I want to display is a datatable (or even an array) with different values, its MEAN and standard deviation, but I am having a hard time showing this data the way I want in a data table. Maybe there is an easier way to display this data.
PS: I am storing the sampling values in a database which actually works with the last state condition, if it can be useful somehow.
Related
I need help regarding c# win Form application. I trying to achieve that to show a Multiplication Table or Times Table in a GridViewControl of win Form. loop iteration is defined with user input textbox control.
The problem is occurring the data is only added to the first column of gridViewControl where as i want to display the data in each cell of the gridViewControl.
Below is the code im using to achieve above mentioned result.
private void button1_Click(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dataGridView1.DataSource = dt;
string selection = comboBox1.SelectedText;
int tblNo = Convert.ToInt32(textBox2.Text);
int limit = Convert.ToInt32(textBox3.Text);
for( int i = 1; i <= limit; i++)
{
int res = i * tblNo;
dt.Columns.Add("",typeof(string));
dt.Rows.Add(tblNo + " x " + i + "=" + res);
}
dataGridView1.DataSource = dt;
}
Result of above mentioned Code
Required Result
Any help would be appreciated.
Thanks in Advance
Required Result can be achieved by iterating dataGridView Control Row element in a for loop and within for loop for-each loop can be used to fill same content in each cell of Row element. Below code i used to get required result set.
for(int i=0; i<any Integer;i++)
{
foreach(DataGridViewCell gvCell in yourgridViewControl.Rows[i].Cells)
{
gvCell.Value = any string;
}
}
I am populating a DataGridView with a semicolon-delimited text file like so:
private void ExistingAppntmntRecs_Load(object sender, EventArgs e)
{
DataTable dt = SeparatedValsFileToDataTable(APPOINTMENTS_FILE_NAME, ";");
dataGridViewExistingAppntmntRecs.DataSource = dt;
}
// from http://stackoverflow.com/questions/39434405/read-csv-to-datatable-and-fill-a-datagridview (Frank)
public static DataTable SeparatedValsFileToDataTable(string filename, string separatorChar)
{
var table = new DataTable("Filecsv");
using (var sr = new StreamReader(filename, Encoding.Default))
{
string line;
var i = 0;
while (sr.Peek() >= 0)
{
try
{
line = sr.ReadLine();
if (string.IsNullOrEmpty(line)) continue;
var values = line.Split(new[] { separatorChar }, StringSplitOptions.None);
var row = table.NewRow();
for (var colNum = 0; colNum < values.Length; colNum++)
{
var value = values[colNum];
if (i == 0)
{
table.Columns.Add(value, typeof(String));
}
else
{ row[table.Columns[colNum]] = value; }
}
if (i != 0) table.Rows.Add(row);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
i++;
}
}
return table;
}
What I want to do now is to color the rows which have an EndDate value within two months of the current date yellow, within one month orange, and those with a date in the past (meaning they have lapsed) red.
There is a PostPaint event which may work, but I don't know how to examine cell contents within the row in that event handler:
private void dataGridViewExistingAppntmntRecs_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
// What now?
}
Here is the contents of the file that is read (bogus/test data, with a header row prepended):
person;phone;email;org;addr1;addr2;city;st8;zip;visitNum;success;adSizeLoc;meetingLoc;meetingDate;meetingTime;adBeginMonth;adBeginYear;adEndMonth;adEndYear;Notes
B.B. King;2221113333;bbking#blues.com;Virtuosos;1234 Wayback Lane;;Chicago;IL;98765;1;false;Full Page Inside Front Cover;Same as Org Address;4/5/2017 2:03:12 PM;9:00 am;May;2017;May;2018;Lucille was her name
Linda Ronstadt;55577889999;rhondalinstadt#eaglet.com;Eagles Singer;La Canada;;Los Angeles;CA;99988;1;false;Full page Inside Back Cover;Same as Org Address;4/5/2017 2:05:23 PM;9:00 am;May;2017;May;2018;She had some good stuff
If the adEndMonth + adEndYear date equates to 2 months or less away, the entire row should be yellow; if 1 month or less away, orange; if today or in the past, paint it red. Finally, if one of the Rolling Stones is running the app, paint it black.
Here is some pseudocode for the PostPaint event, with "TODO:" where I don't know what to do:
private void dataGridViewExistingAppntmntRecs_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
DateTime twoMonthsLimit = DateTime.Now.AddMonths(2);
DateTime oneMonthLimit = DateTime.Now.AddMonths(1);
int endYear = // TODO: assign adEndYear value
int endMonth = // TODO: assign adEndMonth value
DateTime endDate = new DateTime(endYear, endMonth, 1);
if (twoMonthsLimit > endDate) // TODO: paint row yellow
if (oneMonthLimit > endDate) // TODO: paint row orange
if (endDate < DateTime.Now) // TODO: paint row red
}
If the goal is too simply, highlight the rows that fall within certain dates, then changing the rows background color may be an easier option. Repainting the rows may be unnecessary. My solution simply changes the rows background color depending on the dates in the “endMonth” and “endYear” columns.
The cell formatting is an option however, it will fire often and placing this “Coloring” checks every time a cell is changed or displayed is unnecessary. If the rows have already been “Colored”, then the only things to look for is when new rows are added or the values in the “endMonth” or “endYear” columns are changed.
Below is code that simply loops through the DataGridView and sets each row color based on the criteria you described. The logic to get a rows color is fairly straightforward (minus the paint it black). If the date is greater than two months forward from today’s date then leave the background color white. If the date is greater than 1 month but less than 2 months then color the row yellow… etc.
I used a similar approach to read the text file and create the DataTable. Hope this helps.
DataTable dt;
string filePath = #"D:\Test\Artist.txt";
char delimiter = ';';
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
dt = GetTableFromFile(filePath, delimiter);
dataGridViewExistingAppntmntRecs.DataSource = dt;
UpdateDataGridColors();
}
private DataTable GetTableFromFile(string filePath, char delimiter) {
List<string[]> allArtists = GetArtistList(filePath, delimiter);
DataTable dt = GetTableColumns(allArtists[0]);
int totalCols = dt.Columns.Count;
DataRow dr;
for (int i = 1; i < allArtists.Count; i++) {
string[] curArtist = allArtists[i];
dr = dt.NewRow();
for (int j = 0; j < totalCols; j++) {
dr[j] = curArtist[j].ToString();
}
dt.Rows.Add(dr);
}
return dt;
}
private List<string[]> GetArtistList(string inFilePath, char inDelimiter) {
string pathToFile = inFilePath;
char delimiter = inDelimiter;
List<string[]> listStringArrays = File.ReadAllLines(pathToFile).Select(x => x.Split(delimiter)).ToList();
return listStringArrays;
}
private DataTable GetTableColumns(string[] allHeaders) {
DataTable dt = new DataTable();
foreach (string curHeader in allHeaders) {
dt.Columns.Add(curHeader, typeof(string));
}
return dt;
}
private Color GetColorForRow(DataRowView dr) {
// paint it black ;-)
if (dr[0].ToString().Equals("Rolling Stones")) {
return Color.Black;
}
DateTime rowDate;
DateTime dateNow = DateTime.Now;
DateTime twoMonthsLimit = dateNow.AddMonths(2);
DateTime oneMonthLimit = dateNow.AddMonths(1);
if (dr != null) {
string rowStringMonth = dr[17].ToString();
string rowStringYear = dr[18].ToString();
string rowStringDate = "1/" + rowStringMonth + "/" + rowStringYear;
if (DateTime.TryParse(rowStringDate, out rowDate)) {
if (rowDate > twoMonthsLimit)
return Color.White;
if (rowDate > oneMonthLimit)
return Color.Yellow;
if (rowDate > dateNow)
return Color.Orange;
if (rowDate.Month == dateNow.Month && rowDate.Year == dateNow.Year)
return Color.Orange;
// this row date is less than todays month date
return Color.Red;
} // else date time parse unsuccessful - ignoring
}
// date is null
return Color.White;
}
private void UpdateDataGridColors() {
Color rowColor;
DataRowView dr;
foreach (DataGridViewRow dgvr in dataGridViewExistingAppntmntRecs.Rows) {
dr = dgvr.DataBoundItem as DataRowView;
if (dr != null) {
rowColor = GetColorForRow(dr);
dgvr.DefaultCellStyle.BackColor = rowColor;
if (rowColor == Color.Black)
dgvr.DefaultCellStyle.ForeColor = Color.White;
}
}
}
private void dataGridViewExistingAppntmntRecs_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
if (e.ColumnIndex == 17 || e.ColumnIndex == 18) {
//MessageBox.Show("End Date Changed");
UpdateDataGridColors();
}
}
private void dataGridViewExistingAppntmntRecs_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e) {
UpdateDataGridColors();
}
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.
I have a table with 100,000 records and i have a method (using entity framework) that retrieve 10 records, i give it how many records skip to get the next 10 records.
List<Item> GetRecords(int skip = 0);
I load the first 10 records on a list, and set it as datasource of the UltraGrid, how can i call the method to get the next 10 records and add it to the UltraGrid when the scroll reaches the bottom or is near to reach the bottom?
I have a solution for your requirement. Hope this helps you..
First, create a windows form named "test" (say)..
Add a ultraGrid in the form..
check the following code:
public partial class test : Form
{
DataTable dtSource = new DataTable();
int takecount = 50;
int skipcount = 0;
DataTable dtResult;
// CONSTRUCTOR
public test()
{
InitializeComponent();
// Fill Dummy data here as datasource...
dtSource.Columns.Add("SNo", typeof(int));
dtSource.Columns.Add("Name", typeof(string));
dtSource.Columns.Add("Address", typeof(string));
int i = 1;
while (i <= 500)
{
dtSource.Rows.Add(new object[] { i, "Name: " + i, "Address " + i });
i++;
}
dtResult = dtSource.Copy();
dtResult.Clear();
}
// ON FORM LOAD FUNCTION CALL
private void test_Load(object sender, EventArgs e)
{
ultraGrid1.DataSource = dt_takeCount();
ultraGrid1.DataBind();
}
private DataTable dt_takeCount()
{
if (dtSource.Rows.Count - skipcount <= takecount)
{
takecount = dtSource.Rows.Count - skipcount;
}
foreach (var item in dtSource.AsEnumerable().Skip(skipcount).Take(takecount))
{
dtResult.Rows.Add(new object[] { item.Field<int>("SNo"), item.Field<string>("Name"), item.Field<string>("Address") });
}
if (dtSource.Rows.Count - skipcount >= takecount)
{
skipcount += takecount;
}
return dtResult;
}
// EVENT FIRED WHEN ON AFTERROWREGIONSCROLL
private void ultraGrid1_AfterRowRegionScroll(object sender, Infragistics.Win.UltraWinGrid.RowScrollRegionEventArgs e)
{
int _pos = e.RowScrollRegion.ScrollPosition;
if (ultraGrid1.Rows.Count - _pos < takecount)
{
dt_takeCount();
}
}
}
Above code is all that works..
--> "ultraGrid1_AfterRowRegionScroll" function is "AfterRowRegionScroll" event function
--> But be sure that when you choose "takecount", it generates scrollbar,
--> when you run above code... the row will be updated by 50 when you scroll,, till 500th row.. because it is the last row.
I have DataGridView in that one combobox the combobox values are loaded from one table after selecting combobox value i want to update other columns with respective data but this is working only for one row i want update all row.. please give any suggestion for code change.
private void dataGridView2_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (dataGridView2.IsCurrentCellDirty)
{
for (int i = 0; i < (dataGridView2.Rows.Count)-1; i++)
{
try
{
if (dataGridView2.Rows[i].Cells[1].Value.ToString() != "")
{
ConnectionDB gridRdata = new ConnectionDB("SELECT * FROM Ready_Made_Master WHERE RM_Name='" + dataGridView2.Rows[i].Cells[1].Value.ToString() + "';");
DataTable redydata = gridRdata.returntable();
dataGridView2.Rows[i].Cells[2].Value = redydata.Rows[i][2].ToString();
}
}
catch
{
}
}
}
}
After the for loop,try rebinding the gridview ie
for (int i = 0; i < (dataGridView2.Rows.Count)-1; i++)
{
}
ConnectionDB gridRdata = new ConnectionDB("SELECT * FROM Ready_Made_Master");
DataTable redydata = gridRdata.returntable();
gridRdata .Datasource=redydata ;
gridRdata .Databind();
Please make the necessary chnges in Select Statement.