In this case, we filter a column by typing in a filter cell. If we have a horizontal scroll move the filter cell out of the view and then go back to it and try to change the value of the filter cell, the editor has an empty string.
This behavior is observed when the VisualStudio2022Light theme is applied. For example, this is not observed in the Fluent theme.
The problem also occurs, if you just start the form. For example, on 175% (without drag).
Elements in the child grid that are outside of the parent bounds are not active. This means that you can't resize the column, edit, filter, sort or use any other build-in behavior.
The horizontal scrollbar should not appear on top of the data row.
Currently, this behavior is available for RadGridView and the parent row becomes current/selected after expanding. Please refer to the attached gif file for RadGridView and the missing functionality for RadVirtualGrid.
When the control is populated with hierarchy data, the current arrow icon and the expander icon overlap each other. This is observed when you click on a row.
As a workaround, we can increase the size of the indent column to leave enough space for both icons.
this.radVirtualGrid1.TableElement.IndentColumnWidth = 40;
Then we can move the current row arrow icon to the left (or right, depending on your requirement) in the CellFormatting event handler of the control.
private void radVirtualGrid1_CellFormatting(object sender, VirtualGridCellElementEventArgs e)
{
if (e.CellElement is VirtualGridIndentCellElement)
{
var indentCell = e.CellElement as VirtualGridIndentCellElement;
indentCell.ImageLayout = ImageLayout.None;
indentCell.ImageAlignment = ContentAlignment.MiddleLeft;
}
}
Right now, when a VirtualGridDateTimeEditor is displayed in a RadVirtualGrid, and the user chooses to enter the date (and not use the date time picker), then the user has to enter the date inclusing the separators.
So a Dutch date has format: "dd-MM-yyyy", and the user has to enter "31-12-2023", including the dashes.
Hereby my request for a new feature: Entering dates without the separators / dashes, thus: "3112023". So at the moment the user enters two digits, move to the next segment, etc. The request goes for the time segments.
This would be a performance gain while doing a lot of data entry.
Repro steps:
Observed behavior:
Expected behavior:
Observation / suggestion:
Repro-steps
Observed behavior
First, lets compare this with when you right click on a record selector (the cell before a row starts, sometimes with an arrow). In that case only that row gets selected, so you visually see what the context menu is about. When you right click on a cell, the selection does not change so the context remains unclear. In my printscreen above it is hard to see on which cell I clicked.
Second, the actions in the context menu target multiple targets or contexts. Copy and Paste target the whole selection. Clear and Edit target the cell, Delete Row targets the row which I clicked on, but which is not visible.
In this case, the user actually wanted to right click and delete ALL rows, which is not part of the context menu. Instead he clicked on Delete Row and he was unaware of what he had actually deleted.
Expected bevahior
I was trying to move the NewRow to the bottom of the RadVirtualGrid. This is also supported by its big brother RadGridView. I have now learned that this is not supported by RadVirtualGrid.
Hereby my request: Please support this!
I've created a Harmony patch (which works for me); it might help you if you decide to support it:
static class MoveNewRowToBottom
{
[HarmonyPatch(typeof(VirtualGridTableElement), "InvalidatePinnedRows")]
static class VirtualGridTableElement_InvalidatePinnedRows
{
static private bool _isInvalidating;
static private bool Prefix(VirtualGridTableElement __instance)
{
if (_isInvalidating)
return false;
_isInvalidating = true;
__instance.ViewElement.TopPinnedRows.DisposeChildren();
__instance.ViewElement.BottomPinnedRows.DisposeChildren();
var viewInfo = __instance.ViewInfo;
var rowsViewState = __instance.RowsViewState;
var topPinnedItems = rowsViewState.TopPinnedItems;
var bottomPinnedItems = rowsViewState.BottomPinnedItems;
if (viewInfo.ShowFilterRow)
if (!topPinnedItems.Contains(RadVirtualGrid.FilterRowIndex) && !bottomPinnedItems.Contains(RadVirtualGrid.FilterRowIndex))
rowsViewState.SetPinPosition(RadVirtualGrid.FilterRowIndex, PinnedRowPosition.Top);
if (viewInfo.ShowNewRow)
if (!topPinnedItems.Contains(RadVirtualGrid.NewRowIndex) && !bottomPinnedItems.Contains(RadVirtualGrid.NewRowIndex))
rowsViewState.SetPinPosition(RadVirtualGrid.NewRowIndex, PinnedRowPosition.Top);
if (viewInfo.ShowHeaderRow)
if (!topPinnedItems.Contains(RadVirtualGrid.HeaderRowIndex) && !bottomPinnedItems.Contains(RadVirtualGrid.HeaderRowIndex))
rowsViewState.SetPinPosition(RadVirtualGrid.HeaderRowIndex, PinnedRowPosition.Top);
var elementProvider = __instance.RowScroller.ElementProvider;
var topPinnedRows = __instance.ViewElement.TopPinnedRows;
foreach (int topPinnedRow in topPinnedItems.SortTopRowIndexes())
{
VirtualGridRowElement pinnedRow = (VirtualGridRowElement)elementProvider.GetElement(topPinnedRow, null);
topPinnedRows.Children.Add(pinnedRow);
pinnedRow.Attach(topPinnedRow, null);
}
var bottomPinnedRows = __instance.ViewElement.BottomPinnedRows;
foreach (int bottomPinnedRow in bottomPinnedItems.SortBottomRowIndexes())
{
VirtualGridRowElement pinnedRow = (VirtualGridRowElement)elementProvider.GetElement(bottomPinnedRow, null);
bottomPinnedRows.Children.Add(pinnedRow);
pinnedRow.Attach(bottomPinnedRow, null);
}
_isInvalidating = false;
return false;
}
}
static private IEnumerable<int> SortTopRowIndexes(this ReadOnlyCollection<int> topRowIndexes)
{
var specialRowIndex = topRowIndexes.Where(index => index < 0).ToList();
if (specialRowIndex.Contains(RadVirtualGrid.HeaderRowIndex))
yield return RadVirtualGrid.HeaderRowIndex;
if (specialRowIndex.Contains(RadVirtualGrid.NewRowIndex))
yield return RadVirtualGrid.NewRowIndex;
if (specialRowIndex.Contains(RadVirtualGrid.FilterRowIndex))
yield return RadVirtualGrid.FilterRowIndex;
foreach (var index in topRowIndexes.Where(index => index >= 0))
yield return index;
}
static private IEnumerable<int> SortBottomRowIndexes(this ReadOnlyCollection<int> bottomRowIndexes)
{
foreach (var index in bottomRowIndexes.Where(index => index >= 0))
yield return index;
var specialRowIndex = bottomRowIndexes.Where(index => index < 0).ToList();
if (specialRowIndex.Contains(RadVirtualGrid.FilterRowIndex))
yield return RadVirtualGrid.FilterRowIndex;
if (specialRowIndex.Contains(RadVirtualGrid.NewRowIndex))
yield return RadVirtualGrid.NewRowIndex;
if (specialRowIndex.Contains(RadVirtualGrid.HeaderRowIndex))
yield return RadVirtualGrid.HeaderRowIndex;
}
}
I was trying to move the NewRow to the bottom of the RadVirtualGrid. I learned that this is not possible. During my attempts I ran into a bug:
Reproduction steps:
Expected behavior:
Observed behavior:
Repro-steps:
Expected behavior:
Observed behavior:
Please observe that Grid.UserAddedRow is not handled, but since we cannot even see the AddNewRow, it is not required for this bug.
public partial class TestForm: Form
{
private readonly DataView _view;
public TestForm()
{
InitializeComponent();
_view = new DataView(LoadTable(true));
_grid.ColumnCount = _view.Table.Columns.Count;
_grid.RowCount = _view.Count;
_grid.AllowAddNewRow = true;
_grid.SelectionMode = VirtualGridSelectionMode.FullRowSelect;
}
static private DataTable LoadTable(bool fill)
{
var table = new DataTable();
table.Columns.Add("Number", typeof(int));
if (fill)
for(int i = 0; i < 20; i++)
table.Rows.Add(i);
return table;
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this._grid = new Telerik.WinControls.UI.RadVirtualGrid();
((System.ComponentModel.ISupportInitialize)(this._grid)).BeginInit();
this.SuspendLayout();
//
// _grid
//
this._grid.Dock = System.Windows.Forms.DockStyle.Fill;
this._grid.Location = new System.Drawing.Point(0, 0);
this._grid.MultiSelect = true;
this._grid.Name = "_grid";
this._grid.SelectionMode = Telerik.WinControls.UI.VirtualGridSelectionMode.FullRowSelect;
this._grid.Size = new System.Drawing.Size(800, 450);
this._grid.TabIndex = 0;
this._grid.CellValueNeeded += new Telerik.WinControls.UI.VirtualGridCellValueNeededEventHandler(this._grid_CellValueNeeded);
this._grid.UserDeletedRow += new Telerik.WinControls.UI.VirtualGridRowsEventHandler(this._grid_UserDeletedRow);
//
// TestForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this._grid);
this.Name = "TestForm";
this.Text = "TestForm";
((System.ComponentModel.ISupportInitialize)(this._grid)).EndInit();
this.ResumeLayout(false);
}
#endregion
private Telerik.WinControls.UI.RadVirtualGrid _grid;
private void _grid_CellValueNeeded(object sender, Telerik.WinControls.UI.VirtualGridCellValueNeededEventArgs e)
{
if (e.ColumnIndex < 0)
return;
if (e.RowIndex < 0)
{
e.FieldName = _view.Table.Columns[e.ColumnIndex].ColumnName;
if (e.RowIndex == RadVirtualGrid.HeaderRowIndex)
e.Value = e.FieldName;
}
else if (e.RowIndex < _view.Count)
{
e.Value = _view[e.RowIndex][e.ColumnIndex];
}
}
private void _grid_UserDeletedRow(object sender, Telerik.WinControls.UI.VirtualGridRowsEventArgs e)
{
var rowIndexes = e.RowIndices.Distinct().OrderByDescending(i => i).ToList(); // Off topic: I have seen duplicate row indexes in this row collection. And unsorted. Is that by design?
if (rowIndexes.Count == _view.Count)
{
_view.Table.Rows.Clear();
}
else
{
foreach(var index in rowIndexes)
_view[index].Delete();
}
_grid.RowCount = _view.Count;
}
}
RadVirtualGrid does not support (nullable) booleans out of the box.
There are several pages to work around this issues, like:
In my humble opinion this should be supported by default. Why?
In my mind I think of how a meeting would have been while designing the RadVirtualGrid.
;-)