We work with a RadGridView with 145000 rows and 4 columns. We use copy-paste to move data around from other apps and the application with build with Telerik.
When we copy a flat file (with tabs delimited fields) and we paste it to the RadGridView, the whole process is painfully slow. The function to retrieve the data from the clipboard takes minutes (maybe hours, I cancelled it). It tracked the cause down to the StringTokenizer class. The tokenizer splits the string up into separate fields. But after extracting a field it creates a new copy of that string (containing about 10MB of data) minus the field. I patched it (with HarmonyX) and now it takes only one second:
static class StringTokenizerPerformancePatch
{
static private readonly InstanceFieldAccessor<StringTokenizer, LinkedList<string>> _tokens = new InstanceFieldAccessor< StringTokenizer, LinkedList<string>>("tokens");
static private readonly InstanceFieldAccessor<StringTokenizer, string> _sourceString = new InstanceFieldAccessor<StringTokenizer, string>("sourceString");
static private readonly InstanceFieldAccessor< StringTokenizer, string> _delimiter = new InstanceFieldAccessor<StringTokenizer, string>("delimiter");
static private readonly InstanceFieldAccessor< StringTokenizer, IEnumerator<string>> _enumerator = new InstanceFieldAccessor<StringTokenizer, IEnumerator<string>>("enumerator");
[HarmonyPatch(typeof(StringTokenizer), "Tokenize")]
staticclassPatch_StringTokenizer_Tokenize
{
static bool Prefix(StringTokenizer __instance)
{
var tokens = _tokens.GetValue(__instance);
var sourceString = _sourceString.GetValue(__instance);
var delimiter = _delimiter.GetValue(__instance);
Tokenize(tokens, sourceString, delimiter);
_enumerator.SetValue(__instance, tokens.GetEnumerator());
returnfalse;
}
static private void Tokenize(LinkedList<string> tokens, string text, string delimiter)
{
tokens.Clear();
if (string.IsNullOrEmpty(text))
return;
int index = 0;
while(true)
{
var index2 = text.IndexOf(delimiter, index, StringComparison.Ordinal);
if (index2 < 0)
{
tokens.AddLast(text.Substring(index));
break;
}
string token = text.Substring(index, index2 - index);
tokens.AddLast(token);
index = index2 + delimiter.Length;
}
}
}
}
Please update your tokanizer to increase performance. While you are at it:This is about the following method:
public void SetError(GridViewCellCancelEventArgs e, Exception exception)
{
GridViewDataErrorEventArgs args = new GridViewDataErrorEventArgs(exception, 0, 0, GridViewDataErrorContexts.Commit);
if (e != null)
{
args = new GridViewDataErrorEventArgs(exception, e.ColumnIndex, e.RowIndex, GridViewDataErrorContexts.Commit);
}
this.EventDispatcher.RaiseEvent<GridViewDataErrorEventArgs>(EventDispatcher.DataError, this, args);
if (args.ThrowException)
{
throw args.Exception;
}
if (args.Cancel)
{
//TODO: cancel row edit
}
}
Right now, the method GridViewTemplate.SetError accepts a parameter of type GridViewCellCancelEventArgs, which in itself is weird, because event args should only be used inside events and OnXXX-methods. But since SetError fires an event, one could argue that this method is like a OnXXX-method.
But inside it becomes more weird, it translates GridViewCellCancelEventArgs into GridViewDataErrorEventArgs. And I must admit, the last DataError-args feel a lot more logical that CellCancel-args when firing setting an error. Furthermore, if I create an override of class GridViewCellCancelEventArgs to contain more data about an error, this information never reaches the event handlers.
So why this translation? Or better: Why this parameter?
My suggestion would be to make a new overload:
public void SetError(GridViewDataErrorEventArgs e)
{
if (e == null)
throw new ArgumentNullException(nameof(e));
this.EventDispatcher.RaiseEvent<GridViewDataErrorEventArgs>(EventDispatcher.DataError, this, args);
if (args.ThrowException)
throw args.Exception;
if (args.Cancel)
{
// TODO: I really do not know what telerik wanted to do here, so I live this up to Telerik.
}
}
This is about this method:
public void SetError(GridViewCellCancelEventArgs e, Exception exception)
{
GridViewDataErrorEventArgs args = new GridViewDataErrorEventArgs(exception, 0, 0, GridViewDataErrorContexts.Commit);
if (e != null)
{
args = new GridViewDataErrorEventArgs(exception, e.ColumnIndex, e.RowIndex, GridViewDataErrorContexts.Commit);
}
this.EventDispatcher.RaiseEvent<GridViewDataErrorEventArgs>(EventDispatcher.DataError, this, args);
if (args.ThrowException)
{
throw args.Exception;
}
if (args.Cancel)
{
//TODO: cancel row edit
}
}
The method GridViewTemplate.SetError accepts a parameter of type GridViewCellCancelEventArgs (named e), but uses the information to create a new object of type GridViewDataErrorEventArgs (named args) and uses information from e to fill args.
The method then fires an event with args. Args also has a property Cancel which can be set in the event handlers. But nothing is done with that property.
Parameter e also has a property Cancel which is never be filled. So it could be useful, at the end of SetError, to set e.Cancel with args.Cancel. This way the caller can use the Cancel information from the events.
This request is also related to my next request.
PS: Why is GridViewCellCancelEventArgs called this way? It implies it has arguments for an event, but it is not used for an event, am I right?
The method GridViewTemplate.SetError creates in most situations an GridViewDataErrorEventArgs object twice.
Current code:
GridViewDataErrorEventArgs args = new GridViewDataErrorEventArgs(exception, 0, 0, GridViewDataErrorContexts.Commit);
if (e != null)
{
args = new GridViewDataErrorEventArgs(exception, e.ColumnIndex, e.RowIndex, GridViewDataErrorContexts.Commit);
}
In assume in most cases e will not be null, so in must cases the first args will be removed. This has a small negative impact on memory and performace.
Suggestion:
GridViewDataErrorEventArgs args = e == null
? new GridViewDataErrorEventArgs(exception, 0, 0, GridViewDataErrorContexts.Commit)
: new GridViewDataErrorEventArgs(exception, e.ColumnIndex, e.RowIndex, GridViewDataErrorContexts.Commit);
After installing R3 2022, the QuickStart example can't be run:
Repro-steps
Expected behavior
Observed behavior
Repro-steps
Expected behavior
Observed behavior
Extra info
If the first column is of another type (like GridViewDecimalColumn) a DataError occurs (because "System.IO.MemoryStream" cannot be parces to a decimal).
To reproduce the issue, just drag a RadToggleSwitch to the form. Then, at run time call the method:
radToggleSwitch1.SetToggleState(newValue: false, animate: false);
You will notice that the colors are changed but the thumb is not moved:
Workaround:
this.radToggleSwitch1.AllowAnimation = false;
this.radToggleSwitch1.Toggle();

