Total cell count of a DataGridView - c#

I need to get the total number of cells that are present in a datagridview. This is then used to determine if I want to include the column header text when copying/pasting the data, I only want this displayed if all records are selected.
I am using the following code to get the total number of cells but is there a better way to get this value?
var totalCellCount = DataGridView2.ColumnCount * DataGridView2.RowCount;
I couldn't find a property that contained a count of all cells, maybe I am missing it. Is there a better way to get the number of cells?
My datagridview has the ClipboardCopyMode set to EnableWithAutoHeaderText, but I want to set it to EnableAlwaysIncludeHeaderText when they select all rows/columns in the grid. So I am using the total number of cells in the code below:
private void DataGridView_KeyPress(object sender, KeyPressEventArgs e)
{
if (m_RecordCount == 0)
return;
var totalCellCount = DataGridView2.ColumnCount * DataGridView2.RowCount;
if (DataGridView2.SelectedCells.Count == totalCellCount)
{
if (e.KeyChar == (char)3)
{
DataGridView2.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText;
var clipboardContent = this.DataGridView2.GetClipboardContent();
if (clipboardContent != null)
{
Clipboard.SetText(clipboardContent.GetText(TextDataFormat.Text));
}
e.Handled = true;
}
}
}

The DataGrid.Items property returns a DataGridItemCollection representing the DataGridItems in the DataGrid.
Each DataGridItem is representative of a single row in the rendered table. Also, the DataGridItem exposes a Cells property which represents the no. of tablecells (in other words, the columns) in the rendered table. From here if you need any other custom scenarios you will have to either add it to the original Question or code a solution
var rowCount = DataGridView2.Items.Count; //Number of Items...i.e. Rows;
// Get the no. of columns in the first row.
var colCount = DataGridView2.Items[0].Cells.Count;
if you want the total number of Rows also try
If you want to get at total items an you want a real total for example if you have multiple pages.. If so you shouldn't be trying to find that information from the GridView but instead look at the underlying DataSource that you bound your GridView.
Example ----
List<SomeObject> lis = GetYourData();
DataGrid.DataSource = list;
DataGrid.DataBind();
// if you want to get the count for a specific page
int currentPage = 2;
int countForPage2 = (list.Count > currentPage * totalItemsPerPage)) ?
totalItemsPerPage : list.Count - ((currentPage - 1) * totalItemsPerPage);

Related

Show select gridview columns

I have a list of strings that I'm trying to use to control the columns shown in a gridview, but can't seem to figure out how to get it to work. Here's an example List<string> selectedHeaders = new List<string>(new string[] { "header1", "header2", "header3", "header4" });
How can I loop through the gridview columns and compare them to the values in selectedHeaders and set the visibility to false for all columns that don't match. Also note that the number of selectedHeaders can be different than the total number of columns within the gridview.
Here is what I have so far:
foreach (GridViewRow row in gvEmployees)
{
for (int i = 0; i < gvEmployees.Columns.Count; i++)
{
if (gvEmployees.Column[i].HeaderText != selectedHeaders[i])
{
gvEmployees.Column[i].Visible = false;
}
}
}
I'm not sure how to refractor this it gives me an index out of range error, because the gridview has 6 columns, but selectedHeaders could contain 1-6 values.
Your loops don't make sense for what you're trying to accomplish.
What you're doing: looping through every row in the GridView, and looping through every column within that, and looking for a string in your selectedHeaders with a matching index
What you need to be doing: looping through every column, and checking to see if there's a corresponding record in selectedHeaders by value, not by index position.
Change your code to this:
for (int i = 0; i < gvEmployees.Columns.Count; i++)
{
if (!selectedHeaders.Any(h => h == gvEmployees.Column[i].HeaderText))
{
gvEmployees.Column[i].Visible = false;
}
}

DisplayIndex reading incorrectly when rows hidden

