Windows Forms TreeView - Bind hierarchical recursive datasource - c#

I'm converting a WPF client in Windows Forms and I got some problems trying to replicate the TreeView control structure.
In the first project I have a custom factory that builds a structure starting from an input string that is basically a XML.
The return type is a collection.
Custom TreeNode:
public class TreeViewNode
{
public TreeViewNode() { }
public DocumentKey DocKey { get; set; }
public string Text { get; set; }
public IList<TreeViewNode> Children { get; set; }
}
Factory:
public class TreeViewFactory
{
public IList<TreeViewNode> GetSctructure(DocumentKey docKey, string structure, bool loadAllParents)
{
XDocument xmlDocstructure = CommonXmlValueParser.GetXDocument(structure);
var parentsNodes = (from item in xmlDocstructure.Descendants("structure_item")
where (CommonXmlValueParser.GetAttribute(item, "level") == "1")
select new TreeViewNode
{
Text = GetNodeText(item),
DocKey = new DocumentKey()
{
Bank = docKey.Bank,
Ud = int.Parse(CommonXmlValueParser.GetElement(item.Element("ud"))),
Master = int.Parse(CommonXmlValueParser.GetElement(item.Element("master"))),
NVig = int.Parse(CommonXmlValueParser.GetElement(item.Element("nvig"))),
Subjects = docKey.Subjects
},
Children = GetChildrenNodes(item, 2, docKey.Bank)
}).ToList();
return parentsNodes;
}
private IList<TreeViewNode> GetChildrenNodes(XElement element, int level, int dataBank)
{
var childrenNodes = (from item in element.Descendants("structure_item")
where (CommonXmlValueParser.GetAttribute(item, "level") == level.ToString())
select new TreeViewNode
{
Text = GetNodeText(item),
DocKey = new DocumentKey()
{
Bank = dataBank,
Ud = int.Parse(CommonXmlValueParser.GetElement(item.Element("ud"))),
Master = int.Parse(CommonXmlValueParser.GetElement(item.Element("master"))),
NVig = int.Parse(CommonXmlValueParser.GetElement(item.Element("nvig"))),
},
Children = GetChildrenNodes(item, level + 1, dataBank)
}).ToList();
return childrenNodes;
}
}
Binding:
void CreateTree(object tree, EventArgs e)
{
//...
TreeViewFactory treeFactory = new TreeViewFactory();
var documentStructure = treeFactory.Structure(document.DocumentKey, document.XmlStructure, true);
this.tabMainControl.document.SetTreeViewStructureNodes(documentStructure);
}
public void SetTreeViewStructureNodes(IList<TreeViewNode> nodes)
{
this.treeView.ItemsSource = nodes;
}
Update:
I made the TreeViewNode derive from TreeNode and changed the method SetTreeViewStructureNodes in:
private TreeView SetTreeViewStructureNodes(IList<TreeViewNode> nodes)
{
TreeView treeView = new TreeView();
treeView.Nodes.AddRange(nodes.ToArray());
return treeView;
}
still that doesn't achieve my goal as it's still not rendered...
In Windows Forms as far as I know it's not possible to associate a sort of datasource that is a whatever type collection (implements IEnumerable).
Apart from using 3rd party components, how can I solve my problem. My experience on WinForms is pretty short and just when I learnt to manage much better WPF they decided to shift it :(
Appreciate all your help, regards.
Update2:
Piece of WinForms User Control where treeView is filled:
TreeView treeView = (TreeView)documentViewControl.Controls["treeViewStructure"];
TreeViewFactory treeFactory = new TreeViewFactory();
var documentStructure = treeFactory.GetStructure(document.DocumentKey, document.XmlStructure, true);
treeView = this.SetTreeViewStructureNodes(documentStructure);
Basically I'm moving from an UC to another. Both of them are part of 2 Tabs, children of a TabControl.

(Answered by the OP as a question edit. Converted to a community wiki answer. See Question with no answers, but issue solved in the comments (or extended in chat) )
The OP wrote:
Actually I got that on my own. The idea is to mutuate the ricursive idea, creating one TreeNode from the collection (IList) of TreeViewNodes. Problem 1: recursion Problem 2: how to mantain the DocKey custom property
private TreeNode[] GetTreeViewNodes(IList<TreeViewNode> nodes)
{
IList<TreeNode> returnedNodes = new List<TreeNode>();
foreach (var item in nodes)
{
TreeNode node = new TreeNode(item.Text, this.GetTreeViewNodes(item.Children));
node.Tag = item.DocKey;
returnedNodes.Add(node);
}
return returnedNodes.ToArray();
}
And the code required for the treeview becomes this one:
this.treeView.Nodes.Clear();
this.treeView.Nodes.AddRange(this.GetTreeViewNodes(documentStructure));

Related

How to hide a specific node on treeview in c# winform? [duplicate]

I'm having a windows form with a tree view control. This tree view has a Root node and 2 child nodes. My requirement is i need to hide the first child node.
Is it possible to make visible false that particular child nod
Yes you could inherit from tree node and create your own behaviour. Like so.
public class RootNode : TreeNode
{
public List<ChildNode> ChildNodes { get; set; }
public RootNode()
{
ChildNodes = new List<ChildNode>();
}
public void PopulateChildren()
{
this.Nodes.Clear();
var visibleNodes =
ChildNodes
.Where(x => x.Visible)
.ToArray();
this.Nodes.AddRange(visibleNodes);
}
//you would use this instead of (Nodes.Add)
public void AddNode(ChildNode node)
{
if (!ChildNodes.Contains(node))
{
node.ParentNode = this;
ChildNodes.Add(node);
PopulateChildren();
}
}
//you would use this instead of (Nodes.Remove)
public void RemoveNode(ChildNode node)
{
if (ChildNodes.Contains(node))
{
node.ParentNode = null;
ChildNodes.Remove(node);
PopulateChildren();
}
}
}
public class ChildNode : TreeNode
{
public RootNode ParentNode { get; set; }
private bool visible;
public bool Visible { get { return visible; } set { visible = value;OnVisibleChanged(): } }
private void OnVisibleChanged()
{
if (ParentNode != null)
{
ParentNode.PopulateChildren();
}
}
}
No, there is no way to make node invisible. You should remove it instead of making invisible. And later you will have to add it back into its original position.
If you are loading a treeview with a sitemap file, then another approach is to do something like this. Here the user's credentials have been read from a DB and written to a cookie.
private void ManageTreeMenu()
{
var value = Utilities.Cookies.GetCookieValue("IsAdmin");
bool.TryParse(value, out var isAdmin);
var dir = Server.MapPath("~");
File.Delete(dir + "Web.sitemap");
if (isAdmin)
File.Copy(dir + "WebAdmin.sitemap", dir + "/Web.sitemap");
else
File.Copy(dir + "WebOper.sitemap", dir + "/Web.sitemap");
}
You'd have to do this again if the user's role was changed in the program. I have only verified this in Visual Studio, not in a deployed web application. Caveat emptor.

Binding to a treeview programatically not working in UWP

I'm following this article to try and programmatically bind data to a treeview (I'm on 1903).
In a brand new UWP app, I have the following code behind:
public MainPage()
{
this.InitializeComponent();
var items = new List<Item>();
var rootItem = new Item();
rootItem.Name = "Root Item";
rootItem.Children.Add(new Item() { Name = "test child 1" });
items.Add(rootItem);
var treeView = new TreeView();
treeView.ItemsSource = items;
stackPanel.Children.Add(treeView);
}
Item looks like this:
public class Item
{
public string Name { get; set; }
public ObservableCollection<Item> Children { get; set; } = new ObservableCollection<Item>();
public override string ToString()
{
return Name;
}
}
This appears to be the exact structure outlined in the above article. However, when I run the application, I get this:
My guess is that I need to do, or set something that tells this treeview, or the collection that it has children - but I can't see what that might be.
You should create an ItemTemplate as explained in the docs.
You could use the XamlReader class to do this programmatically. Something like this:
const string Xaml = "<DataTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"><TreeViewItem ItemsSource=\"{Binding Children}\" Content=\"{Binding Name}\"/></DataTemplate>";
treeView.ItemTemplate = XamlReader.Load(Xaml) as DataTemplate;
If you use C# to build a TreeView, I recommend adding a TreeViewNode using traversal.
Due to the lack of instructions, TreeView does not automatically handle the Children of the Item. In the documentation you provide, the TreeView has a DataTemplate directive, so the children can render.
You can change code like this:
public MainPage()
{
this.InitializeComponent();
var items = new List<Item>();
var rootItem = new Item();
rootItem.Name = "Root Item";
rootItem.Children.Add(new Item() { Name = "test child 1" });
items.Add(rootItem);
var treeView = new TreeView();
foreach (var root in items)
{
var rootNode = new TreeViewNode() { Content = root.Name };
if (root.Children.Count > 0)
{
foreach (var child in root.Children)
{
rootNode.Children.Add(new TreeViewNode() { Content = child.Name });
}
}
treeView.RootNodes.Add(rootNode);
}
stackPanel.Children.Add(treeView);
}
Best regards.

