Bind and Highlight Specific DataGridView Row - c#

I am binding data from excel file in a list on button click and this works perfectly. Finally the data is binded to a DataGridView. Now I want to iterate the list to check if there are any data that isn't included to the database after binding to a DataGridView. If any data mismatches, then it should highlight the specific row with red color in the DataGridView. Note: There could be multiple data that will not match. Something as the below image and the code tried:
grdUpload.Rows.Clear();
for (int i = 0; i < lstData.Count; i++) //lstData - The Data List
{
if (Facede.ExcelUpload.CheckIfExists(lstData)) //Checking if any data mismatches
{
grdUpload.DataSource = lstData;
grdUpload.Rows[i].DefaultCellStyle.BackColor = Color.Red; //Highlight the row data that mismatches
}
else
{
grdUpload.DataSource = lstData;
}
}
public bool CheckIfExists(List<Data> lst)
{
bool flag = false;
foreach (Data d in lst)
{
string Query = "SELECT M.EmpNo FROM Data m WHERE M.EmpNo = '" + d.EmpNo + "'";
DataTable dt = SelectData(Query);
if (dt != null && dt.Rows.Count > 0)
{
flag = true;
}
else
{
flag = false;
}
}
return flag;
}
Now the issue is it doesn't highlight the specific row if data like EmpNo mismatches. Anything that I am missing here?

Problem is in your for loop.
You are firstly binding data to your datagridview.
Then you are entering for loop
Inside it you ask if condition is met and if it is you AGAIN bind same data to datagridview but after it you color it.
For loop continues and it again enters part where it meets condition and AGAIN you BIND same data but now you overwrite colored data with new (but same) data and then color some new row.
So what you need to do is
Load data into datagridview
Loop through datagridviewrows and if meet condition color that row
So code should look like this:
//Here you bind your data to datagridview
//In code bellow if you want to get row's column's data use
//row.Cells["CELL_VALUE"].Value (convert to what datatype you need before comparing)
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (condition)))
{
dataGridView1.Rows[row.Index].DefaultCellStyle.BackColor = Color.Red;
}
}

Related

Two different datagridview rows getting selected when one should be selected

I am working with winform project. i have two datagridview which has same number of columns and same structure. frist column is checkbox column.
here is code to bind two datagrid with same kind of data.
List<AllGroupNames> grpDtl = GetAllGroups(Nodes);
List<AllGroupNames> grpDtl1 = GetAllGroups(Nodes); //grpDtl.GetRange(0, grpDtl.Count);
//bind two grid with all groups name
if (_grpDtl != null && _grpDtl.Count > 0)
{
dgSingleGroups.AutoGenerateColumns = false;
dgSingleGroups.DataSource = grpDtl;
dgSingleGroups.Columns[0].DataPropertyName = "Select";
dgSingleGroups.Columns[1].DataPropertyName = "GroupName";
dgSingleGroups.Columns[1].ReadOnly = true;
dgSingleGroups.Columns[0].Width = 47;
dgSingleGroups.Columns[1].Width = 346;
dgAllGroups.AutoGenerateColumns = false;
dgAllGroups.DataSource = grpDtl1;
dgAllGroups.Columns[0].DataPropertyName = "Select";
dgAllGroups.Columns[1].DataPropertyName = "GroupName";
dgAllGroups.Columns[1].ReadOnly = true;
dgAllGroups.Columns[0].Width = 47;
dgAllGroups.Columns[1].Width = 346;
}
grpDtl1 = null;
grpDtl = null;
_grpDtl = null;
GetAllGroups() iterate in treeview node collection and accumulate node name.
private List<AllGroupNames> GetAllGroups(TreeNodeCollection tnCollection)
{
//accumulate group name in recursive fashion
foreach (TreeNode tn in tnCollection)
{
if (tn.Tag != null)
{
if (((object)tn.Tag).GetType().ToString().Contains("TunerDetails"))
{
_grpDtl.Add(new AllGroupNames { Select = false, GroupName = tn.Text });
}
GetAllGroups(tn.Nodes);
}
}
return _grpDtl;
}
Now problem is when i check second grid checkbox then my first grid checkbox is getting checked invisibly means when i am reading first grid's first column value in loop then i am getting checkbox value true. where as i have not checked my first grid's any checkbox.
when i select any row of second grid that same row is getting automatically selected in first grid.
I just select one row from right side grid and same row of left side grid automatically gets selected....which is problem for me. Why two grid syncing automatically. screen shot attached.
why it is happening i am not able to capture the reason. please help me what to change in code to get rid of this problem.
Apparently every object of class AllGroupName has a Boolean property Select.
You created Column[0] such, that the checkbox is checked whenever this boolean is true, and vice versa: whenever the operator checks this checkbox, the corresponding AllGroupName.Select will become true.
You decided to use the same DataSource as object in two DataGridViews.
Operator checks the checkbox of row[4] in Dgv1
This means that DataSource[4].Select = true;
This means that all objects that have this DataSource will be notified that [4] has changed
Row[4] in Dgv1 and Dgv2 will be updated: checkbox is checked.
If you don't want that changes in Dgv1 are refelected in Dgv2, then you should make a copy of the datasource.
List<...> data1 = ...; // shouldn't this be an BindingList<...> ?
Dgv1.DataSource = data1;
List<...> data2 = new List<...>(data1); // clone data1
Dgv2.DataSource = data2;

