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.
Related
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.
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'm trying to convert my foreach method to multi-threading.
I have a datagridview and the method gets the value from cell[0] (which contains a url) and sends it to another method which works with httpwebrequest.
public void UrlCheck()
{
foreach (DataGridViewRow row in dataUrlList.Rows)
{
string url= row.Cells[0].Value.ToString();
try
{
string get = getHtml(url);
//work with get string removed
if()
{
row.Cells[1].Value = "page info here";
}
else
{
row.Cells[1].Value = "error info here";
}
}
catch
{
}
}
MessageBox.Show("Done.");
}
The above code is working without any problem but sequentially.
Then with this i've tried to convert this code to that to be multithreaded :
Button :
private void button9_Click(object sender, EventArgs e)
{
int threadcount = Convert.ToInt32(numThreadSearch.Value);
ThreadForSearch = new Thread[threadcount];
checkingForSearch = dataUrlList.Rows.Count;
isRunningForSearch = true;
beenCheckedForSearch = 1;
for (int i = 0; i <= threadcount - 1; i++)
{
ThreadForSearch[i] = new Thread(new ParameterizedThreadStart(MultiThreadMet));
ThreadForSearch[i].Start(i);
}
}
and the multi-threaded method is here :
public void MultiThreadMet (object IndexForSearch)
{
int index = (int)IndexForSearch;
DataGridViewRow row = dataUrlList.Rows[index];
while (isRunningForSearch)
{
try
{
if (beenCheckedForSearch >= checkingForSearch)
{
isRunningForSearch = false;
}
if (index >= dataUrlList.Rows.Count)
{
ThreadForSearch[index].Abort();
}
//For just test i'm trying to add "test" in every cell[1] in datagridview
dataUrlList.Invoke(new Action(() => row.Cells[1].Value = "test"));
beenCheckedForSearch++;
}
catch
{
}
}
ThreadForSearch[1].Abort();
}
It gets the thread count to run from a numericUpDown control and if I choose the value 10 from the numericUpDown it puts "test" text in the first 10 cells of datagridview and then stops, if i choose 4(or 2,5,7), then it puts the "test" text in the first 4(2,5,7) cells and stops.
It doesn't continue to the next rows after the thread is finished. So I'm trying to fire 5 threads (I always choose it from numericUpDown) and when a thread finish its work, it must go to next row.. How can i solve this problem ?
Also these variables have been declared:
Thread[] ThreadForSearch;
int beenCheckedForSearch;
int checkingForSearch;
private bool isRunningForSearch;
from output screen i'm getting
Exception thrown: 'System.Threading.ThreadAbortException' in mscorlib.dll
Thanks.
The index counter was not incremented in the thread method.
There is still an overlapping problem in the program that I will let you fix. Assuming there are 50 urls, the problem is that thread 1 will update cells 0 to 49, thread 2 will update cells 1 to 49, and so on.
public void MultiThreadMet (object IndexForSearch)
{
int index = (int)IndexForSearch;
while (isRunningForSearch)
{
try
{
if (index >= dataUrlList.Rows.Count)
{
return;
}
DataGridViewRow row = dataUrlList.Rows[index];
index++;
//For just test i'm trying to add "test" in every cell[1] in datagridview
dataUrlList.Invoke(new Action(() => row.Cells[1].Value = "test"));
}
catch
{
}
}
}
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.
I have a DataGridView with a DataTable set to its DataSource. The DataTable has two columns. The user has a TextBox which dynamically (after each keypress) searches for a match in the first column.
I want to jump to the matching record (if any) after each key entry, so it needs to be fast.
I use the Find() method on the DefaultView of the DataTable. I then position the Currency Manager, which causes the DataGridView to jump to the correct record.
However, this all breaks when the user elects to sort by the second column. That changes the sort in the DefaultView, and Find() can no longer search on the first column.
If I create a second DataView which is always sorted by the first column, the I can always perform Find(), but then I do not know how to identify the corresponding record in the DataGridView.
Is there a way to use Find() on a column in the DataTable despite the bound DataView being sorted by a different column, and still jump to the found row in the DataGridView?
Think about the problem again, when data is not sorted on the first column (even if it is done by framework) linear search O(n) is the best suited for your problem. Using this I've implemented following demo code which solves your problem, just add DataGridView & TextBox to your form, setup event handlers, you should be seeing the behavior you're looking for with below code. This is not production quality code, rather directon on howto solve your problem in smaller context.
using System;
using System.Data;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form2 : Form
{
/// <summary>
/// You can optimize this functionality only when the first column is sorted.
/// If not there is no faster way other than linear search, even if it would be done by framework, in my opinion linear search is the best posssible solution when the first column is not sorted.
/// This piece of code is for demo purposes on how to do something, should be refined to fit your production cases.
/// </summary>
private void textBox1_TextChanged(object sender, EventArgs e)
{
string text = textBox1.Text.Trim();
int index = dataGridView1.CurrentCell.RowIndex;
CurrencyManager cm = dataGridView1.BindingContext[dataGridView1.DataSource] as CurrencyManager;
DataView view = cm.List as DataView;
if (string.IsNullOrEmpty(text)) return;
//If sorted on first column
if (view.Sort.Contains("First")) //column will be "[First]"
{
index = source.DefaultView.Find(text);
SetIndex(cm, index);
}
//if not
else if (view.Sort.Contains("Second")) //column will be "[Second]"
{
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
if (dataGridView1.Rows[i].Cells["First"].Value.ToString().StartsWith(text))
{
index = i; break;
}
}
SetIndex(cm, index);
}
}
private void SetIndex(CurrencyManager cm, int index)
{
if (index >= 0 && index < source.Rows.Count)
{
cm.Position = index;
}
}
private void CreateData()
{
source.Columns.Add("First", typeof(string));
source.Columns.Add("Second", typeof(string));
var f = from first in Enumerable.Range('a', 26)
select new string(new char[] { (char)first });
var s = f.Reverse();
var c1Enumerator = f.GetEnumerator();
var c2Enumerator = s.GetEnumerator();
for (int i = 0; i < f.Count(); i++)
{
DataRow dr = source.NewRow();
c1Enumerator.MoveNext();
c2Enumerator.MoveNext();
dr[0] = c1Enumerator.Current;
dr[1] = c2Enumerator.Current;
source.Rows.Add(dr);
}
}
DataTable source = new DataTable();
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
CreateData();
dataGridView1.DataSource = source;
}
}
}
It's a bit simpler if you use a BindingSource:
using System;
using System.Data;
using System.Linq;
using System.Windows.Forms;
public class Form1 : Form
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
BindingSource source;
public Form1()
{
Controls.Add(new DataGridView { Name = "DGV", Dock = DockStyle.Fill, TabIndex = 2 });
Controls.Add(new TextBox { Name = "Find", Dock = DockStyle.Top, TabIndex = 1 });
DataTable table = CreateData();
source = new BindingSource(table, null);
(Controls["DGV"] as DataGridView).DataSource = source;
source.Sort = "First";
Controls["Find"].TextChanged += (s, e) =>
{
int index = source.Find("First", (s as Control).Text);
if (index >= 0)
source.Position = index;
};
}
private DataTable CreateData()
{
DataTable table = new DataTable { Columns = { "First", "Second" } };
foreach (var o in Enumerable.Range('a', 26).Select(ch => new { F = new String((char)ch, 1), S = new String((char)('z' - (ch - 'a')), 1)}))
{
DataRow dr = table.NewRow();
dr[0] = o.F;
dr[1] = o.S;
table.Rows.Add(dr);
};
return table;
}
}