ThemeResolutionService.ApplicationThemeName = "Office2019Dark";
this.radDropDownList1.EnableAlternatingItemColor = true;
Hi,
I am using CalHelper to convert recurrence rule to and from string.
It looks like when I convert rule to string and then parse it back to recurrence, some of the properties get dropped.
Please see below from my VS editor highlighting this:
Am I doing something wrong, or is this a bug?
Thanks
Anu
The ChartLegendElement displays the legend items in a StackLayoutElement which can be either vertical or horizontal.
public RadForm1()
{
InitializeComponent();
Random rand = new Random();
List<LineSeries> list = new List<LineSeries>();
for (var index = 1; index <= 15; index++)
{
LineSeries ls = new LineSeries();
ls.LegendTitle = "Series " + index;
list.Add(ls);
}
for (int index = 1; index <= 100; index++)
{
foreach (LineSeries s in list)
s.DataPoints.Add(new CategoricalDataPoint(index, rand.Next(0, rand.Next(5, 20))));
}
this.radChartView1.Series.AddRange(list.ToArray());
this.radChartView1.ShowLegend = true;
this.radChartView1.ChartElement.LegendPosition = LegendPosition.Bottom;
this.radChartView1.ChartElement.LegendElement.StackElement.Orientation = Orientation.Vertical;
}
Horizontal:
Vertical:
It would be good to provide an option for wrapping the legend items:
Workaround: use an appropriate container for the legend items to wrap the legend item and use the space more efficiently
https://docs.telerik.com/devtools/winforms/knowledge-base/chartview-wrap-legend-items
Searching for a workaround for a previously reported bug, in walked into a little thing:
private void SelectAllCells()
{
GridViewRowInfoEnumerator rowInfoEnumerator = new GridViewRowInfoEnumerator((IHierarchicalRow) this.GridViewElement.Template);
List<GridViewCellInfo> gridViewCellInfoList = new List<GridViewCellInfo>();
this.MasterTemplate.SelectedCells.BeginUpdate();
this.MasterTemplate.SelectedCells.Clear();
while (rowInfoEnumerator.MoveNext())
{
GridViewRowInfo current = rowInfoEnumerator.Current;
if (current.CanBeSelected)
{
foreach (GridViewCellInfo cell in current.Cells)
this.MasterTemplate.SelectedCells.Add(cell);
}
}
this.MasterTemplate.SelectedCells.EndUpdate(true);
}
I am rewriting the RadGridViewPaste-mechanism. Therefor I want to call PastingCellClipboardContent-event using the EventDispatcher.CellClipboardPaste. therefor I need the class GridViewCellValueEventArgs. It has two constructors:
The second constructor is marked as Internal, not Public.
Request: Can this constructor be made Public?
Please run the attached sample project, open the drop down and press the down arrow key.
Observed: application hangs
Expected: since all menu items in the drop down are disabled, nothing is expected to happen. However, the application shouldn't hangs.
Workaround:
public class CustomRadDropDownButtonElement : RadDropDownButtonElement
{
protected override Type ThemeEffectiveType
{
get
{
return typeof(RadDropDownButtonElement);
}
}
protected override RadDropDownButtonPopup CreateDropDown()
{
return new CustomDropDown(this);
}
}
public class CustomDropDown : RadDropDownButtonPopup
{
public CustomDropDown(RadElement ownerElement) : base(ownerElement)
{
}
protected override void EnsureItemEnabled(RadItem item, bool isUp)
{
do
{
item = this.GetNextItem(item, !isUp);
if (item == this.Items.Last())
{
break;
}
} while (!item.Enabled);
if (item != null)
{
this.SelectItem(item);
}
}
}
Use a custom font set like this:
this.radSyntaxEditor1.SyntaxEditorElement.EditorFontFamily = new Telerik.WinControls.SyntaxEditor.UI.FontFamily("Cascadia Code");
this.radSyntaxEditor1.SyntaxEditorElement.EditorFontSize = 13;
Then load a large document, at least 10K lines so that the clipping is visible.
Setting the SyntaxEditorElement.HorizontalScrollBar.Visibility property to Collapsed indeed hides the horizontal scrollbar but it is still measured and arranged and overlaps the view if you shrink the view: