I built an application which displays the records from database in the window and checks the the database for new records every couple of seconds. The problem is that the window blinks each time I check for new records and I want to fix it. I have tried to compare the old datatable with the new one and refresh only if they are different.
Does anyone know what is the best practice for such cases? I tried to do it the following way but it doesn't work:
private bool GetBelongingMessages()
{
bool result = false;
DataTable dtTemp = OleDbWorks.GetBelongingMessages(currentCallID);
if(dtTemp != dtMessages)
{
dtMessages = dtTemp;
result = true;
}
else
{
result = false;
}
return result;
}
First off, it's important to recognize that what you're comparing in your code is the references of the datatables, not the contents of the datatables. In order to determine if both datatables have the same contents, you're going to have to loop through all of the rows and columns and see if they're equal:
//This assumes the datatables have the same schema...
public bool DatatablesAreSame(DataTable t1, DataTable t2) {
if (t1.Rows.Count != t2.Rows.Count)
return false;
foreach (DataColumn dc in t1.Columns) {
for (int i = 0; i < t1.Rows.Count; i++) {
if (t1.Rows[i][dc.ColumnName] != t2.Rows[i][dc.ColumnName]) {
return false;
}
}
}
return true;
}
I've been trying to find a way to do DataTable comparison for a while and ended up writing up my own function, here is what I got:
bool tablesAreIdentical = true;
// loop through first table
foreach (DataRow row in firstTable.Rows)
{
foundIdenticalRow = false;
// loop through tempTable to find an identical row
foreach (DataRow tempRow in tempTable.Rows)
{
allFieldsAreIdentical = true;
// compare fields, if any fields are different move on to next row in tempTable
for (int i = 0; i < row.ItemArray.Length && allFieldsAreIdentical; i++)
{
if (!row[i].Equals(tempRow[i]))
{
allFieldsAreIdentical = false;
}
}
// if an identical row is found, remove this row from tempTable
// (in case of duplicated row exist in firstTable, so tempTable needs
// to have the same number of duplicated rows to be considered equivalent)
// and move on to next row in firstTable
if (allFieldsAreIdentical)
{
tempTable.Rows.Remove(tempRow);
foundIdenticalRow = true;
break;
}
}
// if no identical row is found for current row in firstTable,
// the two tables are different
if (!foundIdenticalRow)
{
tablesAreIdentical = false;
break;
}
}
return tablesAreIdentical;
Compared to Dave Markle's solution, mine treats two table with same records but in different orders as identical. Hope this helps whoever stumbles upon this thread again.
You have to cast objects t1.Rows[i][dc.ColumnName] and t1.Rows[i][dc.ColumnName] otherwise the statement t1.Rows[i][dc.ColumnName] != t2.Rows[i][dc.ColumnName] is always true. I modified the code the following way:
for(int i = 0; i < t1.Rows.Count; i++)
{
if((string)t1.Rows[i][1] != (string)t2.Rows[i][1])
return false;
}
and it works but it's not an elegant solution.
public Boolean CompareDataTables(DataTable table1, DataTable table2)
{
bool flag = true;
DataRow[] row3 = table2.Select();
int i = 0;// row3.Length;
if (table1.Rows.Count == table2.Rows.Count)
{
foreach (DataRow row1 in table1.Rows)
{
if (!row1.ItemArray.SequenceEqual(row3[i].ItemArray))
{
flag = false;
break;
}
i++;
}
}
else
{
flag = false;
}
return flag;
}
// here this function will gave boolean as result return true if both are same else return false if both are not same
Related
I have grid with data being input.
Item Price Type
A 1000 1
B 1000 2
C 2000 2
D 3000 3
I want sum(price) with type that has value "2".
try
{
foreach (int i in gridView1.GetSelectedRows())
{
DataRow newRow = gridView1.GetDataRow(i);
if (newRow["NOMINAL"] is DBNull) { newRow["NOMINAL"] = 0; }
if (e.Column.FieldName == "IDISJ")
{
if (verifikasiNamaISJ(IDisj, e.RowHandle) == true)
{
TampilkanPesan.Error("Nama Item Sudah Ada!");
newRow["IDISJ"] = 0;
return;
}
newRow["IDISJ"] = IDisj;
gridView1.FocusedColumn = colNominal;
gridView1.FocusedRowHandle = e.RowHandle;
this.BeginInvoke((MethodInvoker)delegate
{
gridView1.ShowEditor();
});
cariDataItem(Convert.ToInt64(Global.PeriksaDBNullAngka(newRow["IDISJ"])));
newRow["NAMAISJ"] = NamaISJ;
newRow["NAMAJENISISJ"] = Jenis;
newRow["NOMINAL"] = 0;
}
DataTable dt = new DataTable()
>>>> txtTotalPotongan.EditValue = Convert.ToString(dt.Compute("SUM(NOMINAL)", "NAMAJENISISJ = 'Pemotongan'"));
>>>> txtTotalDiterima.EditValue = Convert.ToString(dt.Compute("SUM(NOMINAL)", "NAMAJENISISJ = 'Pendapatan'"));
}
}
catch (FbException ex)
{
TampilkanPesan.Error(ex.Message.ToString());
}
the line with >>>> was my work, but I have no idea how to do it. I guess I'm doing it wrong, since I put Datatable there. It's not data yet, still value of row.
But I don't know how to sum with filter, if not using filter I can do it. Please help me the right line of code for it.
I put this on gridview1_cellvaluechanged for every time something changes, it will update the value on textbox as well.
You could convert your datable to a list using System.Data.DataSetExtensions; and use Linq to filter and find the sum you are looking for.
var sum = dt.AsEnumerable().Where(x=> (int)x["Type"] == 2).Sum(x => (int)x["Price"]);
Here you can find a working example using your Items table.
I've uploaded data to a datatable from a excel file. Whats the best way to check empty rows in datatable. NB: Need solution for Visual Studio 2005. Cant use linq.
foreach (DataRow row in result.Rows)
{
//check if row is empty
//if not continue processing data
//else remove the row from datatable
}
Currently the solution in my mind is inside foreach loop, put another foreach loop and check each column. If all columns are null, remove the row from datatable.
Is there any other best method. Does the above method takes more time to execute as there is another for each loop. There will be lots of rows in the datatable.
Edited:
I've got one code:
private bool checkIfRowEmpty(DataRow row)
{
var r = (DataRow)row;
int emptyCount = 0;
int itemArrayCount = r.ItemArray.Length;
foreach (var i in r.ItemArray) if (string.IsNullOrWhiteSpace(i.ToString())) emptyCount++;
if (emptyCount == itemArrayCount) return false;
else return true;
}
Is this okay? Or should I use any solution from below? Which one is good?
Here is one approach - to iterate through all rows and all columns
DataTable table = new DataTable();
List<int> rowsToRemove = new List<int>();
for (int i = 0; i < table.Rows.Count; i++)
{
DataRow row = table.Rows[i];
foreach (DataColumn col in row.Table.Columns)
{
bool skip = false;
if (row[col] != null)
{
//this column is not null, so mark skip flag as true
skip = true;
}
if (skip)
//this row should be skipped because it has at least one column that isn't null
break;
else
rowsToRemove.Add(i);
//mark this row's index for deletion
}
}
//loop through list in reverse order and remove rows by their index
for (int i = rowsToRemove.Count; i > 0; i--)
table.Rows.RemoveAt(i);
Best solution I can think of is the following one:
public bool IsRowEmpty(DataRow row)
{
if (row == null)
return true;
foreach(var value in row.ItemArray)
{
if (value != null)
return false;
}
return true;
}
Then from your main code:
foreach (DataRow row in result.Rows)
{
if(!IsRowEmpty(row))
{
// Row is not empty.
}
}
Hello,
You can simply achive this using DataView : like this -
DataView dv = yourTable.DefaultView;
dv.Sort = "column_1,column_2";
DataTable dtNew = dv.ToTable(true, "column_1", "column_2",...,"column_n"); // please mention all columns here
dtNew.Rows.RemoveAt(0);
We have a C# app that populates tables on worksheets within an Excel document.
The tables must be populated in the order the rows are returned from the database.
The object DataFileColData is defined as a List and contains the result set rows. For testing purposes, I'm only using [0] of the List.
Code segment #1 below doesn't work. Row order is not preserved in that the end result has the data displayed out of order although the numbers themselves are listed in order:
if (DataFileColData[0].Count() > 0)
{
ConcurrentDictionary<int, DataRow> theRows = new ConcurrentDictionary<int, DataRow>(9, DataFileColData[0].Count());
Parallel.For(0, DataFileColData[0].Count(), i =>
{
// go through each column
int c = 0;
try
{
foreach (var Col in DataFileColData)
{
var cell = Col[i];
if (cell != null)
{
if (cell.GetType().Name == "JArray") //If Jarray then table compression was used not column compression
{
if (theRows.TryAdd(i, Dt.NewRow()))
theRows[i].ItemArray = JsonConvert.DeserializeObject<object[]>(Col[i].ToString());
}
else
{
if (theRows.TryAdd(i, Dt.NewRow()))
theRows[i][c] = cell;
}
}
c++;
}
} //try
catch (Exception e)
{
throw new Exception("Exception thrown in \"PublicMethods.cs | RenderExcelFile\" while in foreach loop over DataFileColData: " + e.ToString());
}
} //for
); //parallel
//Add the rows to the datatable in their original order
//(might have gotten skewed from the parallel.for loop)
for (int x = 0; x < theRows.Count; x++)
Dt.Rows.Add(theRows[x]);
//Set the name so it appears nicely in the Excel Name Box dropdown instead of "table1", "table2", etc etc.
Dt.TableName = ExcelTableSpec.TableTitle + " " + r.TableID;
}
code segment #2 below does work with the row order and data associated with each row preserved :
if (DataFileColData[0].Count() > 0)
{
DataRow[] theRows = new DataRow[DataFileColData[0].Count()];
Parallel.For(0, DataFileColData[0].Count(), i =>
{
DataRow Rw = Dt.NewRow();
// go through each column
int c = 0;
try
{
foreach (var Col in DataFileColData)
{
var cell = Col[i];
if (cell != null)
{
if (cell.GetType().Name == "JArray") //If Jarray then table compression was used not column compression
{
lock (theRows)
{
theRows[i] = Dt.NewRow();
theRows[i].ItemArray = JsonConvert.DeserializeObject<object[]>(Col[i].ToString());
}
}
else
{
lock (theRows)
{
theRows[i] = Dt.NewRow();
theRows[i][c] = cell;
}
}
}
c++;
}
} //try
catch (Exception e)
{
throw new Exception("Exception thrown in \"PublicMethods.cs | RenderExcelFile\" while in foreach loop over DataFileColData: " + e.ToString());
}
} //for
); //parallel
//Add the rows to the datatable in their original order
//(might have gotten skewed from the parallel.for loop)
Dt = theRows.CopyToDataTable();
//Set the name so it appears nicely in the Excel Name Box dropdown instead of "table1", "table2", etc etc.
Dt.TableName = ExcelTableSpec.TableTitle + " " + r.TableID;
}
I don't understand why. I didn't think the locking mechanism would be needed because each thread gets its own instance of "i" and a ConcurrentDictionary is supposed to be thread safe.
Would someone be able to explain to me please why the code isn't working the way I think it should?
Thank you!
UPDATED CODE as per #Enigmativity's comments below.
The MSDN documentation isn't quite clear (to me anyway), but does appear to update the DataTable even though the MSDN documentation doesn't indicate it does when executing the NewRow() method.
New working code below:
if (DataFileColData[0].Count() > 0)
{
DataRow[] theRows = new DataRow[DataFileColData[0].Count()];
Parallel.For(0, DataFileColData[0].Count(), i =>
//for (int i = 0; i < DataFileColData[0].Count(); i++)
{
lock (Dt)
{
theRows[i] = Dt.NewRow();
}
// go through each column
int c = 0;
try
{
foreach (var Col in DataFileColData)
{
var cell = Col[i];
if (cell != null)
{
if (cell.GetType().Name == "JArray") //If Jarray then table compression was used not column compression
{
theRows[i].ItemArray = JsonConvert.DeserializeObject<object[]>(Col[i].ToString());
}
else
{
theRows[i][c] = cell;
}
}
c += 1;
} //foreach
} //try
catch (Exception e)
{
throw new Exception("Exception thrown in \"PublicMethods.cs | RenderExcelFile\" while in foreach loop over DataFileColData: " + e.ToString());
}
} //for
); //parallel
//Add the rows to the datatable in their original order
//(might have gotten skewed from the parallel.for loop)
Dt = theRows.CopyToDataTable();
//Set the name so it appears nicely in the Excel Name Box dropdown instead of "table1", "table2", etc etc.
Dt.TableName = ExcelTableSpec.TableTitle + " " + r.TableID;
//cleanup
if (theRows != null)
Array.Clear(theRows, 0, theRows.Length);
theRows = null;
} //if (DataFileColData[0].Count() > 0)
Please see the documentation for (MSDN Data Tables).
The key point is:
Thread Safety
This type is safe for multithreaded read operations. You must
synchronize any write operations.
So it's not i the the ConcurrentDictionary causing your issues.
I've decompiled the NewRow method and there is a call to NewRow(int record). This code clearly shows write operations.
internal DataRow NewRow(int record)
{
if (-1 == record)
record = this.NewRecord(-1);
this.rowBuilder._record = record;
DataRow row = this.NewRowFromBuilder(this.rowBuilder);
this.recordManager[record] = row;
if (this.dataSet != null)
this.DataSet.OnDataRowCreated(row);
return row;
}
I have a dataset which has duplicate rows i want my error message to execute when duplicate rows are present.
Below is my code please help
DataSet dsXml = new DataSet();
dsXml.ReadXml(new XmlTextReader(new StringReader(xml)));
Hashtable hTable = new Hashtable();
ArrayList duplicateList = new ArrayList();
foreach (DataRow drow in dsXml.Tables[0].Rows)
{
if (hTable.Contains(drow))
{
duplicateList.Add(drow);
}
else
{
script.Append("alert('Error - There are some Duplicate entries.'); ");
ErrorOcc = true;
if (ErrorOcc)
{
this.ScriptOutput = script + " ValidateBeforeSaving = false;";
this.StayContent = "yes";
return;
}
}
}
Your code is not working, because DataRow instances will be compared by references instead of comparing their fields. You can use custom comparer:
public class CustomDataRowComparer : IEqualityComparer<DataRow>
{
public bool Equals(DataRow x, DataRow y)
{
if (x.ItemArray.Length != y.ItemArray.Length)
return false;
for (int i = 0; i < x.ItemArray.Length; i++)
if (!x[i].Equals(y[i]))
return false;
return true;
}
public int GetHashCode(DataRow obj)
{
int hash = 17;
foreach (object field in obj.ItemArray)
hash = hash * 19 + field.GetHashCode();
return hash;
}
}
or use existing DataRowComparer which compares DataRow objects for equivalence by using value-based comparison:
HashSet<DataRow> set = new HashSet<DataRow>(DataRowComparer.Default);
// or: new HashSet<DataRow>(new CustomDataRowComparer());
foreach (DataRow row in dsXml.Tables[0].Rows)
{
if (!set.Add(row))
// duplicate row
}
You can also check if duplicated rows exist with Linq to DataSet query:
var duplicatedRowsExist = dsXml.Tables[0].AsEnumerable()
.GroupBy(r => r, DataRowComparer.Default)
.Any(g => g.Count() > 1);
You have to compare the content of the rows, not the rows themselves. Something like this should do it:
var hasDupes = dsXml.Tables[0].Rows
.AsEnumerable()
.GroupBy(row => new
{
row.Field<string>("Title"),
row.Field<string>("Address"),
row.Field<string>("State"),
row.Field<string>("City"),
row.Field<int>("Status"),
row.Field<int>("CreatedBy"),
row.Field<int>("UpdatedBy")
})
.Where(g => g.Count() > 1)
.Any();
if(hasDupes)
//Show error message
I think you have to alter you logic a little. You don't add the row to the hTable, so there are never duplicates. And I guess you have to show the message in the end, else the list will not be complete yet.
As stated by others, you do need Sergeys answer to get the comparison to work. If you have that covered, this code will solve the other logic problems.
foreach (DataRow drow in dsXml.Tables[0].Rows)
{
if (!hTable.Contains(drow))
{
hTable.Contains(drow);
hTable.Add(drow);
}
else
{
duplicateList.Add(drow);
}
}
script.Append("alert('Error - There are some Duplicate entries.'); ");
ErrorOcc = true;
if (ErrorOcc)
{
this.ScriptOutput = script + " ValidateBeforeSaving = false;";
this.StayContent = "yes";
return;
}
First, you need to define your comparison between rows. It appears when you create your hTable there is nothing in it, so the hTable.Contains call is always going to return false.
As a side note, you can't just compare a DataRow with another DataRow, it will use the default equality comparison (implemented using IEqualityComparer) and effectively boils down to a reference equality check, which none of the rows will be equal to each other.
Somewhere, you can either implement your own IEqualityCompariosn, or simply write a custom method to check the values of each row.
Here is the answer of my above Question
You can Check duplicate rows in dataset.. it is working fine try it.
DataSet dsXml = new DataSet();
dsXml.ReadXml(new XmlTextReader(new StringReader(xml)));
List<string> duplicateList = new List<string>();
foreach (DataRow drow in dsXml.Tables[0].Rows)
{
string strr = "";
for (int j = 0; j < dsXml.Tables[0].Columns.Count; j++ )
{
strr += drow[j];
}
if (!duplicateList.Contains(strr))
{
duplicateList.Add(strr);
}
else
{
script.Append("alert('Error - There are some Duplicate entries.'); ");
ErrorOcc = true;
if (ErrorOcc)
{
this.ScriptOutput = script + " ValidateBeforeSaving = false;";
this.StayContent = "yes";
return;
}
}
}
I have a dataGridView that has 3 columns: SystemId, FirstName, LastName that is bound using database information. I would like to highlight a certain row, which I would do using:
dataGridView1.Rows[????].Selected = true;
The row ID I however do not know and the bindingsource keeps changing, thus row 10 could be "John Smith" in one instance but not even exist in another (I have a filter that filters out the source based on what user enters, so typing in "joh" would yield all rows where first / last name have "joh" in them, thus my list can go from 50 names to 3 in a click).
I want to find a way how I can select a row based on SystemId and a corresponding number. I can get the system ID using the following method:
systemId = dataGridView1.Rows[dataGridView1.CurrentRow.Index].Cells["SystemId"].Value.ToString();
Now I just need to apply it to the row selector. Something like dataGridView1.Columns["SystemId"].IndexOf(systemId} but that does not work (nor does such method exist). Any help is greatly appreciated.
This will give you the gridview row index for the value:
String searchValue = "somestring";
int rowIndex = -1;
foreach(DataGridViewRow row in DataGridView1.Rows)
{
if(row.Cells[1].Value.ToString().Equals(searchValue))
{
rowIndex = row.Index;
break;
}
}
Or a LINQ query
int rowIndex = -1;
DataGridViewRow row = dgv.Rows
.Cast<DataGridViewRow>()
.Where(r => r.Cells["SystemId"].Value.ToString().Equals(searchValue))
.First();
rowIndex = row.Index;
then you can do:
dataGridView1.Rows[rowIndex].Selected = true;
The above answers only work if AllowUserToAddRows is set to false. If that property is set to true, then you will get a NullReferenceException when the loop or Linq query tries to negotiate the new row. I've modified the two accepted answers above to handle AllowUserToAddRows = true.
Loop answer:
String searchValue = "somestring";
int rowIndex = -1;
foreach(DataGridViewRow row in DataGridView1.Rows)
{
if (row.Cells["SystemId"].Value != null) // Need to check for null if new row is exposed
{
if(row.Cells["SystemId"].Value.ToString().Equals(searchValue))
{
rowIndex = row.Index;
break;
}
}
}
LINQ answer:
int rowIndex = -1;
bool tempAllowUserToAddRows = dgv.AllowUserToAddRows;
dgv.AllowUserToAddRows = false; // Turn off or .Value below will throw null exception
DataGridViewRow row = dgv.Rows
.Cast<DataGridViewRow>()
.Where(r => r.Cells["SystemId"].Value.ToString().Equals(searchValue))
.First();
rowIndex = row.Index;
dgv.AllowUserToAddRows = tempAllowUserToAddRows;
Or you can use like this. This may be faster.
int iFindNo = 14;
int j = dataGridView1.Rows.Count-1;
int iRowIndex = -1;
for (int i = 0; i < Convert.ToInt32(dataGridView1.Rows.Count/2) +1; i++)
{
if (Convert.ToInt32(dataGridView1.Rows[i].Cells[0].Value) == iFindNo)
{
iRowIndex = i;
break;
}
if (Convert.ToInt32(dataGridView1.Rows[j].Cells[0].Value) == iFindNo)
{
iRowIndex = j;
break;
}
j--;
}
if (iRowIndex != -1)
MessageBox.Show("Index is " + iRowIndex.ToString());
else
MessageBox.Show("Index not found." );
Try this:
string searchValue = textBox3.Text;
int rowIndex = -1;
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
try
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (row.Cells["peseneli"].Value.ToString().Equals(searchValue))
{
rowIndex = row.Index;
dataGridView1.CurrentCell = dataGridView1.Rows[rowIndex].Cells[0];
dataGridView1.Rows[dataGridView1.CurrentCell.RowIndex].Selected = true;
break;
}
}
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
If you just want to check if that item exists:
IEnumerable<DataGridViewRow> rows = grdPdfs.Rows
.Cast<DataGridViewRow>()
.Where(r => r.Cells["SystemId"].Value.ToString().Equals(searchValue));
if (rows.Count() == 0)
{
// Not Found
}
else
{
// Found
}
This builds on the above answer from Gordon--not all of it is my original work.
What I did was add a more generic method to my static utility class.
public static int MatchingRowIndex(DataGridView dgv, string columnName, string searchValue)
{
int rowIndex = -1;
bool tempAllowUserToAddRows = dgv.AllowUserToAddRows;
dgv.AllowUserToAddRows = false; // Turn off or .Value below will throw null exception
if (dgv.Rows.Count > 0 && dgv.Columns.Count > 0 && dgv.Columns[columnName] != null)
{
DataGridViewRow row = dgv.Rows
.Cast<DataGridViewRow>()
.FirstOrDefault(r => r.Cells[columnName].Value.ToString().Equals(searchValue));
rowIndex = row.Index;
}
dgv.AllowUserToAddRows = tempAllowUserToAddRows;
return rowIndex;
}
Then in whatever form I want to use it, I call the method passing the DataGridView, column name and search value. For simplicity I am converting everything to strings for the search, though it would be easy enough to add overloads for specifying the data types.
private void UndeleteSectionInGrid(string sectionLetter)
{
int sectionRowIndex = UtilityMethods.MatchingRowIndex(dgvSections, "SectionLetter", sectionLetter);
dgvSections.Rows[sectionRowIndex].Cells["DeleteSection"].Value = false;
}
Those who use WPF
for (int i = 0; i < dataGridName.Items.Count; i++)
{
string cellValue= ((DataRowView)dataGridName.Items[i]).Row["columnName"].ToString();
if (cellValue.Equals("Search_string")) // check the search_string is present in the row of ColumnName
{
object item = dataGridName.Items[i];
dataGridName.SelectedItem = item; // selecting the row of dataGridName
dataGridName.ScrollIntoView(item);
break;
}
}
if you want to get the selected row items after this, the follwing code snippet is helpful
DataRowView drv = dataGridName.SelectedItem as DataRowView;
DataRow dr = drv.Row;
string item1= Convert.ToString(dr.ItemArray[0]);// get the first column value from selected row
string item2= Convert.ToString(dr.ItemArray[1]);// get the second column value from selected row
string text = "param";
var rows = dataGridViewlist.Rows.OfType<DataGridViewRow>()
.Select(x => new { Value = x.Cells[6].Value.ToString(), Index = x.Index }).ToList();
var index= rows.Where(w => w.Value == text).Select(s => s.Index).FirstOrDefault();
You can try:
string DT = string.Empty;
try
{
DT = $"{dataGridView1.SelectedRows[0].Cells[1].Value}-{dataGridView1.SelectedRows[0].Cells[2].Value}-{dataGridView1.SelectedRows[0].Cells[3].Value}-{dataGridView1.SelectedRows[0].Cells[4].Value}";
}
catch { }
try
{
dataGridView1.Rows.Cast<DataGridViewRow>().Where(x => $"{x.Cells[1].Value}-{x.Cells[2].Value}-{x.Cells[3].Value}-{x.Cells[4].Value}" == DT).ToList()[0].Selected = true;
}
catch (Exception ex) { }