WPF DataGrid individual row color

I have two datagrid. DG1 and DG2 with one column A (each) and many rows. Task is to highlight a particular row in one Datagrid when user selects row with same value in another datagrid. Example: User selects DG1 row 5, column A whose value is 'ABC'. 'ABC' is row 23, column A in DG2. Hence row 23 of DG should be highlighted in 'Red'.
This should work vice-versa (Clicking row 23 of DG2 should highlight row 5 of DG1).
Currently my code works by invoking the SelectedItem property of a Datagrid. And the down side to this is that there is an infinite loop of selection i.e. when user selects row 5 of DG1 it selects row 23 of DG2 which then kicks of selection for row 5 of DG1 which then kicks of selection of row 23 of DG2.
So a solution to my problem can be either to fix this infinite loop problem or provide a way to highlight a particular row of a datagrid with a specific color thus eliminating a call to selecteditem property of a datagrid altogether.
here is some code:
//This is the function invoked when a user selects a row in a DG1
private void DG1SelectedItem(object sender, SelectionChangedEventArgs e)
{
if (DG1.SelectedItem != null)
{
var val = DG1.SelectedItem; //Get the paticular row that was selected
DataRowView row = (DataRowView)val;
object[] list = row.Row.ItemArray;
String rowValue = list[0].ToString(); //get the value within that selected row
HighLightRow(DG2, account); //Call a function that highlights a particular row in a Datagrid with a given string value
}
}
//This is the function invoked when a user selects a row in a DG2
private void DG2SelectedItem(object sender, SelectionChangedEventArgs e)
{
if (DG2.SelectedItem != null)
{
var val = DG2.SelectedItem; //Get the paticular row that was selected
DataRowView row = (DataRowView)val;
object[] list = row.Row.ItemArray;
String rowValue = list[0].ToString(); //get the value within that selected row
HighLightRow(DG1, account); //Call a function that highlights a particular row in a Datagrid with a given string value
}
}
//Function to highlight the row in datagrid 'dg' with value of 'value' (only checks first column)
private void HighLightRow(DataGrid dg, string value)
{
dg.UnselectAll();
var itemsource = dg.ItemsSource as IEnumerable;
int index = 0;
ArrayList rowIndices = new ArrayList();
//loop through entire datagrid looking for the string 'value' in the first column
//if found, store the index in an array so it can be utillized to highlight all rows later on
foreach (var item in itemsource)
{
DataRowView row = item as DataRowView;
object[] list = row.Row.ItemArray;
String valueToFind = list[0].ToString();
if (valueToFind.Equals(value))
rowIndices.Add(index);
index++;
}
//For all rows where the string 'value' was foung in the first column of the datagrid, highlight it
//Currently the implementation relies on SelectedItem
for (int i = 0; i < rowIndices.Count; i++)
{
object item = dg.Items[(int)rowIndices[i]];
dg.SelectedItem = item; //this code then invokes DG2SelectedItem (or DG1SelectedItem depending on which SelectedItem function was initially invoked) and we have the inifite loop problem
**/*
This is where I would like to select the row at the indices stored in rowIndices and highlight them red (and not rely on dg.SelectedItem like in the line above)
Something like:
dg.Row[i].Background = Brushes.Red;
*/**
int m = dg.SelectedIndex;
dg.UpdateLayout();
dg.ScrollIntoView(dg.Items[m]);
}
dg.LoadingRow += Dg_LoadingRow;
}
Writing code in the code-behind of a Window or Control is not recommended in WPF. Using MVVM approach is far superior. You should consider moving to that paradigm.
Having said that, the solution to your problem is simple. Just introduce a global variable named UpdateFlag. Set it to true at the start of each grid's handler and to false at the end of the handler. At the very beginning of each handler (before setting the flag) check if this flag is true and exit the handler immediately if that is the case. This will stop the infinite recursion of selection change.
Some thing like this:
private bool UpdateFlag = false;
private void FirstHandler()
{
if(UpdateFlag) return;
UpdateFlag = true;
//Do your highlighting here
UpdateFlag = false;
}
private void SecondHandler()
{
if(UpdateFlag) return;
UpdateFlag = true;
//Do your highlighting here
UpdateFlag = false;
}