C# Recursive delete nodes

I created a class for hierarchical data. From this class I generate custom treeview. Before sending data to treeview I need delete a branch that does not end with instance with HaveData = true
public class Data
{
public List<Data> Children
{
get;
set;
}
public bool HaveData
{
get;
set;
}
}
Treeview before:
1 First
1.1 Item
1.1.1 Item (HaveData = false)
1.2 Item
1.2.1 Item (HaveData = true)
...
I need:
1 First
1.2 Item
1.2.1 Item (HaveData = true)
...
How to go through all nodes and remove only those that ending HaveData = false?
Thank you.
Here is one recursive way to do it:
public void DeleteBranchWithNoData()
{
var toBeRemoved = new List<Data>();
foreach(var child in Children)
{
if(!child.HaveData && (child.Children == null || !child.Children.Any()))
{
toBeRemoved.Add(child);
}
else
{
child.DeleteBranchWithNoData();
}
}
Children.RemoveAll(d => toBeRemoved.Contains(d));
}
Call this method from the top node of the branch you want to delete.
You can see a live demo on rextester.
Make HaveData be true if the element has data OR any of its children have data.
Delete any nodes within the tree where HaveData is false.
???
Profit.

What would I use when in need of a dynamicly adjusting multidemnsional list?

I have a list that I want to function as a dynamically expanding tree-view, but if I am to have multiple lists inside of a list I need to declare that in the initial list's definition. What am I missing to accomplish this and is there something better to use to accomplish my goal?
For the sake of context: I am trying to populate it like a tree view so that I may replicate the registry within my application.
Use the correct type for your scenario.
In your case, this is a class TreeNode like so:
public class TreeNode
{
private readonly List<TreeNode> _children = new List<TreeNode>();
public TreeNode(string name, params TreeNode[] children)
{
Name = name;
_children.AddRange(children);
}
public List<TreeNode> Children { get { return _children; } }
public string Name { get; set; }
}
Assume the following tree:
Root
+ Child1
+ Child1a
+ Child1b
+ Child2
+ Child2a
+ Child2aA
+ Child2aB
+ Child2b
You would create it like so:
var root = new TreeNode("Root",
new TreeNode("Child1",
new TreeNode("Child1a"),
new TreeNode("Child1b")),
new TreeNode("Child2",
new TreeNode("Child2a",
new TreeNode("Child2aA"),
new TreeNode("Child2aB")),
new TreeNode("Child2b")));

