Hi there
I found this issue in RadTreeView filtering
I have a 2-level text structure programmatically created
after creation I use the command
radtree.ExpandAll()
This is the only event handled
Private Sub txtFilter_TextChanged(sender As Object, e As EventArgs) Handles txtFilter.TextChanged
radtreeNavigazione.Filter = txtFilter.Text
End Sub
When I set a filter with at least 4 letters and then I select the text in the filter box and press "back", the app freezes with cpu working at 50 %.
I use this workaround to solve
Private Sub txtFilter_TextChanged(sender As Object, e As EventArgs) Handles txtFilter.TextChanged
radtreeNavigazione.Filter = txtFilter.Text
End Sub
I use this workaround
Private Sub txtFilter_TextChanged(sender As Object, e As EventArgs) Handles txtFilter.TextChanged
radtree.CollapseAll()
radtreeNavigazione.Filter = txtFilter.Text
radtree.ExpandAll()
End Sub
In this way it works, but I wanted to report the issue.
Thank you in advance for your attention
Afternoon
We have a RadTReeView that we drag nodes around to reorder within the same treeview.
There are about 2000 nodes in the tree that is a self referencing data bound and goes about 15 levels deep at some parts of the tree
When we click left mouse down (keeping it down) to drag and drop reorder a node, using the mouse wheel to scroll the tree up or down does not work anymore
We want to for instance drag a node from a location to another location outside the current view, using the mouse wheel to navigate the tree view up and down. This used to work, but not after upgrading to the new 2019 telerik.
Is there a tree view control option that needs to be toggled for this to work?
Thank you
Theo
To reproduce: - Open the Property builder, add some nodes and disable them. - There is no way to enable them at design time. Workaround. Disable the nodes at runtime.
To reproduce: add a RadTreeView and a RadBreadCrumb and apply the MaterialTeal theme. Refer to the attached screenshot illustrating the wrong font of the selected item. Workaround: public partial class RadForm1 : Telerik.WinControls.UI.RadForm { public RadForm1() { InitializeComponent(); ThemeResolutionService.ApplicationThemeName = "MaterialTeal"; this.radTreeView1.NodeFormatting += radTreeView1_NodeFormatting; this.radTreeView1.SelectedNodeChanged += radTreeView1_SelectedNodeChanged; } private void UpdateFont() { foreach (RadSplitButtonElement item in this.radBreadCrumb1.BreadCrumbElement.Items) { foreach (RadMenuItem menuItem in item.Items) { if (this.radTreeView1.SelectedNode != null && menuItem.Text == this.radTreeView1.SelectedNode.Text) { menuItem.Font = new Font(f.FontFamily,f.Size, FontStyle.Bold); } } } } private void radTreeView1_SelectedNodeChanged(object sender, Telerik.WinControls.UI.RadTreeViewEventArgs e) { UpdateFont(); } Font f = null; private void radTreeView1_NodeFormatting(object sender, Telerik.WinControls.UI.TreeNodeFormattingEventArgs e) { f = e.NodeElement.ContentElement.Font; }
At the moment the filter predicate is called only for the root nodes. A possible workaround is to recursively apply the filter logic for the child nodes as well. Workaround: private void Button1_Click(object sender, EventArgs e) { this.radTreeView1.TreeViewElement.FilterPredicate = this.FilterNode; this.radTreeView1.Filter = "Custom"; } private bool FilterNode(RadTreeNode node) { Console.WriteLine(node.Text); if (node.Text.Contains("Child: 3")) { return true; } Stack<RadTreeNode> children = new Stack<RadTreeNode>(); if (node.Nodes.Count > 0) { children.Push(node); while (children.Count > 0) { RadTreeNode current = children.Pop(); foreach (RadTreeNode child in current.Nodes) { if (child.Text.Contains("Child: 3")) { return true; } children.Push(child); } } } return false; }
Hi there, through out our app we use the treeview everywhere - but we have had this issue where it crashes when someone right clicks on the treeview where there is no node. This crash happens on the "ContextMenuOpening" event when we try to use e.TreeElement. This is because, on the telerik side of things, the getter for that property is throwing a NullReferenceException (see attached screenshot)
I think this is happening because in your RadTreeViewCancelEventArgs.cs it is this:
public RadTreeViewElement TreeElement
{
get { return node.TreeViewElement; }
}
public RadTreeView TreeView
{
get { return node.TreeView; }
}
But since node == null, it throws the exception. It probably should check for null, and if node is null, return a null for those properties - so we can use null conditional operator to check stuff. We have a workaround but it is just lots of copy and paste of things.
How to reproduce: public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { radTreeView1.CheckBoxes = true; radTreeView1.TriStateMode = true; var rootNode = new RadTreeNode("Root Node"); radTreeView1.Nodes.Add(rootNode); // Create two parent nodes var parentNode1 = new RadTreeNode("First Parent"); var parentNode2 = new RadTreeNode("Second Parent"); // Add the parent nodes to tree view's nodes collection rootNode.Nodes.AddRange(parentNode1, parentNode2); // Create a child node var radTreeNodes2 = new List<RadTreeNode> { new RadTreeNode("nA.1"), new RadTreeNode("nB.2"), new RadTreeNode("nC.3"), new RadTreeNode("nD.4"), new RadTreeNode("nE.5"), }; var radTreeNodes = new List<RadTreeNode> { new RadTreeNode("nA.1"), new RadTreeNode("nB.2"), new RadTreeNode("nC.3"), new RadTreeNode("nD.4"), new RadTreeNode("nE.5"), }; // Add the child node to the first parent's nodes collection parentNode1.Nodes.AddRange(radTreeNodes); // Remove the child from the first parent collection and add it to the second parent nodes collection //parentNode1.Nodes.Remove(childNode); parentNode2.Nodes.AddRange(radTreeNodes2); rootNode.ExpandAll(); rootNode.Checked = true; } private void textBox1_TextChanged(object sender, EventArgs e) { radTreeView1.Filter = textBox1.Text; if(radTreeView1.TopNode != null) radTreeView1.TopNode.ExpandAll(); } }
To reproduce: please refer to the attached gif files with the different versions of the Telerik UI for WinForms suite . You will notice that while you are dragging a node the default cursor is an arrow with a small rectangle. If you hold the Ctrl key pressed while dragging the node, the default cursor is an arrow with a small rectangle and '+'. With the latest version, the '+' sign is missing when you hold the Ctrl key.
To reproduce: please refer to the attached sample project and try to reorder a node. You will notice that the message box is not rendered properly. //case 2 private void DragDropService_PreviewDragDrop(object sender, RadDropEventArgs e) { e.Handled = true; RadMessageBox.Show(this, "Showing a messagebox under dragending doesn't work correctly."); } //case 1 private void radTreeView1_DragEnding(object sender, Telerik.WinControls.UI.RadTreeViewDragCancelEventArgs e) { //e.Cancel = true; //RadMessageBox.Show(this, "Showing a messagebox under dragending doesn't work correctly."); } Workaround: subscribe to the TreeViewElement.DragDropService.PreviewDragDrop and set the Handled argument to true if you want to cancel the drop operation. Then, handle the TreeViewElement.DragDropService.Stopped event and show the desired message. public Form1() { InitializeComponent(); this.radTreeView1.TreeViewElement.DragDropService.PreviewDragDrop += DragDropService_PreviewDragDrop; this.radTreeView1.TreeViewElement.DragDropService.Stopped+=DragDropService_Stopped; } private void DragDropService_Stopped(object sender, EventArgs e) { RadMessageBox.Show(this, "Showing a messagebox under dragending doesn't work correctly."); } private void DragDropService_PreviewDragDrop(object sender, RadDropEventArgs e) { e.Handled = true; }
To reproduce: Run the attached application. Drag Node 2 to after Node 4 while holding down the Alt key. Note that the Node 2 copy is inserted before Node 4. When Node 2 is dragged to after Node 4 without holding down the Alt key, Node 2 is correctly moved to after Node 4. Workaround: you can modify the TreeViewDragDropService and control at what position exactly to be inserted the dragged node: https://docs.telerik.com/devtools/winforms/treeview/drag-and-drop/modify-the-dragdropservice-behavior https://docs.telerik.com/devtools/winforms/treeview/drag-and-drop/drag-and-drop-in-bound-mode
To reproduce: use the following code snippet. You will notice that mnemonics are displayed. public RadForm1() { InitializeComponent(); new RadControlSpyForm().Show(); this.radTreeView1.DisplayMember = "name"; this.radTreeView1.ParentMember = "pid"; this.radTreeView1.ChildMember = "id"; this.radTreeView1.DataSource = this.GetSampleData(); this.radTreeView1.NodeFormatting += radTreeView1_NodeFormatting; this.radTreeView1.SelectedNodeChanged += radTreeView1_SelectedNodeChanged; this.radBreadCrumb1.DefaultTreeView = this.radTreeView1; } private void radTreeView1_SelectedNodeChanged(object sender, Telerik.WinControls.UI.RadTreeViewEventArgs e) { foreach (RadSplitButtonElement item in this.radBreadCrumb1.BreadCrumbElement.Items) { item.ActionButton.TextElement.UseMnemonic = false; } } private void radTreeView1_NodeFormatting(object sender, Telerik.WinControls.UI.TreeNodeFormattingEventArgs e) { e.NodeElement.ContentElement.UseMnemonic = false; } private DataTable GetSampleData() { DataTable dt = new DataTable(); DataColumn dc = new DataColumn(); dc.ColumnName = "id"; dc.DataType = typeof(int); dt.Columns.Add(dc); DataColumn dc1 = new DataColumn(); dc1.ColumnName = "name"; dc1.DataType = typeof(string); dt.Columns.Add(dc1); DataColumn dc2 = new DataColumn(); dc2.ColumnName = "pid"; dc2.DataType = typeof(int); dt.Columns.Add(dc2); DataRow dr = dt.NewRow(); dr[0] = 0; dr[1] = "Hello & Goodbye"; dr[2] = DBNull.Value; dt.Rows.Add(dr); dr = dt.NewRow(); dr[0] = 1; dr[1] = @"&C:\"; dr[2] = 0; dt.Rows.Add(dr); dr = dt.NewRow(); dr[0] = 2; dr[1] = @"&D:\"; dr[2] = 0; dt.Rows.Add(dr); dr = dt.NewRow(); dr[0] = 3; dr[1] = "&Program Files"; dr[2] = 1; dt.Rows.Add(dr); dr = dt.NewRow(); dr[0] = 4; dr[1] = "M&icrosoft"; dr[2] = 3; dt.Rows.Add(dr); dr = dt.NewRow(); dr[0] = 5; dr[1] = "&Telerik"; dr[2] = 3; dt.Rows.Add(dr); dr = dt.NewRow(); dr[0] = 6; dr[1] = "&WINDOWS"; dr[2] = 1; dt.Rows.Add(dr); return dt; } Workaround: set the DefaultTreeView before subscribing to the treeview events. this.radBreadCrumb1.DefaultTreeView = this.radTreeView1; this.radTreeView1.NodeFormatting += radTreeView1_NodeFormatting; this.radTreeView1.SelectedNodeChanged += radTreeView1_SelectedNodeChanged;
To reproduce: public RadForm1() { InitializeComponent(); BindingList<Item> items = new BindingList<Item>(); items.Add(new Item(0,"Root", CheckState.Checked,-1)); for (int i = 1; i < 5; i++) { items.Add(new Item(i, "Node" + i, CheckState.Checked,0)); } this.radTreeView1.CheckBoxes = true; this.radTreeView1.DisplayMember = "Name"; this.radTreeView1.ChildMember = "Id"; this.radTreeView1.ParentMember = "ParentId"; this.radTreeView1.CheckedMember = "IsActive"; this.radTreeView1.AutoCheckChildNodes = true; this.radTreeView1.TriStateMode = true; this.radTreeView1.DataSource = items; this.radTreeView1.ExpandAll(); } public class Item { public int Id { get; set; } public string Name { get; set; } public CheckState IsActive { get; set; } public int ParentId { get; set; } public Item(int id, string name, CheckState isActive, int parentId) { this.Id = id; this.Name = name; this.IsActive = isActive; this.ParentId = parentId; } } Workaround: implement a TypeConverter that can handle converting from/to System.Windows.Forms.CheckState. A sample implementation for creating a custom TypeConverter is demonstrated in the following help article: https://docs.telerik.com/devtools/winforms/treeview/data-binding/togglestateconverter
To reproduce: please run the attached sample project and follow the steps in the attached sample gif file. Workaround: instead of filtering the nodes, you can manipulate the RadTreeNode.Visible property considering the filter criteria and whether the node contains a child that matches the filter. public RadForm1() { InitializeComponent(); this.radTreeView1.ShowLines = true; } private void FilterNode(RadTreeNode node) { bool atLeastOneChildMatches = false; ChildNodeContains(this.radTextBox1.Text.ToLower(), node.Nodes, ref atLeastOneChildMatches); if (node.Text.ToLower().Contains(this.radTextBox1.Text.ToLower()) || atLeastOneChildMatches) { node.Visible = true; } else { node.Visible = false; } } private void ChildNodeContains(string filterCritria, RadTreeNodeCollection nodes, ref bool atLeastOneChildMatches) { foreach (RadTreeNode node in nodes) { if (node.Text.ToLower().Contains(filterCritria)) { atLeastOneChildMatches = true; return; } if (atLeastOneChildMatches == false && node.Nodes.Count > 0) { ChildNodeContains(filterCritria, node.Nodes, ref atLeastOneChildMatches); } } } private void radTextBox1_TextChanged(object sender, EventArgs e) { PerformFilter(this.radTreeView1.Nodes); } private void PerformFilter(RadTreeNodeCollection nodes) { foreach (RadTreeNode node in nodes) { FilterNode(node); if (node.Nodes.Count > 0) { PerformFilter(node.Nodes); } } }
Workaround: Expand and collapse the nodes so that they are synchronized this.radTreeView1.ExpandAll(); this.radTreeView1.CollapseAll();
Workaround: private void radButton1_Click(object sender, EventArgs e) { var allNodes = radTreeView1.TreeViewElement.GetNodes().ToList(); int row = 0; Workbook workbook = new Workbook(); Worksheet newWorksheet = workbook.Worksheets.Add(); foreach (var item in allNodes) { CellSelection cell = newWorksheet.Cells[row, item.Level]; cell.SetValue(item.Text); cell = newWorksheet.Cells[row++, 2]; cell.SetValue(item.Checked); } var formatProvider = new XlsxFormatProvider(); var bytes = formatProvider.Export(workbook); File.WriteAllBytes(@"D:\Test.xlsx", bytes); }
To reproduce. - Add 100 nodes so the scroll appears. - Try scrolling to bottom. Workaround: radTreeView1.TreeViewElement.Scroller.ScrollMode = ItemScrollerScrollModes.Discrete;
Scenario: Populate RadTreeView with data coming from an XML: https://docs.telerik.com/devtools/winforms/treeview/data-binding/binding-to-xml-data The XML file stores a boolean value "IsActive" which will determine the check state of the node. Then, specify the RadTreeView. CheckMember property as well string fileName = @"TempFile.xml"; DataSet tocDataSet = new DataSet("Toc"); tocDataSet.ReadXml(fileName); this.radTreeView1.DataMember = "FlatNode"; this.radTreeView1.DisplayMember = "Title"; this.radTreeView1.ChildMember = "Id"; this.radTreeView1.ParentMember = "ParentId"; this.radTreeView1.CheckedMember = "IsActive"; this.radTreeView1.DataSource = tocDataSet; When you try to check/uncheck a node, an exception occurs indicating the inability to convert the string value "On" to ToggleState. Currently RadTreeView supports only bool, bool? to ToggleState and vice versa. The TypeConverter should be exposed so the developer can change it and implement the custom conversion. <?xml version="1.0" encoding="utf-8"?> <ArrayOfFlatNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <FlatNode> <Title>New Name</Title> <IsActive>false</IsActive> <ParentId>0</ParentId> <Id>1</Id> </FlatNode> <FlatNode> <Title>1st Node</Title> <IsActive>false</IsActive> <ParentId>1</ParentId> <Id>2</Id> </FlatNode> <FlatNode> <Title>1.1</Title> <IsActive>true</IsActive> <ParentId>2</ParentId> <Id>3</Id> </FlatNode> <FlatNode> <Title>1.2</Title> <IsActive>false</IsActive> <ParentId>2</ParentId> <Id>4</Id> </FlatNode> <FlatNode> <Title>1.3</Title> <IsActive>false</IsActive> <ParentId>2</ParentId> <Id>5</Id> </FlatNode> <FlatNode> <Title>2.0</Title> <IsActive>true</IsActive> <ParentId>1</ParentId> <Id>6</Id> </FlatNode> <FlatNode> <Title>2.1</Title> <IsActive>true</IsActive> <ParentId>6</ParentId> <Id>7</Id> </FlatNode> <FlatNode> <Title>2.2</Title> <IsActive>true</IsActive> <ParentId>6</ParentId> <Id>8</Id> </FlatNode> <FlatNode> <Title>2.3</Title> <IsActive>true</IsActive> <ParentId>6</ParentId> <Id>9</Id> </FlatNode> <FlatNode> <Title>3.0</Title> <IsActive>true</IsActive> <ParentId>1</ParentId> <Id>10</Id> </FlatNode> <FlatNode> <Title>4.0</Title> <IsActive>false</IsActive> <ParentId>1</ParentId> <Id>11</Id> </FlatNode> <FlatNode> <Title>5.0</Title> <IsActive>true</IsActive> <ParentId>1</ParentId> <Id>12</Id> </FlatNode> <FlatNode> <Title>3.1</Title> <IsActive>false</IsActive> <ParentId>10</ParentId> <Id>13</Id> </FlatNode> <FlatNode> <Title>New Item</Title> <IsActive>true</IsActive> <ParentId>8</ParentId> <Id>15</Id> </FlatNode> <FlatNode> <Title>New Item</Title> <IsActive>true</IsActive> <ParentId>8</ParentId> <Id>16</Id> </FlatNode> </ArrayOfFlatNode>
Workaround: class CustomTreeView : RadTreeView { //Replace the default element with the custom one protected override RadTreeViewElement CreateTreeViewElement() { return new CustomTreeViewElement(); } //Enable theming for the control public override string ThemeClassName { get { return typeof(RadTreeView).FullName; } } } class CustomTreeViewElement : RadTreeViewElement { //Enable themeing for the element protected override Type ThemeEffectiveType { get { return typeof(RadTreeViewElement); } } protected override bool ProcessContextMenu(Point location) { RadTreeNode node = this.GetNodeAt(location); if (node == null) { RadContextMenu menu = new RadContextMenu(); RadMenuItem item = new RadMenuItem(); item.Text = "Add a root node"; menu.Items.Add(item); item.Click += item_Click; TreeViewContextMenuOpeningEventArgs args = new TreeViewContextMenuOpeningEventArgs(node, menu); OnContextMenuOpening(args); if (!args.Cancel) { menu.Show(this.ElementTree.Control, location); return true; } } return base.ProcessContextMenu(location); } private void item_Click(object sender, EventArgs e) { this.Nodes.Add(new RadTreeNode("New root")); } }
Say you have a button which clears the old nodes and adds a number of new child nodes to the currently selected node. If the selected node is collapsed, the newly added nodes are not visible but the scrollbar updates as if they were. Workaround the issue by using the BeginUpdate/EndUpdate methods of RadTreeView when updating the nodes: radTreeView1.BeginUpdate(); node.Nodes.Clear(); foreach (RadTreeNode treeNode in nodes) { node.Nodes.Add((RadTreeNode)treeNode.Clone()); } radTreeView1.EndUpdate();
How to reproduce: public partial class RadForm1 : Telerik.WinControls.UI.RadForm { private BindingList<TreeViewDataObject> data; public static int Id = 0; public RadForm1() { InitializeComponent(); var theme = new FluentTheme(); ThemeResolutionService.ApplicationThemeName = theme.ThemeName; this.data = new BindingList<TreeViewDataObject>(); for (int i = 1; i <= 5; i += 1) { Id++; TreeViewDataObject root = new TreeViewDataObject() { Id = Id, ParentId = -1, Name = "Root: " + i }; this.data.Add(root); for (int j = 1; j <= 3; j++) { Id++; TreeViewDataObject child = new TreeViewDataObject() { Id = Id, ParentId = root.Id, Name = "Child: " + Id }; this.data.Add(child); for (int K = 1; K <= 5; K++) { Id++; TreeViewDataObject c = new TreeViewDataObject() { Id = Id, ParentId = child.Id, Name = "Child: " + Id }; this.data.Add(c); } } } this.radTreeView1.DataSource = this.data; this.radTreeView1.DisplayMember = "Name"; this.radTreeView1.ParentMember = "ParentId"; this.radTreeView1.ChildMember = "Id"; this.radTreeView1.RelationBindings.Add(new RelationBinding(this.data, "Name", "ParentId", "Id")); this.radTreeView1.ExpandAll(); this.radTreeView1.TreeViewElement.AllowEdit = true; this.radTreeView1.TreeViewElement.AllowAdd = true; this.radTreeView1.TreeViewElement.AllowRemove = true; } } public class TreeViewDataObject { public int Id { get; set; } public int ParentId { get; set; } public string Name { get; set; } } Workaround: Handle the ContextMenuOpening event and add logic for creating a new record in the data source object public partial class RadForm1 : Telerik.WinControls.UI.RadForm { private BindingList<TreeViewDataObject> data; private int id = 0; public RadForm1() { InitializeComponent(); var theme = new FluentTheme(); ThemeResolutionService.ApplicationThemeName = theme.ThemeName; this.data = new BindingList<TreeViewDataObject>(); for (int i = 1; i <= 5; i += 1) { id++; TreeViewDataObject root = new TreeViewDataObject() { Id = id, ParentId = -1, Name = "Root: " + i }; this.data.Add(root); for (int j = 1; j <= 3; j++) { id++; TreeViewDataObject child = new TreeViewDataObject() { Id = id, ParentId = root.Id, Name = "Child: " + id }; this.data.Add(child); for (int K = 1; K <= 5; K++) { id++; TreeViewDataObject c = new TreeViewDataObject() { Id = id, ParentId = child.Id, Name = "Child: " + id }; this.data.Add(c); } } } this.radTreeView1.DataSource = this.data; this.radTreeView1.DisplayMember = "Name"; this.radTreeView1.ParentMember = "ParentId"; this.radTreeView1.ChildMember = "id"; this.radTreeView1.RelationBindings.Add(new RelationBinding(this.data, "Name", "ParentId", "id")); this.radTreeView1.ExpandAll(); this.radTreeView1.ContextMenuOpening += RadTreeView1_ContextMenuOpening; this.radTreeView1.TreeViewElement.AllowEdit = true; this.radTreeView1.TreeViewElement.AllowAdd = true; this.radTreeView1.TreeViewElement.AllowRemove = true; } private void RadTreeView1_ContextMenuOpening(object sender, TreeViewContextMenuOpeningEventArgs e) { TreeViewDefaultContextMenu treeMenu = e.Menu as TreeViewDefaultContextMenu; treeMenu.Items.Remove(treeMenu.AddMenuItem); RadMenuItem item = new RadMenuItem("&New"); item.Click -= Item_Click; item.Click += Item_Click; treeMenu.Items.Insert(2, item); } private void Item_Click(object sender, EventArgs e) { RadTreeNode parent = this.radTreeView1.SelectedNode; if (parent == null || parent.DataBoundItem == null) { return; } this.id++; BindingList<TreeViewDataObject> data = this.radTreeView1.DataSource as BindingList<TreeViewDataObject>; TreeViewDataObject newObject = new TreeViewDataObject() { Id = this.id, ParentId = ((TreeViewDataObject)parent.DataBoundItem).Id, Name = "New Name" }; parent.Expanded = true; data.Add(newObject); } } public class TreeViewDataObject { public int Id { get; set; } public int ParentId { get; set; } public string Name { get; set; } }