.NET TreeView: Attach objects to TreeNodes - c#

In a .NET treeview you can create nodes, subnodes and elements.
All I seem to be able to do is give them names.
But how can I attach information (any object) to an element?

Use the Tag property of the TreeNode to attach an arbitrary object to it.
This doesn't affect the TreeView in any way. It is especially useful in your event handlers, (e.g. AfterSelect) because allows you to refer back to one of "your" objects from the TreeNode that is referenced.
Remember that Tag is of type Object, so you'll want to be careful how you access it. Here's some sample code to show how (I feel) it is best used:
public Form1()
{
InitializeComponent();
theTree.AfterSelect += (sender, args) => ShowSelectedNode();
}
private void ShowSelectedNode() {
var node = theTree.SelectedNode;
var viewable = node.Tag as IViewable;
if (viewable != null) {
viewable.View(this);
}
}
Note that this is the correct use of the as operator.

Related

How to implement a dependency tree of updating objects?

Edit: I am not looking for an implementation but just for some keywords to search and methodologies to get me started.
I am struggling with generating a dependency tree where child nodes are updated by an external process and the requirement is to update all parent nodes of updated child nodes.
Example: Imagine a tree such as this: O [parent], O(l) [left child], O(r) [right child], O(ll), O(lr), O(rl), and O(rr). O(ll), O(lr), O(rl), and O(rr) have reference to a data collection that is updated at random intervals).
I want to implement a pull process, where at certain intervals a process checks whether O is updated. "Updated" is defined as being updated when all child nodes are updated, else just the cached value(result) of that node is used. The pull process's job is to ensure that O is updated when any of the child nodes is not updated. This means that the process needs to traverse the tree and check whether O(ll), O(lr), O(rl), and O(rr) are updated. If the data collection, those child nodes reference, are updated since the last update of those child nodes then those child nodes need to be updated as function of the changed data collection. If the data collection is updated and hence the child nodes O(ll), O(lr), O(rl), and O(rr) are updated as well and this means that O(l) and O(r) also need to be updated and subsequently O will also be updated. Each child node is input to its parent node.
The complexity here is that each child node is shared among different trees, meaning, a child node of one tree can also be any child node of another tree. The purpose of this structure is to avoid the re-calculation of a child node when it is already up-to-date. The child nodes are shared if different trees implement a child node with the exact same functionality (function and parameterization) as the already existing child node.
I am stuck with the design of this structure and how to go about implementing it. I have not provided code yet because I am stuck with the design thought process. Essentially each child is function and depends on dependent functions itself.
What makes me wonder is whether C# offers the ability to decorate methods and classes in order to simplify the checking of whether a node is updated or not. Also does lazy evaluation play a role in this process at all?
I suggest defining a class that keeps track whether its children have been updated via a flag, e.g. a Boolean named Dirty. A node in the tree can tell its parents to become dirty by raising an event. When a node returns its own value, it should check the flag and recompute its own value only when needed. When recomputing, it should check the Value of each child, each of which will then check its own dirty flag, and so on, recursively.
class Node<T>
{
event EventHandler Changed;
private T _value;
private bool _dirty = true;
private List<Node<T>> _children = new List<Node<T>>();
public void AddChild(Node<T> child)
{
child.Changed += (s,e) => _dirty = true;
_children.Add(child);
}
protected void OnChanged()
{
if (Changed != null) Changed(this, new EventArgs());
}
public T Value
{
get
{
if (_dirty)
{
this.Value = ComputeValueFromChildren();
_dirty = false;
}
return _value;
}
set
{
_value = value;
OnChanged();
}
}
private T ComputeValueFromChildren()
{
var values = _children.Select( child => child.Value );
//Return the new value based on the children
}
}

TreeView duplicating root nodes, despite there being a single root in Nodes array

I'm currently using TreeView to display a file tree for visualising diffs for a source control project. I have a "Diff" method that recursively edits an existing node in the root "Nodes" array in the TreeView, and then updates the tree afterward.
However, I've encountered an issue where the root node will duplicate for seemingly no reason, despite the debugger telling me there's a single element in the "Nodes" array at the very root of the TreeView, with no indication of an error.
I've already attempted to use "Nodes.Clear()" and then re-add the offending node, however even when clearing the array, the duplicate persists, (even when Nodes.Count is 0). I've also tried using BeginUpdate() and EndUpdate(), but to no avail.
Here's an MCVE:
public partial class BrokenControl : TreeView
{
public BrokenControl()
{
InitializeComponent();
}
public void Go(object sender, EventArgs e)
{
Nodes.Add("Root");
Nodes[0] = RecursiveEdit(Nodes[0]);
Update();
}
//This function simply recursively edits the Nodes array.
int iterations = 10;
private TreeNode RecursiveEdit(TreeNode node)
{
node.Nodes.Add(iterations.ToString());
iterations--;
if (iterations<=0)
{
return node;
}
RecursiveEdit(node.Nodes[0]);
return node;
}
}
As mentioned, I only expect there to be a single node on the TreeView when it's updated, but instead I get a duplicate node containing duplicated contents of the first.
I managed to fix the issue by using a workaround: instead of directly manipulating the Root node, saving a copy and editing, then clearing and readding, solved my issue.
Still do not know what was causing the dupe even when Nodes.Count was 0 and 1, however this seems to work.
Corrected MCVE:
public partial class BrokenControl : TreeView
{
...
public void Go(object sender, EventArgs e)
{
Nodes.Add("Root");
TreeNode savedNode = RecursiveEdit(Nodes[0]);
//This fixes it.
Nodes.Clear();
Nodes.Add(savedNode);
Update();
}
...
}