Why is DataView readonly despite "AllowEdit=true"?

I have code (below) that I think should allow me to update the underlying tables, but I continue to get a "Underlying data is read only" error.
In the code below, everything I've tried is included. Clearly, I'm missing something! A search in SO hasn't found clues...
APT.Columns["Selected"].ReadOnly = false;
TWR.Columns["Selected"].ReadOnly = false;
RWY.Columns["Selected"].ReadOnly = false;
APT.Columns["Selected"].Expression = "false";
TWR.Columns["Selected"].Expression = "false";
RWY.Columns["Selected"].Expression = "false";
// DataView with filtered "parent" table data
// Apply user's filter for the "Fix"
DataView dataView = new DataView(APT)
{
RowFilter = FixFilter(chkboxShowAll.Checked),
Sort = "FacilityID",
AllowEdit = true,
};
// RWY and TWR dont have ARTCC (filter item),
// so we can't filter by that,
// and "Join" makes the table readonly,
//so must grab root ID and change manually
DataView dvTWR = new DataView(TWR)
{
AllowEdit = true // Enable editing
};
DataView dvRWY = new DataView(RWY)
{
AllowEdit = true
};
// TWRs and RWYs have the same ID as the APT,
// but not may have a TWY or RWY (e.g., Seabase)
// This should update the "Selected" boolean
foreach (DataRowView dataRowView in dataView)
{
dataRowView.BeginEdit();
dataRowView["Selected"] = true; // *** <- EXCEPTION HERE
dataRowView.EndEdit();
int result = dvRWY.Find(dataRowView["ID"]);
if (result != -1)
{
dvRWY[result].BeginEdit();
dvRWY[result]["Selected"] = true;
dvRWY[result].EndEdit();
}
result = dvTWR.Find(dataRowView["ID"]);
if (result != -1)
{
dvTWR[result].BeginEdit();
dvTWR[result]["Selected"] = true;
dvTWR[result].EndEdit();
}
}
dvTWR.Dispose(); // Commit/close tables
dvRWY.Dispose();
At the annotated code line, the exception fires:
System.Data.ReadOnlyException: 'Column 'Selected' is read only.'
Can anyone tell me the error of my ways?
Dai is correct: A dataview cannot modify static public in-memory tables. However, a datagridview, bound directly to the datatable, can.
This is a better solution for me, as ultimately I need to display the "selected" rows: I created a datagridview for each table and placed each one in a tab control container. I checked the "Can edit" box for each datagridview in designer.
One can then drilldown to modify each column's readonly property within designer, but I found it easier to assign the readonly property after I read data into datatables from the text files (either one works, and one can change the designer-setting programmatically as they are actually the same property).
Looping the "Selected" column rows true or false is conveniently dependent on the filter applied to the datagridview, making a single calling routine efficient (just do or do not apply a filter to the datagridview(s) and pass the dgv and/or columns to the called routine).
As a plus, users can un/check "Selected" rows. The underlying table is changed! The tables can then be efficiently filtered on "Selected" for other events/output.
// schema built in VS designer...
static public DataTable APT = new SCTData.APTDataTable();
// Table populated from FAA fixed-column text files (cols vary by file)
ReadFixes.FillAPT();
// Assign the table to the datagridview, apply filter and sort
DgvArpt.DataSource = APT;
(DgvArpt.DataSource as DataTable).DefaultView.RowFilter = filter;
AdjustColumnOrderNSort(DgvArpt);
// Loop the filtered datagrid to make "Selected" col = true
UpdateSelected(DgvArpt,true);
private void UpdateSelected (DataGridView dgv, bool hasFilter = false)
{
// If the filter is applied, selected boxes are true
// otherwise, ALL the selected boxes are false
// But if the ShowAll box is checked, ignore the update
if (chkboxShowAll.Checked == false)
{
foreach (DataGridViewRow row in dgv.Rows)
{
row.Cells["Selected"].Value = hasFilter;
}
}
}

