GetColumnDisplayRectangle no value if no rows - c#

In winforms I need to retrieve the position of each column in my DataGridView, to do this I use this function:
Rectangle rec = dgv.GetColumnDisplayRectangle(getColumnIndexByName(entry.Key), true);
and give me the rec of the column, but if I have a table with no records, the function get a rec with 0 value...
How can I solve this??
here is what I have if the grid has rows:
in the blue panel I've got some textbox that I use as filter of the value of the column below
Now if the grid has some rows, it works perfectly, I get right the column rectangle, I obtain the left and width and I'm able to place the filters above the header.. but if I set a wrong filter and the query retrieve 0 records, I don't know where place the textboxes
Edit: the code of getColumnIndexByName:
protected int getColumnIndexByName(string name)
{
foreach (DataGridViewColumn col in dgv.Columns)
{
if (col.Name.ToLower().Trim() == name.ToLower().Trim()) return col.Index;
}
return -1;
}
thanks

GetColumnDisplayRectangle returns Rectangle.Empty if the column is not Displayed.
You may also want to take a look at source code for GetColumnDisplayRectanglePrivate method which is used by DataGridView to calculate rectangle of the given column.
The method contains following code:
if (!this.Columns[columnIndex].Displayed)
{
return Rectangle.Empty;
}
Workaround
If you are looking for a workaround, you can use the following code which gets a list of rectangles without any flicker:
this.SuspendLayout();
var width = this.dataGridView1.Width;
this.dataGridView1.Width = 8388607;
var rectangles = this.dataGridView1.Columns.Cast<DataGridViewColumn>()
.Select(x => dataGridView1.GetColumnDisplayRectangle(x.Index, false))
.ToList();
this.dataGridView1.Width = width;
this.ResumeLayout();

Related

Is there a way to check if a grid cell, NOT datagrid cell, contains an object

I'm assigning textboxes to cells in a grid but will like to confirm if an object already exists in the cell before assigning. Is it possible to query a row at a specific column that returns null if empty?
I could create a list of lists representing the grid which I modify as i add and remove objects but this sounds to be inefficient.
A sample code I've written:
TextBlock _text = new TextBlock()
{
Text = _cont,
Background = new SolidColorBrush(_colo.disciplinecolor)
}; TextBlockStyle(_text);
int index = SearchDate((DateTime)_dt);
Grid.SetRow(_text, 1); Grid.SetColumn(_text, index);
Maindispgrid.Children.Add(_text);
Essentially this code block is called every time the user clicks a button with the TextBlock added to a dated column(pre-selected by the user), and hopefully, the next available row in the column . I've tried GetRow() but this searched by UIElement which didn't seem to work as all TextBlock are created with the same name.
I might have approached this all wrong so any leads as to what I need to read up on will be much appreciated.
Basically the end result should hopefully work as this:
TextBlock _text = new TextBlock()
{
Text = _cont,
Background = new SolidColorBrush(_colo.disciplinecolor)
}; TextBlockStyle(_text);
int index = SearchDate((DateTime)_dt);
//check for next available row at specific column index
Grid.SetRow(_text, nextAvailableRow); Grid.SetColumn(_text, index);
Maindispgrid.Children.Add(_text);
You can iterate over the children list via Maindispgrid.Children. For each child, retrieve the assigned Row and Column value (if assigned at all). Use this information to calculate the available row.
IList<int[]> indices = new List<int[]>();
foreach (var child in Maindispgrid.Children)
{
int rowIndex = Grid.GetRow(child);
int columnIndex = Grid.GetColumn(child);
indices.Add(new int[] {rowIndex, columnIndex});
}
// work with the "indices" list

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
{
}
}

Total cell count of a DataGridView

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);

How to replace user control in cell into the Grid. WPF?

I have a Grid with 5 rows and 5 columns. I creating this Grid dynamically. Every cell contains custom user control. I would like to replace the user control dynamically with other user control at given row and column. You can see the method implementation which creates the Grid here.
My question is how to replace the user control at given row and column after the Grid is already created? Sorry if my English is bad!
Thank you in advance!
This function will probably fit your needs
public void ReplaceItemAt(Grid grid, FrameworkElement fe, int row, int col)
{
// clear desired cell
var items = grid.Children
.Where(x => Grid.GetRow(x) == row && Grid.GetColumn(x) == col)
.ToArray();
foreach(var item in items) grid.Children.Remove(item);
// make sure the new item is positioned correctly
Grid.SetRow(fe, row);
Grid.SetColumn(fe, col);
// insert the new item into the grid
grid.Children.Add(fe);
}

How can I fill a listview in vertical form?

I have one listview whith several columns.
I want to fill this listview in a vertical form
column to column.
Sorry, but this is not (easily) possible.
A ListView has a list of ListViewItems, where each one has a List of ListViewSubItems (and to make it a little more complex at the first spot, the first ListViewSubItem is the same as the ListViewItem itself).
So if you like to fill up a ListView column by column you first have to add the ListViewItems to the ListView for all the values you want in the first column.
Afterwards you iterate through the ListView.Items and call on every ListViewItem.Subitems.Add to fill up the next column. This must be done for each column you like to fill.
If you like to fill in the column values in another order then from left to right, you should take a look into the DisplayIndex of the ColumnHeader within the ListView.Columns.
Some example code:
// Some values
var someValues = Enumerable.Range(1, 10);
// Fill up the first column
foreach (var item in someValues)
{
listView.Items.Add("0." + item);
}
// Run for each column in the listView (the first is already filled up)
foreach (ColumnHeader column in listView.Columns.Cast<ColumnHeader>().Skip(1))
{
// Get the value and the index for which row the value should be
foreach (var item in someValues.Select((Value, Index) => new { Value, Index }))
{
// Add the value to the given row, thous leading to be added as new column
listView.Items[item.Index].SubItems.Add(column.Index + "." + item.Value);
}
}

Categories