Adding or Removing a level to a TreeNode - winforms

I was wondering if it was possible with a TreeView in a windows form to add or remove a level?
For example:
my treeview is like this to begin with:
ParentNode
| Child1
| Child2
if user clicks on a button to add a level to Child2 it becomes:
ParentNode
| Child1
| | Child1.1
There is a a Node.Level function but only usable to get the level and not to set it.
EDIT:
The nodes are built automatically, the level is assigned depending on the style of an excel cell. The problem is, it happens that the created node is not at it's correct place because the excel file is not well made. So I see 2 options o resolve this problem:
1- the user modifies the excel file directly
2- I create a Move Left Move Right button on a selection of nodes.
I'd like to offer the 2nd possibility.
Here's the code I used to build the nodes:
public static void AddNodes(Excel.Application app,
TreeView treeView)
{
Excel.Range selection = app.Selection;
ArrayList style = new ArrayList();
TreeNode parentNode = treeView.SelectedNode;
//Selected Node => Last used node
for (int i = 1; i <= selection.Rows.Count; i++)
{
TreeNode tn;
int fontSize = Convert.ToInt32(selection.Cells[i].Font.Size);
if (!style.Contains(fontSize))
{
style.Add(fontSize);
}
else if (style[style.Count - 1].Equals(fontSize))
{
try
{
treeView.SelectedNode = treeView.SelectedNode.Parent;
}
catch (Exception x)
{
ErrorBox(x);
}
}
else
{
int indexPreviousCellofSameColor = style.IndexOf(fontSize);
//Select TN parent
for (int j = 1; j <= (style.Count - indexPreviousCellofSameFont); j++)
{ treeView.SelectedNode = treeView.SelectedNode.Parent; }
style.RemoveRange(indexPreviousCellofSameFont + 1, style.Count - indexPreviousCellofSameFont - 1);
}
if (selection.Cells[i].Value2 == null)
{
//if empty cell, do something ... or nothing
treeView.SelectedNode = treeView.SelectedNode.LastNode;
}
else
{
//Add new TN to parent - TN object corresponds to excel cell
tn = new TreeNode()
{
Text = selection.Cells[i].Value2,
Tag = selection.Cells[i],
};
treeView.SelectedNode.Nodes.Add(tn);
tn.ToolTipText = tn.Level.ToString();
//selected TN => created TN
treeView.SelectedNode = tn;
}
}
}

I had to change my answer completely to the changed question.
This seems to do the job in my tests. It moves the selected node to a new level, under the one that was just above it.
It needs more checks offcourse to make sure your not moving nodes to oblivion...
private void button1_Click(object sender, EventArgs e)
{
TreeNode selected = treeViewFilter.SelectedNode;
TreeNode parent = selected.Parent;
// find the node just above the selected node
TreeNode prior = parent.Nodes[selected.Index - 1];
if (parent != prior)
{
treeViewFilter.Nodes.Remove(selected);
prior.Nodes.Add(selected);
}
}

Related

An unhandled exception of type 'System.InvalidCastException' in winform

I have project that I click Button and it's will create a new PictureBox (pb) on a PictureBox1. And when I choose item on a combobox and PictureBox (pb) will appear in the position I want and the problem appears. How can I fix it or use "pb" in void comboBox3_SelectedIndexChanged. Thank you.
private void btaddagv_Click(object sender, EventArgs e)
{
AddNewPictureBox();
}
public System.Windows.Forms.PictureBox AddNewPictureBox()
{
System.Windows.Forms.PictureBox pb = new System.Windows.Forms.PictureBox();
pictureBox1.Controls.Add(pb);
pb.Name = "STT" + tbAdd.Text;
pb.Image = Image.FromFile("AGV-1.jpg");
pb.Height = 30;
pb.Width = 40;
pb.SizeMode = PictureBoxSizeMode.Zoom;
pb.Location = new System.Drawing.Point(tdx, 500);
tdx = tdx + 200;
return pb;
}
private void comboBox3_SelectedIndexChanged(object sender, EventArgs e)
{
if(comboBox3.SelectedItem == "A")
{
PictureBox pb = (PictureBox)sender;
pb.Location = lbA.Location;
}
}
And here is an error
If we make an assumption on this line:
pb.Name = "STT" + tbAdd.Text;
that tbAdd.Text contains the A, B, C, D etc that you're checking for here:
if(comboBox3.SelectedItem == "A")
Then your SelectedIndexChanged event handler should be:
private void comboBox3_SelectedIndexChanged(object sender, EventArgs e)
{
string selected = comboBox3.SelectedItem.ToString();
if (!string.IsNullOrWhitespace(selected))
{
PictureBox pb = picturebox1.Controls.OfType<PictureBox>().Where(p => p.Name == $"STT{selected}").FirstOrDefault();
if (pb != null)
{
Label lb = picturebox1.Controls.OfType<Label>().Where(l => l.Name == $"lb{selected}").FirstOrDefault();
if (lb != null)
pb.Location = lb.Location;
}
}
}
I've made some extra assumptions here. First I've assumed that the "lbA" control you're referring to is a Label. The second assumption is that lbA and the other controls are all share the same parent (picturebox1)
What the above is doing is getting the value of the SelectedItem (if we use your example with the letter A) then attempts to find any PictureBox controls that have the name STTA. If it finds one it looks for a Label on the same parent called lbA. If that exists then it moves the picturebox control to the location of the label.

How to get the child control inside a TreeviewItem?

I customed my TreeViewItem to be a StackPanel with image and textblock inside; I'd like to get a reference to the TextBlock inside. For the codes below node is of type TreeviewItem and I am surechildrenCound =3 which could be StackPanel image textblock! But it can not find any TextBlock inside. I never see any console output and object _itemToMovereturns null
TreeViewItem node = UIHelper.FindVisualParent<TreeViewItem>(e.OriginalSource as FrameworkElement);
var child = VisualTreeHelper.GetChild(node, 0);
int childrenCount = VisualTreeHelper.GetChildrenCount(child);
for (int i = 0; i < childrenCount; i++)
{
TextBlock vc = VisualTreeHelper.GetChild(child, i) as TextBlock;
if (vc != null)
{
Console.WriteLine("ggggggggggggggggggggggggggggggggggggggggggggggg");
_itemToMove = vc.Text as object;
}
}
Console.WriteLine(childrenCount+";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
It may be that your TextBlock is buried deeper than you think. I've always had success using the following helper which is generic enough to be used elsewhere in the app.
public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
if (obj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child is T)
{
return (T)child;
}
T childItem = FindVisualChild<T>(child);
if (childItem != null) return childItem;
}
}
return null;
}
I think I got this from a similar question from StackOverflow.
As Michael T said, TreeViewItem is also a child of a TreeViewItem. I ran into this old question because I wanted only the visualtree elements of a single TreeViewItem and the so many times used by me FindVisualChild function wasn't this time useful.
So I modified this function to retrieve elements of TreeVieItem excluding the TreeViewItems inside. The function is based on the fact that any hierarchy is defined by nodes, and asumed that a node has a NodeType that is not the type of the children. However, you can go deep in the tree as many levels as you want (HierachyLevels param).
The function also search for specific type and name of the elements.
Public Sub FindChildGroup(Of T As DependencyObject, H As DependencyObject)(Parent As DependencyObject _
, ChildName As String _
, ByRef OutputList As List(Of T) _
, Optional HierachyLevels As Integer = 0)
Dim childrenCount As Integer = VisualTreeHelper.GetChildrenCount(Parent)
For i As Integer = 0 To childrenCount - 1
'Analyze child
Dim child = VisualTreeHelper.GetChild(Parent, i)
'Is node?
Dim IsNode = TypeOf child Is H
If IsNode And HierachyLevels > 0 Or TypeOf child IsNot H Then
Dim child_Test As T = TryCast(child, T)
If child_Test IsNot Nothing Then
'should be included in the list?
Dim child_Element As FrameworkElement = TryCast(child_Test, FrameworkElement)
If child_Element.Name Like ChildName Then
OutputList.Add(child_Test)
End If
End If
'Go down next level
If TypeOf child Is H Then HierachyLevels -= 1
FindChildGroup(Of T, H)(child, ChildName, OutputList, HierachyLevels)
If TypeOf child Is H Then HierachyLevels += 1
End If
Next
End Sub

string comparion with the innertext of a childnode

In the following code, I am trying to do a text parsing by using a streamreader. This is to get the email address from the text file. If i have no email address, combobox is left blank (index = -1). If i have a email that is found in my xml file, then i will select it. Else, i will add a node to my xml file with the new email address.
code:
private void Textparsing()
{
using (StreamReader sr = new StreamReader(Masterbuildpropertiespath))
{
while (sr.Peek() >= 0)
if (line.StartsWith("Builder_Email:"))
{
string[] fields = line.Split('\t');
string builderemail = fields[3];
XmlDocument emailparse = new XmlDocument();
emailparse.LoadXml(#"C:\GUI\buildermanageremail.xml");
XmlNodeList emailnode = emailparse.GetElementsByTagName("value");
if (string.IsNullOrEmpty(builderemail))
comboBox1.SelectedIndex = -1;
else
foreach (XmlNode node in emailnode)
{
if (builderemail == node.InnerText)
{
int count = emailparse.SelectNodes("email/builderemail/builder").Count;
count--;
comboBox1.SelectedIndex = count;
}
else
{
//create main node
XmlNode abc = emailparse.CreateNode(XmlNodeType.Element, "builder", null);
//create the first child node
XmlNode value = emailparse.CreateElement("value");
//set the value
value.InnerText = builderemail;
// add childes to father
//node.AppendChild(id);
abc.AppendChild(value);
// find the node we want to add the new node to
XmlNodeList l = emailparse.GetElementsByTagName("builderemail");
// append the new node
l[0].AppendChild(abc);
// save the file
emailparse.Save(#"C:\GUI\buildermanageremail.xml");
//then we populate the new updated xml file into the drop down list:
PopulateDDLFromXMLFile();
int count = emailparse.SelectNodes("email/builderemail/builder").Count;
count--;
comboBox1.SelectedIndex = count;
}
}
}
However, I get an XmlException (Data at the root level is invalid. Line 1, position 1.) at this line:
emailparse.LoadXml(#"C:\GUI\buildermanageremail.xml");
Why is that so?
my xmlfile:
<?xml version="1.0" encoding="utf-8"?>
<email>
<builderemail>
<builder>
<value>abc#123.com</value>
</builder>
<builder>
<value>Others</value>
</builder>
</builderemail>
<manageremail>
<manager>
<value>abc#456.com</value>
</manager>
<manager>
<value>Others</value>
</manager>
</manageremail>
</email>
You should use the
emailparse.Load(#"C:\GUI\buildermanageremail.xml");
method instead of
emailparse.LoadXml(#"C:\GUI\buildermanageremail.xml");
since LoadXml can load xml string, not the file.

Is there a way to get all BindingExpression objects for a Window?

Is there a way to get all BindingExpression objects for a Window?
I am trying to refresh the form when the number PropertyChanged events that need to be fired to refresh a form is too high and not a good option. I am thinking doing it the other way that the form/window can re-query all bindings.
If you raise PropertyChanged with the PropertyChangedEventArgs that have a parameter of null or String.Empty the bindings of all properties will update.
[MSDN Reference]
Doing it the other way around is a lot more complicated and probably more performance consuming i think. You would need to check every DependencyProperty of every DependencyObject in the whole window for bindings.
Edit: Wrote the following sketchy extension method which does what you asked for, it's awfully inefficient (there is probably room for improvement but you're still dealing with an algorithm of considerable complexity):
public static void UpdateAllBindings(this DependencyObject o)
{
//Immediate Properties
List<FieldInfo> propertiesAll = new List<FieldInfo>();
Type currentLevel = o.GetType();
while (currentLevel != typeof(object))
{
propertiesAll.AddRange(currentLevel.GetFields());
currentLevel = currentLevel.BaseType;
}
var propertiesDp = propertiesAll.Where(x => x.FieldType == typeof(DependencyProperty));
foreach (var property in propertiesDp)
{
BindingExpression ex = BindingOperations.GetBindingExpression(o, property.GetValue(o) as DependencyProperty);
if (ex != null)
{
ex.UpdateTarget();
}
}
//Children
int childrenCount = VisualTreeHelper.GetChildrenCount(o);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(o, i);
child.UpdateAllBindings();
}
}
Just for reference, WPF itself does exactly this (iterates through all the data bound properties) when you call BindingOperations.ClearAllBindings().
The code for that is the following:
public static void ClearAllBindings(DependencyObject target)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
LocalValueEnumerator localValueEnumerator = target.GetLocalValueEnumerator();
ArrayList arrayList = new ArrayList(8);
while (localValueEnumerator.MoveNext())
{
LocalValueEntry current = localValueEnumerator.Current;
if (BindingOperations.IsDataBound(target, current.Property))
{
arrayList.Add(current.Property);
}
}
for (int i = 0; i < arrayList.Count; i++)
{
target.ClearValue((DependencyProperty)arrayList[i]);
}
}
LocalValueEnumerator is public so you can use it too.
You should be able to deduce the solution from this easily.

How do you restore a "versioned node" in a jackrabbit 2.1 repository?

Once a node has been deleted, how do you locate it so that you can restore it using jackrabbit or the jcr APIs?
I'm not an expert in Jackrabbit versioning, but as far as I know there is no easy way to locate such a node unless you know some of it's data. If you do know, then you can use a query and navigate to the next parent that is an instance of javax.jcr.version.Version, and restore it. If you don't know, then you need to iterate over the version storage and print all the data. You could filter out nodes that are not deleted, but otherwise it's a manual job because the path of a node is not stored in the version storage (unless you add a property that contains the path). Here is an example on how to list all nodes in the version storage. It will restore the last javax.jcr.version.Version it finds:
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.version.Version;
import javax.jcr.version.VersionManager;
import org.apache.jackrabbit.core.TransientRepository;
public class TestRestoreDeleted {
public static void main(String... args) throws Exception {
TransientRepository rep = new TransientRepository();
Session s = rep.login(
new SimpleCredentials("", new char[0]));
try {
// clear the repository first
if (s.getRootNode().hasNode("test")) {
s.getRootNode().getNode("test").remove();
s.save();
}
// add test/t1 and check in the change
Node test = s.getRootNode().addNode("test");
Node t1 = test.addNode("t1");
t1.addMixin("mix:versionable");
s.save();
VersionManager vm = s.getWorkspace().
getVersionManager();
for(int i=0; i<3; i++) {
vm.checkout("/test/t1");
t1.setProperty("data", "Hello" + i);
s.save();
vm.checkin("/test/t1");
}
// remove the node
t1.remove();
s.save();
// list all versions of all nodes in the repository
Node vs = s.getRootNode().
getNode("jcr:system").
getNode("jcr:versionStorage");
Version v = traverseVersionStorage(vs, 0);
// restore a version
vm.restore("/test/t1", v, false);
// get the node and print the data
t1 = s.getRootNode().
getNode("test").getNode("t1");
System.out.println("Restored: " +
t1.getProperty("data").getString());
} finally {
s.logout();
}
}
private static Version traverseVersionStorage(
Node n, int level) throws Exception {
Version v = null;
for (NodeIterator it = n.getNodes(); it.hasNext();) {
Node n2 = it.nextNode();
if (n2 instanceof Version
&& !n2.getName().startsWith("jcr:")) {
v = (Version) n2;
System.out.println("version " + n2.getName() +
" of node " + n2.getParent().getName() + ":");
Node n3 = n2.getNode("jcr:frozenNode");
for (PropertyIterator pt =
n3.getProperties(); pt.hasNext();) {
Property p = pt.nextProperty();
if (!p.getName().startsWith("jcr:")) {
System.out.println(" " + p.getName() + "="
+ (p.isMultiple() ? p.getValues().toString()
: p.getValue().getString()));
}
}
System.out.println();
}
Version v2 = traverseVersionStorage(n2, level + 1);
v = v == null ? v2 : v;
}
return v;
}
}

Resources