Condition to get specific cell value in ultragrid

I am trying to make some cells readonly on specific condition(flag). But i'm having problem to set the exact condition. I have a not null bit column and trying to set condition on its value.
Here is my code :
private void grdOtherItemsInfo_InitializeLayout(object sender, InitializeLayoutEventArgs e)
{
UltraGridBand band;
try
{
band = e.Layout.Bands[0];
band.ColHeaderLines = 2;
foreach (UltraGridRow row in **can't find right option**)
{
if (row.Cells[OtherItemStoreRequisitionForBatchChild.IsAutoDispense].Value.ToString() == "1")
{
band.Columns[OtherItemStoreRequisitionForBatchChild.IsAutoDispense].CellActivation = Activation.ActivateOnly;
band.Columns[OtherItemStoreRequisitionForBatchChild.IndentedUOM].CellActivation = Activation.ActivateOnly;
band.Columns[OtherItemStoreRequisitionForBatchChild.IndentedQty].CellActivation = Activation.ActivateOnly;
}
}
}
foreach (UltraGridRow row in grdOtherItemsInfo.Rows)
{
foreach (UltraGridRow urow in grdChemicalItemInfo.Rows[row.Index].ChildBands[0].Rows)
{
if (Convert.ToBoolean(urow.Cells[OtherItemStoreRequisitionForBatchChild.IsAutoDispense].Value))
{
foreach (UltraGridCell col in urow.Cells)
{
col.Activation = Activation.ActivateOnly;
}
}
}
}
The band has no rows property. If you need to iterate through all the rows you need to call grdOtherItemsInfo.Rows. You may use code like this:
private void grdOtherItemsInfo_InitializeLayout(object sender, Infragistics.Win.UltraWinGrid.InitializeLayoutEventArgs e)
{
UltraGridBand band;
try
{
band = e.Layout.Bands[0];
band.ColHeaderLines = 2;
// Rows collection of the grid contains the rows in Band[0] or the top level of GroupByRows
foreach (UltraGridRow row in this.grdOtherItemsInfo.Rows)
{
// Check if the row is DataRow, otherwise you will get an exception when you call Cell property of not data row
if (row.IsDataRow)
{
// Cashing the cell so not taking it twice
UltraGridCell cell = row.Cells[OtherItemStoreRequisitionForBatchChild.IsAutoDispense];
if (cell.Value.ToString() == "1")
{
// Setting the cells' Activation will set each cell its own activation
// If you set it to the column all the cells in the column will have same activation
cell.Activation = Activation.ActivateOnly;
row.Cells[OtherItemStoreRequisitionForBatchChild.IndentedUOM].Activation = Activation.ActivateOnly;
row.Cells[OtherItemStoreRequisitionForBatchChild.IndentedQty].Activation = Activation.ActivateOnly;
}
}
}
}
catch(Exception ex)
{
// TODO: ...
}
}
Note that in your code you are going through all the rows and you are setting the column CellActivation depending on some row's cell value. In the result of this the CellActivation may change several times but in the end it will depend on the value of the cell in the last row. Setting the CellActivation on the column force all the cells in that column to have same CellActivatio. If you need different CellActivation for each cell in a column you need to set the Activation property of each individual cell - this is what the code I send you do.
Check also this link showing how to iterate grid's rows

DataGridView Filter Data

So probably a newbie question but something I could not find on google a let say resanable example off.
So I have a DataTable that I created from some xml
The columns are like:
key
Description
parent
This DataTable is bound to a gridview with a DataBinding
The filter form returns an array of keys(string)
Now I would like to display only keys that get returned from filter.
My code so far:
List<string> listKeys = new List<string>(_keys);
foreach (DataGridViewRow row in grdData.Rows)
{
string k = row.Cells[4].Value.ToString();
if (!listKeys.Contains(k))
{
CurrencyManager currencyManager1 = (CurrencyManager)BindingContext[grdData.DataSource];
currencyManager1.SuspendBinding();
row.Visible = false;
currencyManager1.ResumeBinding();
}
else
{
row.Visible = true;
//listKeys.Remove(k);
}
}
The result I am getting is either always full grid or none.
And the code above is very slow(performance wise). Any faster way of doing this?

Categories

Resources