Datatable gets reset unexpectedly - c#

I have a datagrid which refreshes every 3 seconds and while refresh happens Datatable gets reset everytime unexpectedly. What i do is adding values from one datatable to another.
However if use 'DefaultView.ToTable' it stores the data.
Basically, I have 2 datatables dtTopSQL and dtCurTopSQL.
dtTopSQL getting new data and adding to the datatable dtCurTopSQL and I want to store the rows in dtCurTopSQL;
// Initial Load the Datatable Structure
private void Main_Load(object sender, EventArgs e)
{
dtTopSQL.Columns.Add("SQL_ID", typeof(string));
dtTopSQL.Columns.Add("Count", typeof(Int16));
dtTopSQL.Columns.Add("CurTime", typeof(DateTime));
}
// Timer start refreshing the datagrid
private void timer_TimerTopSQL(object sender, EventArgs e)
{
dtTopSQL.Clear(); // Clear before the Fill
odaTopSQL = new OracleDataAdapter(getTopSQLString, oradb);
odaTopSQL.Fill(dtTopSQL);
getTopSQL();
}
// Merging datatable starts here.
public void getTopSQL()
{
for (int i = 0; i < dtTopSQL.Rows.Count; i++)
{
bool isDupe = false;
for (j = 0; j < dtCurTopSQL.Rows.Count; j++)
{
if (dtTopSQL.Rows[i][0].ToString() == dtCurTopSQL.Rows[j][0].ToString())
{
dtCurTopSQL.Rows[j][1] = int.Parse(dtCurTopSQL.Rows[j][1].ToString()) + int.Parse(dtTopSQL.Rows[i][1].ToString());
dtCurTopSQL.Rows[j][2] = CurDate;
isDupe = true;
break;
}
}
if (!isDupe)
{
dtCurTopSQL.ImportRow(dtTopSQL.Rows[i]);
dtCurTopSQL.Rows[j][2] = CurDate;
}
}
ugTopSQL.DataSource = dtCurTopSQL; // Bind the merged Datatable.
}
Above code works if i use below before just binding data;
dtCurTopSQL = dtCurTopSQL.DefaultView.ToTable();
However i don't understand why? I want to store data without using DefaultView.ToTable
Could you explain please?

Above code works if i use below before just binding data;
dtCurTopSQL = dtCurTopSQL.DefaultView.ToTable();
However i don't understand why? I want to store data without using
DefaultView.ToTable
ugTopSQL.DataSource = dtCurTopSQL; // Bind the merged Datatable.
The first time this statement is executed, the DataSource changes from null to dtCurTopSQL. Each subsequent time it is executed, the DataSource setter checks if the new value is the same as its existing value and if so it does nothing.
By assigning a new table to dtCurTopSQL before assigning the DataSource you are assigning a new object to the DataSource, so it acts according with the new source.
A simple solution would be to add
ugTopSQL.DataSource = null;
as the first statement in the getTopSQL method.

What you are doing is absolutely unnecessary. When UltraGrid is bound to DataTable each time data table's rows change the grid will automatically show this change. So change your Load event like this:
// Initial Load the Datatable Structure
private void Main_Load(object sender, EventArgs e)
{
dtTopSQL.Columns.Add("SQL_ID", typeof(string));
dtTopSQL.Columns.Add("Count", typeof(Int16));
dtTopSQL.Columns.Add("CurTime", typeof(DateTime));
ugTopSQL.DataSource = dtCurTopSQL; // Bind the merged Datatable.
}
Then remove this row from getTopSQL method:
ugTopSQL.DataSource = dtCurTopSQL; // Bind the merged Datatable.
This way whatever changes you made to dtCurTopSQL will be automatically shown in the grid without setting the grid's DataSource, which by the way is expensive operation and should not be done each three seconds.

Related

How to bind Access database to DataTable?