The goal is to copy the selected cell data out of a selected row.
I'm doing this by catching the CopyingRowClipBoardContent event inside my datagrid and redirecting it to this code:
var currentCell = e.ClipboardRowContent[VwrGrid.CurrentCell.Column.DisplayIndex];
e.ClipboardRowContent.Clear();
e.ClipboardRowContent.Add(currentCell);
This works perfectly! the only issue, is that if some of the columns are hidden, DisplayIndex reads improperly.
So if we have Item 1, Item 2, and Item 3.
If all are showing and I selected item3 and copy it, I get the cell value in Item 3.
The problem is, If Item 2 is collapsed/not shown, then copying Item 3 will tell you you're trying to copy out of bounds. because it's counted displayIndex , 3 from the left, and only two were shown. so it's moved outside of the table
For WPF Datagrid, try this:
// The clipboard row works only for visible cells
// To obtain the data column use the columnIndex and then map that to the Columns collection
int columnIndex = dataGrid.CurrentCell.Column.DisplayIndex;
var column = dataGrid.Columns[columnIndex];
// Now get the needed column
var cellContent = e.ClipboardRowContent.Where(i => i.Column == column).First();
e.ClipboardRowContent.Clear();
e.ClipboardRowContent.Add(cellContent);
For Winforms:
Use .Index instead. .DisplayIndex applies only to visible columns.
Because this is WPF and I can't simply use an index, I just iterated through the columns and counter the number of columns that had their visibility set to collapsed up to the column we were attempting to grab the displayindex for. Then subtracted that number from the displayIndex.
private void DataGrid_CopyingRowClipboardContent(object sender, DataGridRowClipboardEventArgs e)
{
//because we need to use displayindex, we need to check how many collapsed columns there are before our column, and adjust our display index accordingly
int invisibleCols = 0;
foreach(DataGridColumn col in VwrGrid.Columns)
{
if (col.Visibility == Visibility.Collapsed)
invisibleCols++;
if (col.Header.ToString() == VwrGrid.CurrentCell.Column.Header.ToString()) break;
}
try
{
var currentCell = e.ClipboardRowContent[VwrGrid.CurrentCell.Column.DisplayIndex - invisibleCols];
e.ClipboardRowContent.Clear();
e.ClipboardRowContent.Add(currentCell);
}
catch
{
}
}

