When the DataType of the column is not a floating point number, decimals should not be displayed. This should affect both the excel like filtering and the regular filtering
ADD. RadGridView should support binding to IQuerable
ADD. RadGridView - add kitetic scrolling support
Steps to reproduce: 1. Drag a RadGridView to a form 2. Add a text column and a hyperlink column 3. Add a row with null value for the hyperlink cell 4. Set some text for the hyperlink cell 5. Subscribe to the HyperlinkOpening/ed events 6. Run the project and click on the hyperlink, you will see that none of the events will be fired. Workaround: To work around it, you can create your own cell element which derives from the GridHyperlinkCellElement and override the ProcessHyperlink method to handle the user clicking on a hyperlink: public class MyHyperlinkCellElement : GridHyperlinkCellElement { public MyHyperlinkCellElement(GridViewColumn col, GridRowElement row) : base(col, row) { } protected override void ProcessHyperlink() { //handle hyperlink click } } After you have created your cell element, you can replace the original using the CreateCell event of the RadGridView: private void radGridView1_CreateCell(object sender, GridViewCreateCellEventArgs e) { if (e.Column is GridViewHyperlinkColumn) { e.CellType = typeof(MyHyperlinkCellElement); } }
The following HTMLViewDefinition has a wrong layout when run: HtmlViewDefinition view = new HtmlViewDefinition(); view.RowTemplate.Rows.Add(new RowDefinition()); view.RowTemplate.Rows.Add(new RowDefinition()); view.RowTemplate.Rows.Add(new RowDefinition()); view.RowTemplate.Rows[0].Cells.Add(new CellDefinition("column0", 0, 1, 1)); view.RowTemplate.Rows[0].Cells.Add(new CellDefinition("column1", 0, 1, 3)); view.RowTemplate.Rows[0].Cells.Add(new CellDefinition("column2", 0, 1, 1)); view.RowTemplate.Rows[1].Cells.Add(new CellDefinition("column3", 0, 1, 2)); view.RowTemplate.Rows[1].Cells.Add(new CellDefinition("column4", 0, 1, 1)); view.RowTemplate.Rows[2].Cells.Add(new CellDefinition("column5", 0, 1, 1)); The layout puts column5 over column1. A possible workaround would be to refresh the rows in the grid's SizeChanged event handler: private void RadGridView1_SizeChanged(object sender, EventArgs e) { radGridView1.TableElement.ViewElement.UpdateRows(true); }
Steps to reproduce: 1. expand a parent row 2. remove the first child row (e.g. 'Jon Smith') by selecting the child row and press the button at the top 3. remove the second (last) child row (in this case 'Pete van Dijk') the same way Here is the code snippet: public partial class Form1 : Form { private List<Department> _departments; public Form1() { InitializeComponent(); SetGridParentColumns(); SetGridChildTemplate(); InitData(); grid.RowSourceNeeded += grid_RowSourceNeeded; } private void InitData() { _departments = new List<Department>(2); Department dep1 = new Department() { DepartmentId = 1, Name = "Accounting" }; Department dep2 = new Department() { DepartmentId = 2, Name = "Finance" }; Employee emp1 = new Employee() { DepartmentId = 1, EmployeeId = 1, FirsName = "John", LastName = "Smith" }; Employee emp2 = new Employee() { DepartmentId = 1, EmployeeId = 2, FirsName = "Pete", LastName = "van Dijk" }; Employee emp3 = new Employee() { DepartmentId = 2, EmployeeId = 3, FirsName = "Mark", LastName = "Smith" }; Employee emp4 = new Employee() { DepartmentId = 2, EmployeeId = 4, FirsName = "Jan", LastName = "Janssen" }; dep1.Employees = new List<Employee>() { emp1, emp2 }; dep2.Employees = new List<Employee>() { emp3, emp4 }; _departments.Add(dep1); _departments.Add(dep2); grid.DataSource = _departments; grid.AllowAddNewRow = false; grid.BestFitColumns(); } private void SetGridParentColumns() { grid.AutoGenerateColumns = false; grid.Columns.Add("DepartmentId", "DepartmentId", "DepartmentId"); grid.Columns.Add("Name", "Name", "Name"); } private void SetGridChildTemplate() { grid.Templates.Clear(); GridViewTemplate childTemplate = new GridViewTemplate(); childTemplate.Columns.Add("EmployeeId"); childTemplate.Columns.Add("DepartmentId"); childTemplate.Columns.Add("FirstName"); childTemplate.Columns.Add("LastName"); grid.Templates.Add(childTemplate); childTemplate.HierarchyDataProvider = new GridViewEventDataProvider(childTemplate); } private void grid_RowSourceNeeded(object sender, GridViewRowSourceNeededEventArgs e) { Department dep = e.ParentRow.DataBoundItem as Department; foreach (Employee employee in dep.Employees) { GridViewRowInfo gridRow = e.Template.Rows.NewRow(); gridRow.Cells["EmployeeId"].Value = employee.EmployeeId; gridRow.Cells["DepartmentId"].Value = employee.DepartmentId; gridRow.Cells["FirstName"].Value = employee.FirsName; gridRow.Cells["LastName"].Value = employee.LastName; e.SourceCollection.Add(gridRow); } e.Template.AllowAddNewRow = false; e.Template.BestFitColumns(); } private void btnRemoveChildRow_Click(object sender, EventArgs e) { GridViewRowInfo row = grid.SelectedRows[0]; if (row.ViewTemplate.Parent == null) return; //department (parent) row, so don't remove it int employeeId = Convert.ToInt32(row.Cells["EmployeeId"].Value); int departmentId = Convert.ToInt32(row.Cells["DepartmentId"].Value); Department dep = GetDepartment(departmentId); dep.RemoveEmployee(employeeId); row.ViewTemplate.Refresh(); } private Department GetDepartment(int departmentId) { foreach (Department dep in _departments) if (dep.DepartmentId == departmentId) return dep; return null; } } public class Department { public int DepartmentId { get; set; } public string Name { get; set; } public List<Employee> Employees { get; set; } public void RemoveEmployee(int employeeId) { Employee empToRemove = null; foreach (Employee emp in Employees) { if (emp.EmployeeId == employeeId) { empToRemove = emp; break; } } if (empToRemove != null) Employees.Remove(empToRemove); } } public class Employee { public int DepartmentId { get; set; } public int EmployeeId { get; set; } public string FirsName { get; set; } public string LastName { get; set; } } Workaround: use the ViewInfo.Refresh method instead of the ViewTemplate.Refresh method
To reproduce: use the code from our demo application for Custom Filtering. Instead of Customers table, bind the grid to Orders or another table with 1000+ rows. Resolution: You can surround the row operation in Begin/EndUpdate(false) and remove the InvalidateRow method. The Custom Filtering example in our demo Application is updated for better performance or you can use the following code snippet: For example: private void radGridView1_CustomFiltering(object sender, Telerik.WinControls.UI.GridViewCustomFilteringEventArgs e) { if (string.IsNullOrEmpty(this.radTextBox1.Text)) { this.radGridView1.BeginUpdate(); e.Visible = true; for (int i = 0; i < this.radGridView1.ColumnCount; i++) { e.Row.Cells[i].Style.Reset(); } //e.Row.InvalidateRow(); this.radGridView1.EndUpdate(false); return; } this.radGridView1.BeginUpdate(); e.Visible = false; for (int i = 0; i < this.radGridView1.ColumnCount; i++) { string text = e.Row.Cells[i].Value.ToString(); if (text.IndexOf(this.radTextBox1.Text, 0, StringComparison.InvariantCultureIgnoreCase) >= 0) { e.Visible = true; e.Row.Cells[i].Style.CustomizeFill = true; e.Row.Cells[i].Style.DrawFill = true; e.Row.Cells[i].Style.BackColor = Color.FromArgb(201, 252, 254); } else { e.Row.Cells[i].Style.Reset(); } } //e.Row.InvalidateRow(); this.radGridView1.EndUpdate(false); }
To reproduce: Sub New() InitializeComponent() Dim dt As New DataTable() For index = 1 To 13 If index = 4 Then dt.Columns.Add("Col" & index, Type.GetType("System.Int32")) Else dt.Columns.Add("Col" & index) End If Next Dim rand As New Random For index = 1 To 9000 Dim newRow As DataRow = dt.NewRow() For Each col As DataColumn In dt.Columns If col.ColumnName = "Col4" Then newRow(col.ColumnName) = rand.Next(2, 1000).ToString() Else newRow(col.ColumnName) = "Data" & index.ToString() & "." & dt.Columns.IndexOf(col).ToString() End If Next dt.Rows.Add(newRow) Next Me.RadGridView1.DataSource = dt Me.RadGridView1.AutoSizeColumnsMode = Telerik.WinControls.UI.GridViewAutoSizeColumnsMode.Fill Me.RadGridView1.Columns.RemoveAt(3) Dim list As New List(Of CustomerMaster) For index = 1 To 1000 list.Add(New CustomerMaster((index + 1), "Name" & (1000 - index + 1).ToString())) Next Dim CustomerMaster_IDColumn As GridViewComboBoxColumn = New GridViewComboBoxColumn With CustomerMaster_IDColumn .Name = "CustomerMaster_ID" .HeaderText = "Customer Master" .DataSource = list .ValueMember = "Id" .DisplayMember = "Name" .FieldName = "Col4" .Width = 200 .DisplayMemberSort = True .DropDownStyle = Telerik.WinControls.RadDropDownStyle.DropDownList End With Me.RadGridView1.Columns.Insert(3, CustomerMaster_IDColumn) End Sub Public Class CustomerMaster Private _id As String Private _name As String Public Sub New(id As String, name As String) Me._id = id Me._name = name End Sub Public Property Id() As String Get Return _id End Get Set(ByVal value As String) _id = value End Set End Property Public Property Name() As String Get Return _name End Get Set(ByVal value As String) _name = value End Set End Property End Class
To reproduce: - Add RadGridView to a blank project and bind it to a blank binding source. - Add GridViewMultiComboBoxColumn to the grid and bind it to another binding source. - In the CellEditorInitialized set the autocomplete mode to Suggest. - When you start the application you will notice that the first items is selected but not displayed and to display it you should select other value first and you will be able to select the first one. Workaround: Set the selected item of the editor to null in the CellEditorInitialized event.
To reproduce: 1.Use the Self-Referencing Hierarchy help article to fill the grid with data. 2.Subscribe to the ChildViewExpanding event and use the following code snippet: private void radGridView1_ChildViewExpanding(object sender, ChildViewExpandingEventArgs e) { if (e.ParentRow.ChildRows != null && e.ParentRow.ChildRows.Count > 0) { e.ParentRow.ChildRows[0].Delete(); } }
To reproduce: Add a RadGridView and use the RowFormatting event for showing rows with errors. Use a timer to change the data. The RowFormatting event fires when the user does not group rows. But after grouping the rows by any column, the grid is not refreshed according to the applied style in RowFormatting event. In order to reproduce the problem, use the following code: private readonly BindingList<DataItem> _items = new BindingList<DataItem>(); private readonly Random _rnd = new Random(); public Form1() { InitializeComponent(); for (var i = 1; i <= 10; i++) { _items.Add(new DataItem("Text1 = " + i, "Type = None", _rnd.Next(0, 2) == 1)); } radGridView1.DataSource = _items; timer1.Start(); } private void radGridView1_RowFormatting(object sender, Telerik.WinControls.UI.RowFormattingEventArgs e) { var item = e.RowElement.Data.DataBoundItem as DataItem; if (item != null && item.IsDataCorrect) { e.RowElement.ResetValue(VisualElement.ForeColorProperty, ValueResetFlags.Local); } else { e.RowElement.ForeColor = Color.Red; } } private void timer1_Tick(object sender, EventArgs e) { foreach (var item in _items) { item.IsDataCorrect = _rnd.Next(0, 2) == 1; } } ic class DataItem : INotifyPropertyChanged private bool _isDataCorrect; private string _text; private string _type; public DataItem(string text1, string type, bool isOk) { Text = text1; Type = type; IsDataCorrect = isOk; } public event PropertyChangedEventHandler PropertyChanged; public bool IsDataCorrect { get { return _isDataCorrect; } set { if (_isDataCorrect != value) { _isDataCorrect = value; OnPropertyChanged("IsDataCorrect"); } } } public string Text { get { return _text; } set { if (_text != value) { _text = value; OnPropertyChanged("Text1"); } } } public string Type { get { return _type; } set { if (_type != value) { _type = value; OnPropertyChanged("Type"); } } } protected virtual void OnPropertyChanged(string propertyName) { var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } }
To reproduce: - Open the hierarchy example in the demo application. - Select Auto-GenratedDataSet and add a new row in the third level template. - Select Manually generated for Bound Mode and then re-select Auto-GenratedDataSet - Try to add new row again. Yo will notice that the new row is not added. Workaround: class MyNewRowBehavior : GridNewRowBehavior { protected override bool ProcessEnterKey(KeyEventArgs keys) { if (this.GridControl.IsInEditMode) { GridViewSynchronizationService.SuspendEvent(this.GridControl.CurrentRow.ViewTemplate, KnownEvents.CurrentChanged); bool result = base.ProcessEnterKey(keys); GridViewSynchronizationService.ResumeEvent(this.GridControl.CurrentRow.ViewTemplate, KnownEvents.CurrentChanged); return result; } else { return base.ProcessEnterKey(keys); } } protected override bool ProcessTabKey(KeyEventArgs keys) { if (this.GridControl.IsInEditMode) { GridViewSynchronizationService.SuspendEvent(this.GridControl.CurrentRow.ViewTemplate, KnownEvents.CurrentChanged); bool result = base.ProcessTabKey(keys); GridViewSynchronizationService.ResumeEvent(this.GridControl.CurrentRow.ViewTemplate, KnownEvents.CurrentChanged); this.Navigator.SelectNextColumn(); return result; } else { return base.ProcessTabKey(keys); } } } The default behavior can be changed as follows: ((BaseGridBehavior)radGridView1.GridBehavior).UnregisterBehavior(typeof(GridViewNewRowInfo)); ((BaseGridBehavior)radGridView1.GridBehavior).RegisterBehavior(typeof(GridViewNewRowInfo), new MyNewRowBehavior());
Workaround - listen for Reset of the rows collection and introduce your modification there: radGridView1.Rows.CollectionChanged += Rows_CollectionChanged; void Rows_CollectionChanged(object sender, Telerik.WinControls.Data.NotifyCollectionChangedEventArgs e) { if (e.Action == Telerik.WinControls.Data.NotifyCollectionChangedAction.Reset) { foreach (GridViewRowInfo row in radGridView1.Rows) { row.Cells[0].Value = false; } } }
This is not considered an issue, it is how RadGridView works. Here are more details: In order for a GridDetailViewCellElement to display a pageview instead of a single table element, either the template of the row holding it has to have more than one child template, or its ShowChildViewCaptions should be true. Once there is a page view, the tabs in it will be visible at all times, except when some of the templates has no rows and AllowAddNewRow for it is false – if it does not have any rows and the user cannot add row, it is considered that there is no need from it. If one needs to change the visibility of the tabs, this can be done in the ViewCellFormatting event: private void RadGridView1_ViewCellFormatting(object sender, CellFormattingEventArgs e) { GridDetailViewCellElement detailCell = e.CellElement as GridDetailViewCellElement; if (detailCell != null) { foreach (RadPageViewItem item in detailCell.PageViewElement.Items) { item.Visibility = Telerik.WinControls.ElementVisibility.Visible; } } }
To reproduce: - Open the examples solution in Visual Studio. - Navigate to hierarchy example. - Enable the excel like filtering for all templates. - Start the application and open the hierarchy first look example. - Add a new row in the second template. - Add such filter that only the newly added row will remain visible. - Expand the new added row and try to add new row to it.
To reproduce: Add a RadGridView and fill it with data. Set the first column's IsVisible property to false. Right click over the row to Copy the row content and paste it in Excel. All data is in a single cell. The Clipboard content does not contain the corresponding <TABLE><TR> tags. Note: if the last columns is hidden, the line-breaks in the copied text are missing. Workaround: create custom RadGridView and replace the wrong content in the Clipboard: public class CustomGrid : RadGridView { protected override RadGridViewElement CreateGridViewElement() { return new CustomRadGridViewElement(); } } public class CustomRadGridViewElement : RadGridViewElement { protected override MasterGridViewTemplate CreateTemplate() { return new CustomMasterGridViewTemplate(); } } public class CustomMasterGridViewTemplate : MasterGridViewTemplate { public override void Copy() { base.Copy(); if (Clipboard.ContainsData(DataFormats.Html)) { string data = Clipboard.GetData(DataFormats.Html).ToString(); StringBuilder sb = new StringBuilder(data); if (!data.Contains("<TABLE>")) { int insertIndex = data.IndexOf("<TD>"); sb.Insert(insertIndex, "<TABLE><TR>"); } else if (!data.Contains("</TABLE>")) { int insertIndex = data.LastIndexOf("<TD>"); sb.Insert(insertIndex, "</TR></TABLE>"); } Clipboard.SetData(DataFormats.Html, sb.ToString()); } } }
To reproduce: - Subscribe to the following RowsChanged event handler: void radGridView1_RowsChanged(object sender, GridViewCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.ItemChanged) { GridViewDataRowInfo updrow = (GridViewDataRowInfo)e.NewItems[0]; GridViewDataRowInfo oldrow = (GridViewDataRowInfo)e.OldItems[0]; if (updrow.Cells[2].Value != oldrow.Cells[2].Value) { Console.WriteLine(updrow.Cells[2].Value); Console.WriteLine(oldrow.Cells[2].Value); } } } - You will notice that both values are always equal. Workaround: The CellValidating event can be used instead.
Workaround: void radGridView1_PrintCellFormatting(object sender, PrintCellFormattingEventArgs e) { if (e.Column is GridViewImageColumn && e.Row is GridViewDataRowInfo) { e.PrintCell.DrawFill = true; if (radGridView1.PrintStyle.PrintAlternatingRowColor && e.Row.Index % 2 == 1) { e.PrintCell.BackColor = radGridView1.PrintStyle.AlternatingRowColor; } else { e.PrintCell.BackColor = radGridView1.PrintStyle.CellBackColor; } } }
To reproduce: add a RadGridView and bind it to the Northwind.Products data table. Use the following code: private void Form1_Load(object sender, EventArgs e) { this.productsTableAdapter.Fill(this.nwindDataSet.Products); RadMessageBox.Show(string.Format("TOTAL2 = {0}", NewExpression())); } public string NewExpression() { this.radGridView1.Columns.Add(new GridViewDecimalColumn("TOTAL1") { Expression = "ReorderLevel + UnitsOnOrder" }); this.radGridView1.Columns.Add(new GridViewDecimalColumn("TOTAL2") { Expression = "TOTAL1 + UnitPrice" }); //uncomment to get the value of "TOTAL2" // var total1 = this.radGridView1.Rows[2].Cells["TOTAL1"].Value; var total2 = this.radGridView1.Rows[2].Cells["TOTAL2"].Value; return total2.ToString(); } It is not possible to access the column value ("TOTAL2") before firstly to access the column "TOTAL1" value.