Searching a Child Control

I am using coded ui test with my windows store app.
My control hierarchy is :
UIPearsonPOCCommonViewFlipViewItem (XAMLFlipViewItem - > UIWebViewPane (XAMLWebViewPane) - > Rest of the content.
For rest of the child controls, there is no specific automation id or unique names and they look like html control for e.g. refer the image appended.
I want to iterate over the children of UIWebViewPane and reach to child DIV which is having the innerText.
I am relatively very new to coded ui test. I am unable to iterate over the children of UIWebViewPane(XAMLWebViewPane)
If the child control's inner text is unique, you could always search on that using the parent control in the definition. Ex:
public HtmlControl child()
{
HtmlControl parent = new HtmlControl(browser);
parent.SearchProperties["id"] = "[my id]";
HtmlControl child = new HtmlControl(parent);
child.SearchProperties["innerText"] = "[the inner text]";
return child;
}
If you truly want to iterate through, then you'll have to crawl the structure using the .GetParent() and .GetChildren() methods of the UITestControl class.
public HtmlControl child()
{
//First, we create an empty HtmlControl to return.
HtmlControl result = new HtmlControl()
//Specify the parent and get a collection of the children (this only goes one level,
// so if you have to go deeper, you'll have to nest your foreach loops and get
// children of the children, etc.
HtmlControl parent = new HtmlControl(browser);
parent.SearchProperties["id"] = "[my id]";
UITestControlCollection children = parent.GetChildren();
foreach (UITestControl child in children)
{
// If the child has the text you're looking for, then assign it to the result
// object and break the loop.
if (child.GetProperty("InnerText").ToString().Equals(searchTerm))
{
result = (HtmlControl)child;
break;
}
}
return result;
}
Personally, I'd try the first option. Your best bet, though, is to ask (politely) for the developer to add some unique and static tags to the HTML.

Copying a TabItem with an MVVM structure

This is an attempt to expand on this question. In my WPF program I've been cloning tabItems by using an XamlWriter in a function called TrycloneElement. I originally found this function here, but the function can also be viewed in the link to my previous question.
Now that I am beginning to worry about functionality inside my program, I found that the TrycloneElement function does not replicate any code-behind functionality assigned to the tabItem that it is cloning.
Because of High Core's link and comment on my earlier question I decided to start implementing functionality on my tabItems through Data Binding with my ViewModel.
Here is a sample of a command that I've implemented:
public viewModel()
{
allowReversing = new Command(allowReversing_Operations);
}
public Command AllowReversing
{
get { return allowReversing; }
}
private Command allowReversing;
private void allowReversing_Operations()
{
//Query for Window1
var mainWindow = Application.Current.Windows
.Cast<Window1>()
.FirstOrDefault(window => window is Window1) as Window1;
if (mainWindow.checkBox1.IsChecked == true) //Checked
{
mainWindow.checkBox9.IsEnabled = true;
mainWindow.groupBox7.IsEnabled = true;
}
else //UnChecked
{
mainWindow.checkBox9.IsEnabled = false;
mainWindow.checkBox9.IsChecked = false;
mainWindow.groupBox7.IsEnabled = false;
}
}
*NOTE: I know that I cheated and interacted directly with my View in the above code, but I wasn't sure how else to run those commands. If it is a problem, or there is another way, please show me how I can run those same commands without interacting with the View like I did.
Now to the question:
After changing my code and adding the commands to my ViewModel, the TrycloneElement function no longer works. At run time during the tab clone I receive an XamlParseException on line, object x = XamlReader.Load(xmlReader); that reads:
I'm fine with ditching the function if there is a better way and I don't need it anymore. But ultimately, how do I take a tabItem's design and functionality and clone it? (Please keep in mind that I really am trying to correct my structure)
Thank you for your help.
Revision of Leo's answer
This is the current version of Leo's answer that I have compiling. (There were some syntax errors)
public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues) })
select DependencyPropertyDescriptor.FromProperty(pd)
into dpd
where dpd != null
select dpd.DependencyProperty).ToList();
}
public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
FrameworkElement controlToCopy)
{
foreach (var dependencyValue in GetAllProperties(controlToCopy)
.Where((item) => !item.ReadOnly)
.ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
{
controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
}
}
Here is my example of a properly-implemented dynamic TabControl in WPF.
The main idea is that each Tab Item is a separate widget that contains its own logic and data, which is handled by the ViewModel, while the UI does what the UI must do: show data, not contain data.
The bottom line is that all data and functionality is managed at the ViewModel / Model levels, and since the TabControl is bound to an ObservableCollection, you simply add another element to that Collection whenever you need to add a new Tab.
This removes the need for "cloning" the UI or do any other weird manipulations with it.
1.) To fix that XamlParseException, make sure you have a public constructor like an empty one, you probably defined a constructor and when you tried to serialize that object and deserialize it can't. You have to explicitly add the default constructor.
2.) I don't like the word clone, but I'd say, when they want to copy. I'll manually create a new tab item control then do reflection on it.
I have this code that I made
public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] {new PropertyFilterAttribute(PropertyFilterOptions.SetValues)})
select DependencyPropertyDescriptor.FromProperty(pd)
into dpd where dpd != null select dpd.DependencyProperty).ToList();
}
public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
FrameworkElement controlToCopy)
{
foreach (var dependencyValue in GetAllProperties(controlToCopy)
.Where((item) => !item.ReadOnly))
.ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
{
controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
}
}
So it would be like
var newTabItem = new TabItem();
newTabItem.CopyPropertiesFrom(masterTab);

