I miss grouping functionality in VirtualGrid. In my opinion, the following process would be conceivable: 1. user interaction triggers an event - e. g. GroupChanged (similar to FilterChanged). 2. This event determines all available groups and returns them to the VirtualGrid. 3. if the user expand a group > a second event is fired - and returns all matching data rows.
To reproduce: open the VirtualGrid >> First Look example in QSF and follow the steps: 1) I have a virtual grid which has both vertical and horizontal scroll bars. 2) Scroll to the very bottom of the grid 3) Select the last row 4) Scroll all the way to the right At this moment the selected row disappears from the view. While scrolling horizontally the vertical scrollbar jumps several rows above although it seems scrolled to the bottom.
To reproduce: - Open the attached project. - Unselect Column3 from the list. Workaround: this.radVirtualGrid1.MasterViewInfo.CustomColumns.RemoveAt(0); foreach (VirtualGridRowElement rowElement in this.radVirtualGrid1.VirtualGridElement.TableElement.GetDescendants(delegate(RadElement x) { return x is VirtualGridRowElement; }, TreeTraversalMode.BreadthFirst)) { rowElement.CellContainer.Children.Clear(); }
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;
}
}
To reproduce: void radButton1_Click(object sender, EventArgs e) { this.radVirtualGrid1.CurrentCell = null; } Workaround: class MyRadVirtualGrid : RadVirtualGrid { protected override void CreateChildItems(RadElement parent) { base.CreateChildItems(parent); parent.Children.RemoveAt(0); var field = typeof(RadVirtualGrid).GetField("virtualGridElement", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); var element = new MyVirtualGridElement(); field.SetValue(this,element); parent.Children.Add(element); } } class MyVirtualGridElement : RadVirtualGridElement { protected override Type ThemeEffectiveType { get { return typeof(RadVirtualGridElement); } } protected override bool SetCurrentCell(VirtualGridCellInfo value) { if (value == null) { if (this.IsInEditMode && !this.EndEdit()) { return false; } int row = int.MinValue; VirtualGridViewInfo viewInfo = null; if (this.CurrentCell != null) { row = this.CurrentCell.RowIndex; viewInfo = this.CurrentCell.ViewInfo; } if (this.CurrentCell != null && (value == null || this.CurrentCell.ViewInfo != value.ViewInfo || this.CurrentCell.RowIndex != value.RowIndex)) { VirtualGridRowValidatingEventArgs e = new VirtualGridRowValidatingEventArgs(row, viewInfo); this.OnRowValidating(e); if (e.Cancel) { return false; } } var currentCell = typeof(RadVirtualGridElement).GetField("currentCell", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); currentCell.SetValue(this, value); if (this.CurrentCell != null) { this.EnsureCellVisible(this.CurrentCell.RowIndex, this.CurrentCell.ColumnIndex, this.CurrentCell.ViewInfo); } VirtualGridTableElement tableElement = null; if (this.CurrentCell != null) { tableElement = this.GetTableElement(this.CurrentCell.ViewInfo); } else { tableElement = this.TableElement; } if (tableElement != null) { VirtualGridRowElement rowElement = tableElement.ViewElement.GetRowElement(row); if (rowElement != null) { rowElement.Synchronize(false); } } if (value != null) { tableElement = this.GetTableElement(value.ViewInfo); if (tableElement != null) { VirtualGridRowElement rowElement = tableElement.ViewElement.GetRowElement(value.RowIndex); if (rowElement != null) { rowElement.Synchronize(false); } } } if (viewInfo != null) { VirtualGridRowEventArgs args = new VirtualGridRowEventArgs(row, viewInfo); this.OnRowValidated(args); } this.OnCurrentCellChanged(EventArgs.Empty); return true; } return base.SetCurrentCell(value); } }
To reproduce: - Remove the default padding: private void RadVirtualGrid1_RowExpanding(object sender, VirtualGridRowExpandingEventArgs e) { e.ChildViewInfo.Padding =new Padding(0,0,0,0); } Still some space remains, this can be seen in the demo application as well.
Scenario to cover: the user tries to filter the grid not with each entered symbol but when the value in the filter cell is committed (Enter key is pressed) and the editor is closed. The CellValuePushed event is fired only for data cells and the FilterChanged event is fired with each keystroke but not when the editor is closed. A similar scenario is valid for the new row. There is no suitable event for capturing when a certain cell is updated in the new row as well.
The attached gif file illustrates how to reproduce the problem with the Demo application.
Please run the attached sample project and navigate with the left/right arrows as it is demonstrated in the gif file.
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.
;-)
To reproduce: public RadForm1() { InitializeComponent(); radVirtualGrid1.CellValueNeeded += RadVirtualGrid1_CellValueNeeded; radVirtualGrid1.ColumnCount = 20; radVirtualGrid1.RowCount = 1000; radVirtualGrid1.AutoSizeColumnsMode = Telerik.WinControls.UI.VirtualGridAutoSizeColumnsMode.Fill; } private void RadVirtualGrid1_CellValueNeeded(object sender, Telerik.WinControls.UI.VirtualGridCellValueNeededEventArgs e) { e.Value = string.Format("Col {0} Row{1}", e.ColumnIndex, e.RowIndex); } - Press and hold the down key, you will notice that the selection freezes after some time and is updated after the key is released.
To reproduce: public Form1() { InitializeComponent(); this.radVirtualGrid1.ColumnCount = 5; this.radVirtualGrid1.RowCount = 20; this.radVirtualGrid1.CellValueNeeded += radVirtualGrid1_CellValueNeeded; } private void radVirtualGrid1_CellValueNeeded(object sender, Telerik.WinControls.UI.VirtualGridCellValueNeededEventArgs e) { e.Value = "Data" + e.RowIndex + "." + e.ColumnIndex; } Workaround: use a custom ScrollableVirtualCellsContainer: public Form1() { InitializeComponent(); this.radVirtualGrid1.CreateRowElement += radVirtualGrid1_CreateRowElement; this.radVirtualGrid1.ColumnCount = 5; this.radVirtualGrid1.RowCount = 20; this.radVirtualGrid1.CellValueNeeded += radVirtualGrid1_CellValueNeeded; } private void radVirtualGrid1_CreateRowElement(object sender, VirtualGridCreateRowEventArgs e) { e.RowElement = new CustomVirtualGridRowElement(); } private void radVirtualGrid1_CellValueNeeded(object sender, Telerik.WinControls.UI.VirtualGridCellValueNeededEventArgs e) { e.Value = "Data" + e.RowIndex + "." + e.ColumnIndex; } public class CustomVirtualGridRowElement : VirtualGridRowElement { public CustomVirtualGridRowElement() : base() { FieldInfo fi = typeof(VirtualGridRowElement).GetField("scrollableColumns", BindingFlags.NonPublic | BindingFlags.Instance); ScrollableVirtualCellsContainer scrollableColumns = fi.GetValue(this) as ScrollableVirtualCellsContainer; CustomScrollableVirtualCellsContainer newScrollableColumns = new CustomScrollableVirtualCellsContainer(); FieldInfo fi2 = typeof(ScrollableVirtualCellsContainer).GetField("rowElement", BindingFlags.NonPublic | BindingFlags.Instance); fi2.SetValue(newScrollableColumns, this); this.Children.Remove(scrollableColumns); this.Children.Insert(1, newScrollableColumns); } } public class CustomScrollableVirtualCellsContainer : ScrollableVirtualCellsContainer { protected override SizeF MeasureElementCore(RadElement element, SizeF availableSize) { int scrollOffset = 0; if (this.RowElement.TableElement.ColumnScroller.ScrollMode == ItemScrollerScrollModes.Discrete) { for (int i = 0; i < this.RowElement.TableElement.ColumnScroller.Scrollbar.Value; i++) { scrollOffset += this.RowElement.TableElement.ColumnScroller.GetScrollHeight( this.RowElement.TableElement.ViewInfo.ColumnsViewState.GetItemSize(i)); } } else { scrollOffset = this.RowElement.TableElement.HScrollBar.Value; } VirtualGridCellElement cellElement = element as VirtualGridCellElement; int itemOffset = this.RowElement.ViewInfo.ColumnsViewState.GetItemOffset(cellElement.Data); int itemWidth = this.RowElement.ViewInfo.ColumnsViewState.GetItemSize(cellElement.Data); element.Measure(availableSize); if (itemOffset + itemWidth > scrollOffset && itemOffset < scrollOffset) { return new SizeF((itemOffset + itemWidth) - scrollOffset, element.DesiredSize.Height); } return element.DesiredSize; } }
To reproduce: private void RadVirtualGrid1_EditorRequired(object sender, Telerik.WinControls.UI.VirtualGridEditorRequiredEventArgs e) { switch (e.ColumnIndex) { case 1: { VirtualGridTextBoxControlEditor editor = new VirtualGridTextBoxControlEditor(); editor.CharacterCasing = CharacterCasing.Upper; e.Editor = editor; } break; } } - Open the editor and try using the backspace or the arrow keys. Workaround: class MyGrid : RadVirtualGrid { protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (this.ActiveEditor is VirtualGridTextBoxControlEditor) { var editor = this.ActiveEditor as VirtualGridTextBoxControlEditor; var element = editor.EditorElement as RadTextBoxControlElement; element.InputHandler.ProcessKeyDown(e); } } protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); if (this.ActiveEditor is VirtualGridTextBoxControlEditor) { var editor = this.ActiveEditor as VirtualGridTextBoxControlEditor; var element = editor.EditorElement as RadTextBoxControlElement; element.InputHandler.ProcessKeyUp(e); } } }
To reproduce: use the following code snippet and try to select another cell. public RadForm1() { InitializeComponent(); this.radVirtualGrid1.RowCount = 30; this.radVirtualGrid1.ColumnCount = 5; this.radVirtualGrid1.CellValueNeeded += radVirtualGrid1_CellValueNeeded; this.radVirtualGrid1.CurrentCellChanging += radVirtualGrid1_CurrentCellChanging; } private void radVirtualGrid1_CurrentCellChanging(object sender, Telerik.WinControls.UI.VirtualGridCellInfoCancelEventArgs e) { e.Cancel = true; } private void radVirtualGrid1_CellValueNeeded(object sender, Telerik.WinControls.UI.VirtualGridCellValueNeededEventArgs e) { e.Value = e.RowIndex + "." + e.ColumnIndex; } Workaround: public class CustomVirtualGrid : RadVirtualGrid { public override string ThemeClassName { get { return typeof(RadVirtualGrid).FullName; } } protected override void OnMouseDown(MouseEventArgs e) { if (e.Button == System.Windows.Forms.MouseButtons.Left) { return; } base.OnMouseDown(e); } }
To reproduce: use the following code snippet and follow the steps: public RadForm1() { InitializeComponent(); this.radVirtualGrid1.RowCount = 10; this.radVirtualGrid1.ColumnCount = 5; this.radVirtualGrid1.CellValueNeeded+=radVirtualGrid1_CellValueNeeded; } private void radVirtualGrid1_CellValueNeeded(object sender, Telerik.WinControls.UI.VirtualGridCellValueNeededEventArgs e) { e.Value = e.RowIndex + "." + e.ColumnIndex; } 1. Pin (left or right) 1st column 2. Scroll horizontally (to not display 2nd column) 3. Unpin 1st column 4. Scroll horizontally to display 1st column => 1st column is hidden, the only way to make it visible is to resize a column or the entire window Workaround: private void radVirtualGrid1_ContextMenuOpening(object sender, Telerik.WinControls.UI.VirtualGridContextMenuOpeningEventArgs e) { foreach (RadItem item in e.ContextMenu.Items) { if (item.Text == RadVirtualGridLocalizationProvider.CurrentProvider.GetLocalizedString(RadVirtualGridStringId.PinMenuItem)) { foreach (RadMenuItem subItem in ((RadMenuItem)item).Items) { if (subItem.Text == RadVirtualGridLocalizationProvider.CurrentProvider.GetLocalizedString(RadVirtualGridStringId.UnpinColumnMenuItem)) { subItem.Click-=subItem_Click; subItem.Click+=subItem_Click; } } } } } private void subItem_Click(object sender, EventArgs e) { this.radVirtualGrid1.MasterViewInfo.SetColumnWidth(0, this.radVirtualGrid1.MasterViewInfo.GetColumnWidth(0)); }
To reproduce: - Change the editor to VirtualGridDropDownListEditor - Press alpha-numeric key when the grid is not in edit mode. Workaround: radVirtualGrid1.VirtualGridElement.InputBehavior = new MyBehavior(radVirtualGrid1.VirtualGridElement); class MyBehavior : VirtualGridInputBehavior { public MyBehavior(RadVirtualGridElement element) : base(element) { } protected override bool HandleAlphaNumericKey(KeyPressEventArgs keys) { if (!this.GridElement.IsInEditMode && (this.GridElement.BeginEditMode == RadVirtualGridBeginEditMode.BeginEditOnKeystroke || this.GridElement.BeginEditMode == RadVirtualGridBeginEditMode.BeginEditOnKeystrokeOrF2)) { //this.GridElement.HideContextMenu(); this.GridElement.BeginEdit(); if (this.GridElement.ActiveEditor is VirtualGridDropDownListEditor) { string symbol = keys.KeyChar.ToString(); var editor = this.GridElement.ActiveEditor as VirtualGridDropDownListEditor; var element = editor.EditorElement as RadDropDownListEditorElement; if ((element.AutoCompleteMode & AutoCompleteMode.Append) == AutoCompleteMode.Append) { int index = element.AutoCompleteAppend.FindShortestString(symbol); if (index == -1) { element.EditableElementText = symbol; element.EditableElement.SelectionStart = 1; element.EditableElement.SelectionLength = 0; return true; } } } return false; } return base.HandleAlphaNumericKey(keys); } }
Workaround: this.radVirtualGrid1.RowCount += 100; this.radVirtualGrid1.TableElement.PagingPanelElement.UpdateView();