(C#) DataGridView: How to get Selected Count with cells AND rows?

So I have this DataGridView
And This code:
if(dataGridView1.SelectedRows.Count > 1)
{
MessageBox.Show("Error: More than one value selected");
return false;
}
It counts correctly to 2 if I have 2 completly selected rows.
But I want to check if any cells from 2 different rows or more are selected.
In other words:
in my picture my current selection is returning 1 at the moment but i want it to return 2.
Thank you.
Edit after fix: Working code:
if(dataGridView1.SelectedCells.Cast<DataGridViewCell>().Select(c => c.RowIndex).Distinct().Count() > 1)
{
MessageBox.Show("Error: More than one value selected");
return false;
}
To get the number of rows with selected cells :
int count = dataGridView1.SelectedCells.Cast<DataGridViewCell>()
.Select(c => c.RowIndex).Distinct().Count();
To check if more than one row is selected:
var selectedCells = dataGridView1.SelectedCells;
bool check = selectedCells.Count > 0
&& selectedCells.Cast<DataGridViewCell>().Any(c => c.RowIndex != selectedCells[0].RowIndex);
If you have a fairly standard setup where each column is bound to a property of your backing data object, and each row represents one object, the following should work:
dataGridView1.SelectedCells.Select(c => c.Item).Distinct().Count()
This will return the number of items the different cells are bound to. Since each item has one row that binds to it, it will return the count of every row with at least one selected cell.

CodedUI: Why is searching for a cell so slow?

I've got a grid (FlexGrid, from ComponentOne) in a Winform application and I'm trying to find a cell in that grid, given the cell's column index and its value.
I've written the extension method below to loop through the grid and find that cell.
I'm testing that method on a grid that has 6 columns and 64 rows. It took 10 minutes for my code to find the correct cell (which was on the last row)
Is there any way I can speed up my algorithm ?
Note: I've also tried setting PlayBack.PlayBackSetting.SmartMatchOption to TopLevelWindow, but it does not seem to change anything...
Thanks!
public static WinCell FindCellByColumnAndValue(this WinTable table, int colIndex, string strCellValue)
{
int count = table.GetChildren().Count;
for (int rowIndex = 0; rowIndex < count; rowIndex++)
{
WinRow row = new WinRow(table);
WinCell cell = new WinCell(row);
row.SearchProperties.Add(WinRow.PropertyNames.RowIndex, rowIndex.ToString());
cell.SearchProperties.Add(WinCell.PropertyNames.ColumnIndex, colIndex.ToString());
cell.SearchProperties.Add(WinCell.PropertyNames.Value, strCellValue);
if (cell.Exists)
return cell;
}
return new WinCell();
}
Edit
I modified my method to be like below(e.g. I don't use winrow anymore), this seems to be around 3x faster. It still needs 7 sec to find a cell in a table with 3 rows and 6 columns though, so it's still quite slow...
I'll mark this answer as accepted later, to leave time to other people to suggest something better
public static WinCell FindCellByColumnAndValue(this WinTable table, int colIndex, string strCellValue, bool searchHeader = false)
{
Playback.PlaybackSettings.SmartMatchOptions = Microsoft.VisualStudio.TestTools.UITest.Extension.SmartMatchOptions.None;
int count = table.GetChildren().Count;
for (int rowIndex = 0; rowIndex < count; rowIndex++)
{
WinCell cell = new WinCell(table);
cell.SearchProperties.Add(WinRow.PropertyNames.RowIndex, rowIndex.ToString());
cell.SearchProperties.Add(WinCell.PropertyNames.ColumnIndex, colIndex.ToString());
cell.SearchProperties.Add(WinCell.PropertyNames.Value, strCellValue);
if (cell.Exists)
return cell;
}
return new WinCell();
}
Edit #2:
I've tried using FindMatchingControls as per #Andrii's suggestion, and I'm nearly there, except that in the code below the cell's column index (c.ColumnIndex) has the wrong value..
public static WinCell FindCellByColumnAndValue2(this WinTable table, int colIndex, string strCellValue, bool searchHeader = false)
{
WinRow row = new WinRow(table);
//Filter rows containing the wanted value
row.SearchProperties.Add(new PropertyExpression(WinRow.PropertyNames.Value, strCellValue, PropertyExpressionOperator.Contains));
var rows = row.FindMatchingControls();
foreach (var r in rows)
{
WinCell cell = new WinCell(r);
cell.SearchProperties.Add(WinCell.PropertyNames.Value, strCellValue);
//Filter cells with the wanted value in the current row
var controls = cell.FindMatchingControls();
foreach (var ctl in controls)
{
var c = ctl as WinCell;
if (c.ColumnIndex == colIndex)//ERROR: The only cell in my table with the correct value returns a column index of 2, instead of 0 (being in the first cell)
return c;
}
}
return new WinCell();
}
I would suggest to perform direct looping through child controls - according to my experience searching controls with complicated search crieteria in Coded UI often works slow.
Edit:
To improve performance it is better to remove counting table's children and looping through rows. Also to avoid exception when finding control without row number you can use FindMatchingControls method, like in following:
public static WinCell FindCellByColumnAndValue(this WinTable table, int colIndex, string strCellValue, bool searchHeader = false)
{
Playback.PlaybackSettings.SmartMatchOptions = Microsoft.VisualStudio.TestTools.UITest.Extension.SmartMatchOptions.None;
WinCell cell = new WinCell(table);
cell.SearchProperties.Add(WinCell.PropertyNames.ColumnIndex, colIndex.ToString());
cell.SearchProperties.Add(WinCell.PropertyNames.Value, strCellValue);
UITestControlCollection foundControls = cell.FindMatchingControls();
if (foundControls.Count > 0)
{
cell = foundControls.List[0];
}
else
{
cell = null;
}
return cell;
}
When table field will be searched directly in the table it will save time for counting child controls in table. Also searching without for loop will save time for search of field for each iteration of row number that does not match.
As row number is iterated through all available values in your extension - it is not essential search criterion in the long run. In same time each iteration through a row number value invokes additional control search request - eventually multiplying method's execution time by the number of rows in grid.

In Telerik Radgrid, how can I page the results according the number of group headers?

Is there a way of set some "PageSize" property according the number of "Group Headers" in a RadGrid?
Regards!
The code snippet is bellow:
protected void PageResults(DataTable AnyDataTable) {
//Textbox where user inserts the number of registers that will be showed per page.
if (txt_register_per_page.Value.HasValue)
{
int RegistersPerPage = 0, EveryItens = 0;
string OldData = "";
//The loop runs over all the table's rows.
for (int Index = 0; Index <= AnyDataTable.Rows.Count; Index++)
{
//The "ColumName" is the one that all the others will be grouped.
//If no matches with the current data, means that is another "group".
if (!(String.Equals(AnyDataTable.Rows[Index]["ColumnName"].ToString(), OldData)))
{
RegistersPerPage++;
if (RegistersPerPage == txt_register_per_page.Value)
{
EveryItens = Index;
break;
}
OldData = AnyDataTable.Rows[Index]["ColumnName"].ToString();
}
}
MyRadGrid.PageSize = EveryItens;
}
}
As I see, the PageSize property allows the grid to show pages based in ALL the registers, then I tried to begin writing something that converts the total data for the respective number of groups that the user inputs on the textbox.
There is a pagesize property, but it doesn't take into affect the row type to do some specialized function. You'd have to examine the data (and do the grouping yourself) and manually calculate the groups... I don't know if that is an efficient solution.
What type of grouping calculation do you want to do?

Categories