How to pass string parameter of a method from one class to another class in same namespace?

Little silly question, but got stuck for a long time.
I have written two classes one which is a Form (TreeDisplay class) and other which contains buiseness logic (MyTreeNode class).
TreeDisplay class contains browse button to select a file pass it to a method initiatingTree(string filename) which is in MyTreeNode class.
Now I have to pass this string parameter filename to MyTreeNode class. When I run my code the XML file which I have selected is shown in the textbox but not in treeview.
I have written the part of code but it is throwing NullReferenceException(Object reference not set to an instance of an object).
When the whole code was writeen in Form.cs the code was running successfully but on separating the business logic the exception has occured.
Can you please tell me where I am wrong?
This is the code in Browse button of TreeDisplay class (My main form):
if (open.ShowDialog(this) == DialogResult.OK)
{
txtFileName.Text = open.FileName;
MytreeNodeClass treenodeClass = new MytreeNodeClass();
treenodeClass.initiatingTree(open.FileName,treeView1);
}
Here is the code of initiatingTree() in my MyTreeNode class:
public class MytreeNodeClass
{
private readonly System.Windows.Forms.TextBox txtFileName;
private TreeView treeView1;
private readonly ToolStripStatusLabel toolStripStatusLabel1;
public string Filename
{
get { return filename; }
}
protected string filename;
public MytreeNodeClass()
{
}
public void initiatingTree(string nameofFile,TreeView treeView1)
{
try
{
//Create XML document & load the XML file.
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(nameofFile);
treeView1 = new System.Windows.Forms.TreeView();
treeView1.Nodes.Clear();
if (xmlDocument.DocumentElement != null)
{
TreeNode treeNodedoc = new TreeNode(xmlDocument.DocumentElement.Name);
treeView1.Nodes.Add(treeNodedoc);
}
On using breakpoint treeView1.Nodes.Clear(), the code comes out from this line and enters the catch block throwing NullReferenceException.
Please help to find root cause of exception. Thanks.
What is the value of treeView1 at your breakpoint?
I suggest this may be null, as the reference isn't available in your Business Logic Layer.
Updated with sample code:
public void initiatingTree(string nameofFile, TreeView treeView1)
{
try
{
//Create XML document & load the XML file.
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(nameofFile);
treeView1.Nodes.Clear();
if (xmlDocument.DocumentElement != null)
{
TreeNode treeNodedoc = new TreeNode(xmlDocument.DocumentElement.Name);
treeView1.Nodes.Add(treeNodedoc);
}
}
}
and where you call this:
if (open.ShowDialog(this) == DialogResult.OK)
{
txtFileName.Text = open.FileName;
MytreeNodeClass treenodeClass = new MytreeNodeClass();
treenodeClass.initiatingTree(open.FileName, treeView1);
}
As c.k said.
You don't have access to the usercontrols from your business layer as a standard.
May i suggest that you keep you control code in the gui code behind for simplicity.
An example how to sort your code
(Gui Layer) Codebehind for control interaction
Business Layer for data manipulation
Data Layer for database manipulations and such
Initialize the treeView1 object before accessing it.
I guess you copied and pasted, right?
Anyways here's a little note:
When you drag controls on a form in VS Designer, vs generates code for these controls "usually in InitializeComponent()". VS writes the references in your class (e.g form1.cs) like the following:
private System.Windows.Forms.TreeView treeView1;
this is just declaring a variable of type System.Windows.Forms.TreeView this variable (itself) is stored on the stack, because it's just a reference, a reference that doesn't point to any actual heap objects till now (i.e NullReference). Now this reference is useless and you can't use it unless you initialize it and tell it to point to a real object on the heap, and that's what VS does for you in the designer, it simply adds the next line to InitializComponents:
this.treeView1 = new System.Windows.Forms.TreeView();
And this is the line that you forgot to copy to the other class, which left your reference uninitialized (i.e still NullReference), hence when you tried to use it, a NullReferenceException was thrown. I guess this is your problem or at least this is how i could understand it. Forgive me if i failed to understand you!

Categories