I'm new to C# and Visual Studio.
I'm working on a project that is searching through a database of information.
I want to bind a database (Microsoft access file) to my datagridview
but I want it to work with my preexisting code which utilizes a datatable converted into a dataview.
My database has a lot of information and I don't want to put it in manually. I've tried binding the information directly to the datagridview (through datasource in the properties) but then searching doesn't work**. I've looked into sql but im trying to avoid learning 2 languages at the same time.
My projects basic functionality contains: 1 combobox (idCbo) containing the search query's 1 datagridview for displaying the information
this setup is for searching one column only, im going to duplicate the code for the oher columns
The name of the column in the datagridview selects the column(id) for filtering then the combo box(idCbo) searches that column for matching characters in the datagridview and comboBox list.
the combo box contains the values 1-100 for searching the column
public partial class Form1 : Form
{
DataTable dt = new DataTable();
DataView dataView;
public Form1()
{
InitializeComponent();
dt.Columns.Add("id", typeof(int));
for (int i = 0; i < 100; i++)
dt.Rows.Add(i);
dataView = new DataView(dt);
this.dataGridView1.DataSource = dataView;
}
private void idCbo_SelectedIndexChanged(object sender, EventArgs e)
{
string query = idCbo.Text;
dataView.RowFilter = $"convert(id,'System.String') LIKE '%{query}%'";
}
}
**
Binding the database to the datagridview while using this code renders column titles but not the information and the code cannot access the database, columns or the rows System.Data.EvaluateException: 'Cannot find column ...
Big thanks to Johng for assisting me with the code :)
CURRENT WORKING CODE
public Form1()
{
InitializeComponent();
}
public static BindingSource gridBindingSource;
private void idCbo_SelectedIndexChanged(object sender, EventArgs e)
{
string query = idCbo.Text;
gridBindingSource = (BindingSource)dataGridView1.DataSource;
if (gridBindingSource != null)
{
if (query == "All")
{
gridBindingSource.Filter = "";
}
else
{
gridBindingSource.Filter = "convert(id,'System.String') LIKE '%" + query + "%'";
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the '_InfoFinalv_2___CopyDataSet.Info' table. You can move, or remove it, as needed.
infoTableAdapter.Fill(this._InfoFinalv_2___CopyDataSet.Info);
idCbo.Items.Add("All");
for (int i = 1; i < 100; i++)
{
idCbo.Items.Add(i);
}
idCbo.SelectedIndex = -1;
}
private void idReset_Click(object sender, EventArgs e)
{
idCbo.SelectedIndex = -1;
}
If you have set up the grids data source in the designer “correctly” then using the DataView as you want can be simplified by using the existing BindingSource that is usually created when you set up the grid’s data source in the designer.
We can use the existing grid’s BindingSource and then use it’s Filter property as opposed to converting the BindingSource to a DataView to filter. This will allow us to set the filter in the grid WITHOUT having to “change” the grids data source.
Remove all the code you have in the form constructor obviously leaving the InitializeComponent(); and add the code below to the forms Load event. In the load event all we do is set up the combo box with the proper values. I added an “All” option to allow the user to “un-filter” the data in the grid.
private void Form1_Load(object sender, EventArgs e) {
// TODO: This line of code loads data into the 'database1DataSet.EmployeeDT' table. You can move, or remove it, as needed.
employeeDTTableAdapter.Fill(this.database1DataSet.EmployeeDT); // <- created by the designer
idCbo.Items.Add("All");
for (int i = 1; i < 100; i++) {
idCbo.Items.Add(i);
}
idCbo.SelectedIndex = 0;
}
Then in the combo boxes SelectedIndexChanged event... change the code as shown below. Cast the grids DataSource to a BindingSource and then use its Filter property.
private void idCbo_SelectedIndexChanged(object sender, EventArgs e) {
string query = idCbo.Text;
BindingSource GridBS = (BindingSource)dataGridView1.DataSource;
if (GridBS != null) {
if (query == "All") {
GridBS.Filter = "";
}
else {
GridBS.Filter = "EmpID LIKE '%" + query + "%'";
}
}
}
Here's the tip:
On the form load, make an ajax call to the database and fetch only the required data columns. Return data will be in JSON that can be used as data for DataTable.
I used it in an MVC project recently and it works fine. If you would like I can share the detailed code and logic.
Not sharing the code since I'm not sure if you are on .Net MVC.

Data from another class cannot be put to a datagridview

Good day. I have passed a data variable from one class to another to put into a datagridview in the main form. I put some message boxes in each case to know that it accesses the said function and that the data is clearly passed. But when I run the program. The table doesn't put the data inside it.
Here is the code when I pass the data
if (txtCode1.ElementAt(intCtr + 1).Equals(val4)) {
MessageBox.Show("Lol");
Compilourdes_GUI cmp = new Compilourdes_GUI();
cmp.AddtotblLexeme(val2, val2);
break;
}
And here is the code of AddtotblLexeme
public void AddtotblLexeme(string lexeme, string token) {
MessageBox.Show(lexeme+" "+token);
tblLexeme.Rows.Add(lexeme , token); //adding tokens and lexeme to the table
}
Code where I made the DataTable
private void Start()
{
tbl1.AutoGenerateColumns = true;
tbl1.DataSource = null;
tbl1.Rows.Clear();
InitTable();
string txtCode1 = txtCode.Text;
LexicalAnalyzer lex = new LexicalAnalyzer(txtCode1);
lex.StartLex();
tbl1.DataSource = tblLexeme;
}
public void InitTable()
{
tblLexeme = new DataTable();
tblLexeme.Columns.Add("Lexeme", typeof(string));
tblLexeme.Columns.Add("Token", typeof(string));
}
DataTable tblLexeme = new DataTable();
Here is the image of the output . the "TEST" word/s should be inside the table, but as you can see, it didn't get put in.
Ok I think I understand your problem. If you added the columns directly in the designer, my guess is that you added unbound columns. If so, then the DataGridView cannot match up the row you are adding to the rows in the table. To fix this, delete the columns from the DatagridView. Then make sure that your DataGridView has property AutoGenerateColumns = true, before setting DataSource = tblLexeme. Now two things happen automatically: firstly the DataGridView picks up the columns from your DataTable; and secondly, when adding a new row to the DataTable, it should show automatically in the DataGridView.
In AddtotblLexeme, for testing purposes, can you please add, in place of your Rows.Add():
DataRow nR = tblLexeme.NewRow();
nR[0] = lexeme;
nR[1] = token;
tblLexeme.Rows.Add(nR);
Then in debugger check that nR does have an ItemArray with 2 columns.

c# clear datagridview bound to datasource

I have a datagridview bound to a datasource, all headers are added to the columns collection with a datapropertyname set.
When I clear the datagridview using
DataGridView1.DataSource = null;
The headers disappear also and when I fill the datagridview again the header texts are the database column names. How do I clear a bound datagridview without removing the headers?
You can use this
if (dataGridViewNotas.DataSource != null)
((DataTable) dataGridViewNotas.DataSource).Rows.Clear();
One of the approach to handle this issue is to use collection as data source,
Create a class with properties representing the data source (Each property would represent a column in the database)
public class Student
{
public string Name { get; set; }
public string Address { get; set; }
}
You need Add column to datagridview manually and set relevant DataPropertyName for each column and set the HeaderText. When you load the data from database first fill this data into a List. So you will have a List<Student>.
List<Student> studentDetail = new List<Student>();
Set this as the data source of the datagridview.
dataGridView1.DataSource = studentDetail;
Clearing Data source
To clear the data source of the Grid just create a empty Student list and set it as data source again. When you set like this header of each column will be retained.
List<Student> emptyStudentDetail = new List<Student>();
dataGridView1.DataSource = emptyStudentDetail;
If you do not want to use collection of objects and still refresh your data source using this.dataGridView1.DataSource = null; then try this approach:
Assuming you are using for data source either DataSet or local database. Each time before you bind the dataGridView with new data source, create unbound columns with the same names as the names of your data source. Once they are created, you should hide them, because they will be needed when you refresh dataGridView's data source. The following sample code is aware of the data source columns names and therefore they are hard coded, but you can loop each time through the data source and create new collection of unbound columns, if it is necessary.
private void Form1_Load(object sender, EventArgs e)
{
this.dataGridView1.DataSource = GetDataSet();
this.dataGridView1.DataMember = "Students";
this.dataGridView1.Columns.Add("unboundColumn1", "ID");
this.dataGridView1.Columns.Add("unboundColumn2", "Name");
this.dataGridView1.Columns["unboundColumn1"].Visible = false;
this.dataGridView1.Columns["unboundColumn2"].Visible = false;
}
private void button1_Click(object sender, EventArgs e)
{
this.dataGridView1.Columns["unboundColumn1"].Visible = true;
this.dataGridView1.Columns["unboundColumn2"].Visible = true;
this.dataGridView1.DataSource = null;
}
private DataSet GetDataSet()
{
DataSet dataSet = new DataSet();
dataSet.Tables.Add("Students");
dataSet.Tables["Students"].Columns.Add("ID", typeof(int));
dataSet.Tables["Students"].Columns.Add("Name", typeof(string));
dataSet.Tables["Students"].Rows.Add(1, "John Joy");
dataSet.Tables["Students"].Rows.Add(2, "Ivan Nova");
dataSet.Tables["Students"].Rows.Add(3, "Michael German");
return dataSet;
}
Struggled with this for 24 hrs. Not sure why it works, but the solution that did not produce a runtime error for me is to dispose of the table adapter associated with the datagridview:
if (this.dataTableTableAdapter != null)
{
this.dataTableTableAdapter.Dispose();
}

Using C# to set System.Drawing.Color.FromName

Objective: The objective is to set the System.Drawing.Color dynamically based on a text value within a datatable. The text value is the name of a color within System.Drawing.Color. The purpose is to change the backcolor of a grid based on the given value.
Issue: The method I am using now does not obtain a value from the datatable and sets the Color to 0,0,0,0. The datatable is created using a ViewState. The research I've conducted on this issue indicates this should work. However, the value obtained from the DataTable is "" . What is incorrect in this code? Thank you in advance for you comments, suggestions and consideration. The code is as follows:
DataTable code
private void CreateList()
{
DataTable dtMyList = new DataTable();
DataColumn dcID = new DataColumn("ID");
dtMyList.Columns.Add(dcID);
DataColumn dcColor = new DataColumn("Color");
dtMyList.Columns.Add(dcColor);
ViewState["MyList"] = dtMyList;
}
On RowDataBound code intended to change the backcolor
protected void grdMyList_RowDataBound(object sender, GridViewEventsArgs e)
{
DataTable dtMyList = (DataTable)ViewState["MyList"];
for (int i = 0; i < dtMyList.Rows.Count; i++)
{
e.Row.Cells[0].BackColor = System.Drawing.Color.FromName(Convert.ToString(dtMyList.Rows[0]["Color"]));
}
}
First of all: Is the datatable filled with any data?
Second: you are trying to fill that one row (given in the event args) with all the colors from the datatable, resulting in that only one property (e.Row.Cells[0].BackColor) is filled with the color coming from the last row in the table (dtMyList.Rows[i]["Color"]).
I think you should first lookup the correct datarow which is attached to your gridrow (e.Row.DataItem), then read its color and then fill the property of your gridrow. Like this:
protected void grdMyList_RowDataBound(object sender, GridViewEventsArgs e)
{
DataRow row = (DataRow)e.Row.DataItem;
e.Row.Cells[0].BackColor = System.Drawing.Color.FromName(row["Color"].ToString());
}
You're looping through the entire table and setting the color for each row. I think you want something like
protected void grdMyList_RowDataBound(object sender, GridViewEventsArgs e)
{
DataTable dtMyList = (DataTable)ViewState["MyList"];
index i = e.Row.RowIndex;
e.Row.Cells[0].BackColor = System.Drawing.Color.FromName(Convert.ToString(dtMyList.Rows[i]["Color"]));
}

DataGridView hidden column doesn't stay hidden

I have a DataGridView tied to a DataTable source. Among the data on the elements in the table is a Guid which I want to remain hidden. (It's used internally for reference, but should not be displayed.) The code I'm using to create the table is as follows:
private DataTable mEntities = new DataTable();
private System.Windows.Forms.DataGridView EntitiesGridView;
These are declared elsewhere, just showing here for reference.
private void BuildEntityTable()
{
mEntityTable.Columns.Add("id", typeof(Guid));
mEntityTable.Columns.Add("Name", typeof(string));
... (some other columns)
foreach (Foo foo in mEntities)
{
DataRow row = mEntityTable.NewRow();
row["id"] = foo.id;
row["Name"] = foo.Name;
... (rest of data)
mEntityTable.Rows.Add(row);
}
DataColumn[] entityKeys = new DataColumn[1];
entityKeys[0] = entityTable.Columns["id"];
mEntityTable.PrimaryKey = entityKeys;
EntitiesGridView.DataSource = mEntityTable.DefaultView;
EntitiesGridView.Columns["id"].visible = false;
}
So far so good. The table is created, and there's no visible "id" column. However, if I later add a new object to the table, we run into trouble. The code is almost the same:
void AddNewObject(object sender, MyArgs e)
{
Foo foo = e.Foo;
lock (mEntities)
{
mEntities.Add(foo);
}
lock (mEntityTable)
{
DataRow row = mEntityTable.NewRow();
row["id"] = foo.id;
row["Name"] = foo.Name;
... (rest of data)
mEntityTable.Rows.Add(row);
}
}
For some reason, this makes the "id" column come back. I've tried copying the EntitiesGridView.Columns["id"].visible = false; line from the previous code, but it does no good. Nothing I do after this point will make that column go away and stay gone. Any clues what I'm missing?
just write this line
datagridview1.Columns[0].visible = false;
call this event in your form_load()
private void dgv_DataBindingComplete(Object sender, DataGridViewBindingCompleteEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
dgv.Columns[3].Visible = false;
}
I have also encountered this problem, but found that you can make your datagridview changes at design time and then save the project. Run the application, then quit from the application. The dgv on the design form has now automatically changed its display. Close the form and reopen it and you will see that the columns you originally included/excluded are returned. No additional code is required for this fix.

Categories