(Two related issues) 1) If I call method RunExport(@"C:\Temp\Foo.xml") on an instance of ExportToExcelML, it creates a file called "C:\Temp\Foo.xls" (notice the change of the file extension). 2) But "*.xls" is the wrong file extension, causing Excel to produce a "The file format and extension of 'Foo.xls' don't match..." warning when the file is opened. This warning does not appear if the file is saved as "*.xml". My suggestion: 1) Keep any given file extension in ExportToExcelML.RunExport(..) 2) Adjust the intellisense help text of the RunExport-method accordingly that it mentions the possibility to save the file with extension ".xml" to avoid the said excel warning but with the drawback that this file extension is not exclusively associated with Excel.
Note: InvalidCastException in case of converting Char to Boolean. To reproduce: use the following code snippet: public Form1() { InitializeComponent(); DataTable dt = new DataTable(); dt.Columns.Add("Id", typeof(string)); dt.Columns.Add("Name", typeof(string)); dt.Columns.Add("Active", typeof(string)); dt.Rows.Add("1", "Item1", "Y"); dt.Rows.Add("2", "Item2", "N"); this.radGridView1.DataSource = dt; this.radGridView1.Columns.RemoveAt(2); GridViewCheckBoxColumn checkBoxColumn = new GridViewCheckBoxColumn(); checkBoxColumn.FieldName = "Active"; checkBoxColumn.DataTypeConverter = new ToggleStateConverter(); radGridView1.MasterTemplate.Columns.Add(checkBoxColumn); radGridView1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill; radGridView1.EnableFiltering = true; } public class ToggleStateConverter : TypeConverter { public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { return destinationType == typeof(ToggleState); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { string charValue = value + ""; switch (charValue) { case "Y": return ToggleState.On; case "N": return ToggleState.Off; case "M": return ToggleState.Indeterminate; } return base.ConvertTo(context, culture, value, destinationType); } public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return sourceType == typeof(ToggleState); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { ToggleState state = (ToggleState)value; switch (state) { case ToggleState.On: return "Y"; case ToggleState.Off: return "N"; case ToggleState.Indeterminate: return "M"; } return base.ConvertFrom(context, culture, value); } } Try to filter by "Name" column. As a result FormatException occurs because of inability to convert "Y"/"N" to Boolean although a custom TypeConverter is implemented to handle it.
DECLINED: This happens because you are setting a non-image value to a GridViewImageColumn. The GridViewImageColumn is designed to work with image data directly and if any non-image data is used with this column, this will result in a large number of internal exceptions which are thrown while the column tries to read the image. The throw operation is an expensive one and this causes the slowdown. Also, note that the slowdown is much heavier when running the application with the debugger attached, because when this is the case, each internally thrown exception is written to the console, and writing to the console is an even more expensive operation. If the image is to be applied on the CellFormatting event and the value of the cells in a column are not going to be images, then GridViewTextBoxColumn should be used instead. To reproduce: add a RadGridView and an ImageList with two images. Use the following code snippet: public Form1() { InitializeComponent(); DataTable dt = new DataTable(); for (int i = 0; i < 10; i++) { dt.Columns.Add("Pic" + i); } for (int i = 0; i < 100; i++) { DataRow newRow = dt.NewRow(); foreach (DataColumn col in dt.Columns) { newRow[col.ColumnName] = i; } dt.Rows.Add(newRow); } radGridView1.AutoGenerateColumns = false; for (int i = 0; i < 10; i++) { GridViewImageColumn imgCol = new GridViewImageColumn("col" + i); imgCol.Width = 100; imgCol.FieldName = "Pic" + i; this.radGridView1.Columns.Add(imgCol); } this.radGridView1.DataSource = dt; this.radGridView1.CellFormatting += radGridView1_CellFormatting; } private void radGridView1_CellFormatting(object sender, CellFormattingEventArgs e) { if (e.Row is GridViewDataRowInfo) { e.CellElement.Image = this.imageList1.Images[e.RowIndex % 2]; } }
To reproduce: public Form1() { InitializeComponent(); DataTable dt = new DataTable(); DataColumn colId = new DataColumn("Id", typeof(int)); DataColumn colItem = new DataColumn("Item", typeof(string)); DataColumn colPrice = new DataColumn("Price", typeof(decimal)); dt.Columns.Add(colId); dt.Columns.Add(colItem); dt.Columns.Add(colPrice); for (int i = 0; i < 10; i++) { dt.Rows.Add(i % 5, "Item" + i, i * 2.25m); } GridViewDecimalColumn col = new GridViewDecimalColumn(); col.HeaderText = "Id"; col.Name = "Id"; col.FieldName = "Id"; radGridView1.Columns.Add(col); GridViewTextBoxColumn col1 = new GridViewTextBoxColumn(); col1.HeaderText = "Item"; col1.Name = "Item"; col1.FieldName = "Item"; radGridView1.Columns.Add(col1); GridViewMaskBoxColumn col2 = new GridViewMaskBoxColumn(); col2.HeaderText = "Price"; col2.Name = "Price"; col2.FieldName = "Price"; col2.MaskType = MaskType.Standard; col2.Mask = "###"; col2.TextMaskFormat = MaskFormat.ExcludePromptAndLiterals; radGridView1.Columns.Add(col2); radGridView1.DataSource = dt; this.radGridView1.AutoSizeColumnsMode = Telerik.WinControls.UI.GridViewAutoSizeColumnsMode.Fill; this.radGridView1.ValueChanging += radGridView1_ValueChanging; this.radGridView1.ValueChanged += radGridView1_ValueChanged; } private void radGridView1_ValueChanged(object sender, EventArgs e) { Console.WriteLine("ValueChanged"); } private void radGridView1_ValueChanging(object sender, ValueChangingEventArgs e) { Console.WriteLine("ValueChanging >> old value: " + e.OldValue + " new value: " + e.NewValue); }
To reproduce: Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load Me.CustomersTableAdapter.Fill(Me.NwindDataSet.Customers) Me.RadGridView1.BestFitColumns(Telerik.WinControls.UI.BestFitColumnMode.AllCells) Me.RadGridView1.Columns(2).IsPinned = True Me.RadGridView1.Columns(2).PinPosition = Telerik.WinControls.UI.PinnedColumnPosition.Left Me.RadGridView1.Columns(5).IsPinned = True Me.RadGridView1.Columns(5).PinPosition = Telerik.WinControls.UI.PinnedColumnPosition.Left Dim summaryItem As New GridViewSummaryItem() summaryItem.Name = "ContactTitle" summaryItem.Aggregate = GridAggregateFunction.Count Dim summaryRowItem As New GridViewSummaryRowItem() summaryRowItem.Add(summaryItem) Me.RadGridView1.SummaryRowsBottom.Add(summaryRowItem) Me.RadGridView1.MasterTemplate.ShowTotals = True Me.RadGridView1.EnableKineticScrolling = True Me.RadGridView1.MasterView.SummaryRows(0).PinPosition = PinnedRowPosition.Bottom End Sub Please refer to the attached gif file.
Description: When we scroll the RadGridView from Top to Bottom and Bottom to Top the first record is cut off, if we click the refresh button then it will be displayed properly. To reproduce: - add a RadGridView to a form - add a RadButton to a form - use the following code snippet: public Form1() { InitializeComponent(); radGridView1.MasterTemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.None; radGridView1.EnableCustomFiltering = false; this.radGridView1.AutoSizeRows = true; radGridView1.Columns["CustomerID"].Width = 100; radGridView1.Columns["CompanyName"].Width = 150; radGridView1.Columns["ContactName"].Width = 150; radGridView1.Columns["Country"].Width = 100; radGridView1.Columns["Phone"].Width = 90; radGridView1.Columns["Fax"].Width = 90; radGridView1.Columns["Phone"].AllowFiltering = false; radGridView1.Columns["Fax"].AllowFiltering = false; } private void Form1_Load(object sender, EventArgs e) { // TODO: This line of code loads data into the 'nwindDataSet.Customers' table. You can move, or remove it, as needed. this.customersTableAdapter.Fill(this.nwindDataSet.Customers); } private void radButton1_Click(object sender, EventArgs e) { Form1_Load(sender, e); }
To reproduce: radGridView1 = new RadGridView(); radGridView1.Dock = DockStyle.Fill; Controls.Add(radGridView1); t.Columns.Add("ID"); t.Rows.Add(7); t.Rows.Add(5); t.Rows.Add(8); t.Rows.Add(4); t.Rows.Add(9); radGridView1.DataSource = t; radGridView1.Columns[0].Width = 100; With the code above, start the app, sort the grid ascending and select the row with value 8. Use the following code on a button to delete the row with value 5: private void radButton1_Click(object sender, EventArgs e) { t.Rows[1].Delete(); } In this case, the current should be the row with value 8, not the one with value 7 as is.
Allow changing the header row height when RadGridView is set to ColumnGroupsViewDefinition.
To reproduce: public Form1() { InitializeComponent(); ColumnGroupsViewDefinition view = new ColumnGroupsViewDefinition(); view.ColumnGroups.Add(new GridViewColumnGroup("Customer Contact")); view.ColumnGroups.Add(new GridViewColumnGroup("Details")); view.ColumnGroups[1].Groups.Add(new GridViewColumnGroup("Address")); view.ColumnGroups[1].Groups.Add(new GridViewColumnGroup("Contact")); view.ColumnGroups[0].Rows.Add(new GridViewColumnGroupRow()); view.ColumnGroups[0].Rows[0].Columns.Add(this.radGridView1.Columns["CompanyName"]); view.ColumnGroups[0].Rows[0].Columns.Add(this.radGridView1.Columns["ContactName"]); view.ColumnGroups[0].Rows[0].Columns.Add(this.radGridView1.Columns["ContactTitle"]); view.ColumnGroups[1].Groups[0].Rows.Add(new GridViewColumnGroupRow()); view.ColumnGroups[1].Groups[0].Rows[0].Columns.Add(this.radGridView1.Columns["Address"]); view.ColumnGroups[1].Groups[0].Rows[0].Columns.Add(this.radGridView1.Columns["City"]); view.ColumnGroups[1].Groups[0].Rows[0].Columns.Add(this.radGridView1.Columns["Country"]); view.ColumnGroups[1].Groups[1].Rows.Add(new GridViewColumnGroupRow()); view.ColumnGroups[1].Groups[1].Rows[0].Columns.Add(this.radGridView1.Columns["Phone"]); view.ColumnGroups[1].Groups[1].Rows[0].Columns.Add(this.radGridView1.Columns["Fax"]); radGridView1.ViewDefinition = view; radGridView1.RightToLeft = System.Windows.Forms.RightToLeft.Yes; view.ColumnGroups[0].PinPosition = PinnedColumnPosition.Left; } Please see the attached screenshot. Workaround: add the columns belonging to the pinned group in a reversed order.
Wrapping text in ColumnsGroupViewDefinition does not affect the size of the cells.
To reproduce: - Create a grid with ColumnGroupsViewDefinition view( Column Groups View) - Set the AutoSizeColumnsMode to fill - Start the project and resize a column - Resize a column then minimize and maximize - The column have a different size Workaround: -Handle the form layout event like this: void Form1_Layout(object sender, LayoutEventArgs e) { if (this.WindowState == FormWindowState.Minimized) { this.radGridView1.GridElement.SuspendLayout(); } else { this.radGridView1.GridElement.ResumeLayout(true); } }
To reproduce: Create a RadGridView and set its view to ColumnGroupsViewDefinition following this article - http://www.telerik.com/help/winforms/gridview-viewdefinitions-column-groups-view.html. The text in the headers long enough so it does not fit in the cells. Using the ViewCellFormatting event set each cell's TextWrap property to true. You will notice that the header cells are not being TextWrapped
Currently, when you have an object which has more than one parent, the grid falls in an invalid state and shows the child under all of the parents, however, it does not behave correctly. As this is not supported scenario, the user should be notified via exception.
Workaround: use the CellValidating event
To reproduce: this.radGridView1.MultiSelect = true; GroupDescriptor descriptor1 = new GroupDescriptor(); descriptor1.GroupNames.Add("ProductId", ListSortDirection.Ascending ); this.radGridView1.GroupDescriptors.Add(descriptor1); Please refer to the attached gif file.
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: private RadGridView radGridView1; public Form1() { InitializeComponent(); radGridView1 = new RadGridView() { Dock = DockStyle.Fill }; Controls.Add(radGridView1); List<Item> items = new List<Item>(); for (int i = 0; i < 10; i++) { items.Add(new Item(i, "Item" + i, DateTime.Now.AddDays(i))); } radGridView1.AutoGenerateColumns = true; this.radGridView1.DataSource = items; this.radGridView1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill; this.radGridView1.EnableFiltering = true; this.radGridView1.CustomFiltering += radGridView1_CustomFiltering; radGridView1.EnableCustomFiltering = true; } void radGridView1_CustomFiltering(object sender, GridViewCustomFilteringEventArgs e) { e.Handled = false; e.Visible = true; var item = (Item)e.Row.DataBoundItem; if (item == null) return; if (item.Name.EndsWith("Item")) { e.Handled = true; e.Visible = false; } } public class Item { public int Id { get; set; } public string Name { get; set; } public Nullable<DateTime> Date { get; set; } public Item(int id, string name, Nullable<DateTime> date) { this.Id = id; this.Name = name; this.Date = date; } } Workaround: private void radGridView1_CellEndEdit(object sender, GridViewCellEventArgs e) { this.radGridView1.MasterTemplate.Refresh(); }
To reproduce: Add a RadGridView. Create a DataTable with 3 columns with data type - Decimal. To the last column set the following expression - "column1 + column2". Add some rows with some values. Turn on excel like filtering - http://www.telerik.com/help/winforms/gridview-filtering-excel-like-filtering.html. Subscribe to the CellValueChanged event and add the following code: if (this.radGridView1.CurrentRow.DataBoundItem != null) { ((DataRowView)this.radGridView1.CurrentRow.DataBoundItem).Row.EndEdit(); // force row subtotal update } Start the application and filter the last column by some of the available values. Change a value in one of the first two cells. You will notice that the last cell's value is not updated although it is updated in the data table. This behavior does not occur when the grid is not filtered.
To reproduce: this.radGridView1.AllowEditRow = false; this.radGridView1.AllowColumnHeaderContextMenu = true; this.radGridView1.AllowCellContextMenu = true; Workaround: subscribe to the ContextMenuOpening event and hide the items. An alternative solution is to use RadGridView in read-only mode.
To reproduce: Add a RadGridView and three GridViewComboBoxColumns with repetitive values: for (int i = 0; i < 3; i++) { List<int> datasource = new List<int>(); for (int j = 0; j < 12; j++) { datasource.Add(j); } this.Grid.Columns.Add(new GridViewComboBoxColumn() { DataSource = datasource }); } for (int i = 0; i < 10; i++) { for (int j = 0; j < 5; j++) { this.Grid.Rows.Add(i, i, i); } } Start the project and sort second and last column by the same value and make sure that you have vertical scrollbar. Select a cell in the second column change its value and press the tab key(or click on another cell in the same row). You will see that the scrollbar will go to the bottom of the grid. Workaround: Use the following class: public class ActionExecuteHelper { #region Singleton private static ActionExecuteHelper instance; private static readonly object syncRoot = new object(); public static ActionExecuteHelper Instance { get { if (instance == null) { lock (syncRoot) { if (instance == null) { instance = new ActionExecuteHelper(); } } } return instance; } } #endregion private readonly Timer timer; public ActionExecuteHelper() { this.timer = new Timer(); } public void ExecuteDelayedAction(Action action, int delayInMilliseconds) { EventHandler timerTick = null; timerTick = delegate { this.timer.Stop(); this.timer.Tick -= timerTick; action.Invoke(); }; this.timer.Tick += timerTick; this.timer.Interval = delayInMilliseconds; this.timer.Start(); } } And use it with the following code private int savedValue; private void Grid_CellEndEdit(object sender, GridViewCellEventArgs e) { this.Grid.TableElement.VScrollBar.ValueChanged += ValueChangedDelegate; } private void ValueChangedDelegate(object sender, EventArgs e) { this.Grid.TableElement.VScrollBar.ValueChanged -= ValueChangedDelegate; ActionExecuteHelper.Instance.ExecuteDelayedAction(new Action(this.SetScrollbarValue), 5); } private void SetScrollbarValue() { this.Grid.TableElement.VScrollBar.Value = savedValue; } private void Grid_ValueChanging(object sender, ValueChangingEventArgs e) { this.savedValue = this.Grid.TableElement.VScrollBar.Value; } This way the value of the scrollbar will be saved and restored.