How to make a child node visible = false in a Treeview Control

I'm having a windows form with a tree view control. This tree view has a Root node and 2 child nodes. My requirement is i need to hide the first child node.
Is it possible to make visible false that particular child nod
Yes you could inherit from tree node and create your own behaviour. Like so.
public class RootNode : TreeNode
{
public List<ChildNode> ChildNodes { get; set; }
public RootNode()
{
ChildNodes = new List<ChildNode>();
}
public void PopulateChildren()
{
this.Nodes.Clear();
var visibleNodes =
ChildNodes
.Where(x => x.Visible)
.ToArray();
this.Nodes.AddRange(visibleNodes);
}
//you would use this instead of (Nodes.Add)
public void AddNode(ChildNode node)
{
if (!ChildNodes.Contains(node))
{
node.ParentNode = this;
ChildNodes.Add(node);
PopulateChildren();
}
}
//you would use this instead of (Nodes.Remove)
public void RemoveNode(ChildNode node)
{
if (ChildNodes.Contains(node))
{
node.ParentNode = null;
ChildNodes.Remove(node);
PopulateChildren();
}
}
}
public class ChildNode : TreeNode
{
public RootNode ParentNode { get; set; }
private bool visible;
public bool Visible { get { return visible; } set { visible = value;OnVisibleChanged(): } }
private void OnVisibleChanged()
{
if (ParentNode != null)
{
ParentNode.PopulateChildren();
}
}
}
No, there is no way to make node invisible. You should remove it instead of making invisible. And later you will have to add it back into its original position.
If you are loading a treeview with a sitemap file, then another approach is to do something like this. Here the user's credentials have been read from a DB and written to a cookie.
private void ManageTreeMenu()
{
var value = Utilities.Cookies.GetCookieValue("IsAdmin");
bool.TryParse(value, out var isAdmin);
var dir = Server.MapPath("~");
File.Delete(dir + "Web.sitemap");
if (isAdmin)
File.Copy(dir + "WebAdmin.sitemap", dir + "/Web.sitemap");
else
File.Copy(dir + "WebOper.sitemap", dir + "/Web.sitemap");
}
You'd have to do this again if the user's role was changed in the program. I have only verified this in Visual Studio, not in a deployed web application. Caveat emptor.

Categories