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
Related
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;
}
This is the table. On the right are the namesI have a gridview that shows a table from the database. I want to search a name in the gridview using a textbox and a button. This is what i have so far. When i want to search I get in a messagebox this: object reference is not set on an instance of an object.
private void btn_zoek_Click(object sender, EventArgs e)
{
string searchValue = tb_SearchOverzicht.Text;
metroGrid1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
try
{
foreach (DataGridViewRow row in metroGrid1.Rows)
{
if (row.Cells[2].Value.ToString().Equals(searchValue))
{
row.Selected = true;
break;
}
}
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
}
Check if the values and object you are trying to work with are filled with data and not null.
In your row
if (row.Cells[2].Value.ToString().Equals(searchValue))
you should check first, if the cell exists and has value, like this:
if(row.Cells[2] != null && row.Cells[2].Value.ToString() == searchValue)
{
// Some code
}
and just to be sure, check if your searchValue is not empty or without any chars or maybe even correct formatted:
if(!String.IsNullOrWhiteSpace(searchValue))
but I dont think this is necessary here, just nice to have.
The goal here is: Not all values have to be filled. It can appear that a cell doesnt have value in that column, the row may be the filter row or even empty, or worst: the grid isn't even initialized. So please try to check if values or objects are filled before using them.
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;
}
}
I have a DataGridView where the values found in the 1st column should not be repeated, and neither should it have any blank columns. This is the method I've implemented in order to remove this repetition:
public static void eliminateRepetition(DataGridView table)
{
for (int currentRow = 0; currentRow < table.RowCount; currentRow++)
{
DataGridViewRow rowToCompare = table.Rows[currentRow];
foreach (DataGridViewRow row in table.Rows)
{
if (row.Equals(rowToCompare)) // If the row to compare is the current row, skip
{
continue;
}
if (row.Cells[0].Value != null && rowToCompare.Cells[0].Value != null)
{
if (int.Parse(row.Cells[0].Value.ToString()) != int.Parse(rowToCompare.Cells[0].Value.ToString())) // 1st column values must match in order to be considered as a repetition
{
continue;
}
else
{
table.Rows.Remove(row); // Remove repeated row
}
}
else
{
table.Rows.Remove(row); // Remove blank rows
}
}
}
}
The result I have is always 2 rows with the same value in the 1st column.
Any help will be much appreciated.
After removing a row from the table, the indexes change. Next iteration will not be as expected.
I think it would be better to create a list of objects. Make them unique in terms of your rule. Then set the DataSource property of the datagridview to the object list you have.
I am showing a Totals Row as last row. I want to exclude that row from sorting when the user clicks on the column header. By using sql union I am adding total column to my result. I am using SQL, C# and DataGridView control. I am not able to expose ColumnHeader_Click event. I am only using TableStyle[0].AllowSorting = false. How can I apply that custom sorting on the control?
Thanks
Thanks TaW, your answer helped me. My needs were a little different, I needed the Total to appear at the top and also retain the sort column throughout as my grid is highly interactive with loads of filtering and changes in the data being presented.
My sorting is done via
protected void ReportGridView_Sorting(object sender, GridViewSortEventArgs e)
Here's what I ended up using in my method to populate the GridView:
if (!myDataTable.Columns.Contains("SortLevel"))
{
myDataTable.Columns.Add("SortLevel", typeof(Int16));
foreach (DataRow dr in myDataTable.Rows)
{
dr["SortLevel"] = 0;
}
dt.Rows[0]["SortLevel"] = 1;
}
if ((Session["SortDirection"] != null) && (Session["SortExpression"] != null))
{
myDataTable.DefaultView.Sort = "SortLevel DESC, " + Session["SortExpression"] + " " + Session["SortDirection"];
}
MyGridView.DataSource = myDataTable;
MyGridView.AllowSorting = true;
MyGridView.DataBind();
Side note: I had to use Sessions to hold the custom sorting instead of ViewState as this wasn't working properly with the dynamically created buttons in my gridview
This solution is based on #T.S.'s suggestion but works directly in the DataSet, not in SQL.
I have tested it in VS2013; I don't know if it will work in .Net 1.1 and would have to revive a very old machine to test that..
I don't know what you mean by
I am not able to expose columnheader_click event.
I have split the solution into a function and the ColumnHeaderMouseClick event; if you really can't use that event you will have to find another way; but one way or the other you need to trigger the sort and decide by which column(s) to sort.
The heart of the solution are the setting of the new column values and the expression by which you identify your 'TotalsRow'. I have used my test table's PK column 'ID' and push the 'ID=1 record' to the bottom. After setting the sort column to 0 on all rows the first row to fit the expression is set to maxInt.
You will have to adapt that to an expression that works for your result set.
I am adding and setting the custom sort column dynamically and remove it after the sort; the DGV is suspending its layout until the whole affair is completed.
DataTable yourTable = ..
private void dataGridView1_ColumnHeaderMouseClick(object sender,
DataGridViewCellMouseEventArgs e)
{
string col = dataGridView1.Columns[e.ColumnIndex].Name;
if (col != "") sortDGV (col );
}
private void sortDGV(string col)
{
dataGridView1.SuspendLayout();
yourTable.Columns.Add("sortMe", typeof(Int32));
yourTable.DefaultView.Sort = col;
DataRow[] dr = yourTable.Select("ID='1'");
for (int r = 0; r < yourTable.Rows.Count; r++) yourTable.Rows[r]["sortMe"] = 0;
dr[0]["sortMe"] = int.MaxValue;
yourTable.DefaultView.Sort = "sortMe," + col;
yourTable.Columns.Remove("sortMe");
dataGridView1.ResumeLayout();
}
Although 8 years old, I came across this desire myself but had a different solution: I wanted the last Row to be an "Editing Row" which would always remain at the bottom regardless of sorting.
So, create a DataGridViewRow separate from the regular data grid, add it after filling the DGV. Before sorting, remove the Row from the DGV, then add it again after sorting. I used Clicking on the RowHeaderText to trigger the sort (manual sort using a private class comparer for the sorting, and the DGV_Sort event to indicate sorting has completed. (You have to keep track of when you enter this event as I found it enters before and after sorting - so I used a form global boolean to keep tack of that.
I keep track of any editing on my 'my Removeable Row' separately, but you can always just Clone the row before removing if you don't want to do that.
Note that I have AllowUserToAddRows = true when I start, but after I programmatically fill the grid, I switch it to false, to prevent the addition of more 'editable' rows beneath my 1 row edit at a time desire. I have a button to add the row to the DGV when finished editing, at which time I just create a new myRemovableRow and add that row to the DGV.
Public partial class Form1 : Form
{
public DataGridViewRow myRemoveableRow;
public bool bDoingSort = false;
.
.
private DataGridViewRow CreateNewBlankRow()
{ // create your a new row with whatever default values for DGV
DataGridViewRow newRow = (DataGridViewRow) DGV.Rows[0].Clone();
// or however you want to create your row and fill in default values
return newRow;
}
private void FillDGV()
{
// Do whatever to fill your DataGridView (called DGV here)
myRemoveableRow = CreateNewBlankRow();
DGV.Rows.Add(myRemoveableRow);
}
private void DGV_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
bDoingSort = true;
DGV.Rows.Remove(myRemoveableRow);
SortOrder dir = SortOrder.Ascending; // or whatever logic you use
.
DGV.Sort(new RowComparer(dir, e.ColumnIndex));
}
private void DGV_Sorted(object sender, EventArgs e)
{ // 'Sorted' Event from the DataGridView events
if (bDoingSort)
{
DGV.Rows.Add(myRemoveableRow);
bDoingSort = false; //reset sorting boolean
}
else // we haven't set the sorting up yet
{
return;
}
}
// setup manual sorter (I use separate Classes for int, string, double etc)
private class RowComparer : System.Collections.IComparer
{
private static int sortOrderModifier = 1;
private readonly int Column;
public RowComparer(SortOrder sortorder, int iColumn)
{
if (sortOrder == SortOrder.Descending)
sortOrderModifier = -1;
else
sortOrderModifier = 1;
this.Column = iColumn;
}
public int Compare (Object objA, Object objB)
{
DataGridViewRow row1 = (DataGridViewRow)objA;
DataGridViewRow row2 = (DataGridViewRow)objB;
// do your sort compare (for eg.)
return sortOrderModifier * (row1.Cells[Column].Value).CompareTo(row2.Cells[Column].Value);
}
}
}
I find this works well - I not only sort but filter (out) entries on the fly. (VS 2022)