Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

TreeView behaviour

Status
Not open for further replies.

neculai

Programmer
Jul 21, 2004
13
CA
Hi, experts

I want to use TreeView dynamically. I did these steps (code below is simplified):

1. Built one TreeView (code in Form's Init) with 2 parents

This.List1.Nodes.Add(, 1, 'TX', 'Taxonomy', 0)
This.List1.Nodes.Add(, 1, 'ID', 'Identifiers', 0)

This.List1.Nodes.Add('TX', 4, 'Genus', 'Genus', 0)
This.List1.Nodes.Add('TX', 4, 'Species','Species',0)

This.List1.Nodes.Add('ID', 4, 'Genotype', 'Genotype', 0)
This.List1.Nodes.Add('ID', 4, 'Organ', 'Organ', 0)
This.List1.Nodes.Add('ID', 4, 'Treatment', 'Treatment', 0)
This.List1.Nodes.Add('ID', 4, 'Location', 'Location', 0)
This.List1.Nodes.Add('ID', 4, 'Unit', 'Hormone unit', 0)
This.List1.Nodes.Add('ID', 4, 'Date', 'Date', 0)
This.List1.Nodes.Add('ID', 4, 'Remarks', 'Remarks', 0)

2. I deleted all the Children belonging to ID parent.

For n = 1 To Thisform.List1.Nodes.Count
If Substr(ThisForm.List1.Nodes.Item(n).FullPath, 1, 11) == 'Identifiers'​
ThisForm.List1.Nodes.Remove(n)
Endif​
Next

3. Recreated back the children for ID parent.

Thisform.List1.Nodes.Add('ID', 4, 'Genostrain', 'Genotype', 0)
Thisform.List1.Nodes.Add('ID', 4, 'Organ', 'Organ', 0)
Thisform.List1.Nodes.Add('ID', 4, 'Treatment', 'Treatment', 0)
Thisform.List1.Nodes.Add('ID', 4, 'Location', 'Location', 0)
Thisform.List1.Nodes.Add('ID', 4, 'Unit', 'Hormone unit', 0)
Thisform.List1.Nodes.Add('ID', 4, 'Date', 'Date', 0)
Thisform.List1.Nodes.Add('ID', 4, 'Remarks', 'Remarks', 0)

4. Displayed all the nodes
For n = 1 To Thisform.List1.Nodes.Count
Wait Window ThisForm.List1.Nodes.Item(n).FullPath​
Next

And I don't see anymore the Children for ID node. I tried ThisForm.Refresh and ThisForm.List1.Refresh, but in vain. I checked the number of nodes and it is displayed correctly, showing that I really added the Children. Obviously, I miss something! Please, help! Thank you very much.

All the best,

Neculai
 
I have some doubts.

First of all, in the step 2 you try to delete all the nodes whose path begin with 'Identifiers', which starts with the parent node with key='ID' itself.
After deleting it, your code should fail on the attempt to delete the first of his children, because by deleting it, you have deleted all his children.

Code:
PUBLIC oFrm
ofrm=CREATEOBJECT("MyForm")
oFrm.Show()

DEFINE CLASS MyForm as Form
	width=600
	height=420
	ADD OBJECT List1 as olecontrol WITH oleclass="Mscomctllib.treectrl.2",width=400,height=400
	ADD OBJECT cmddel as commandbutton WITH left=420,caption="Del nodes"

	PROCEDURE init
		This.List1.Nodes.Add(, 1, 'TX', 'Taxonomy', 0)
		This.List1.Nodes.Add(, 1, 'ID', 'Identifiers', 0)

		This.List1.Nodes.Add('TX', 4, 'Genus', 'Genus', 0)
		This.List1.Nodes.Add('TX', 4, 'Species','Species',0)

		This.List1.Nodes.Add('ID', 4, 'Genotype', 'Genotype', 0)
		This.List1.Nodes.Add('ID', 4, 'Organ', 'Organ', 0)
		This.List1.Nodes.Add('ID', 4, 'Treatment', 'Treatment', 0)
		This.List1.Nodes.Add('ID', 4, 'Location', 'Location', 0)
		This.List1.Nodes.Add('ID', 4, 'Unit', 'Hormone unit', 0)
		This.List1.Nodes.Add('ID', 4, 'Date', 'Date', 0)
		This.List1.Nodes.Add('ID', 4, 'Remarks', 'Remarks', 0)
	ENDPROC
	
	PROCEDURE cmddel.click
		For n = 1 TO Thisform.List1.Nodes.Count
			If Substr(ThisForm.List1.Nodes.Item(n).FullPath, 1, 11) == 'Identifiers'
				MESSAGEBOX(ThisForm.List1.Nodes.Item(n).FullPath) && First shows and then delete 'Identifiers', and on the next step, fails
				ThisForm.List1.Nodes.Remove(n)
			Endif
		Next		
	ENDPROC
ENDDEFINE

That's easy to correct, by adding one supplementary condition to the IF, like AND ThisForm.List1.Nodes.Item(n).key<>'ID'
Second, the way you try to delete, with a FOR from 1 to Thisform.List1.Nodes.Count will fail anyway.
By deleting one node, the total number of the nodes will decrease (the nodes collection will contain less nodes and Thisform.List1.Nodes.Count will decrease).
But because of particularities of the VFP's FOR command, although Thisform.List1.Nodes.Count decreases, the value used by the FOR command remains unchanged .
Excerpt from the "For" help topic :
Note
The values of nInitialValue, nFinalValue, and nIncrement are read only initially. However, changing the value of the counter VarName inside the loop affects the number of times the loop is executed. Changing the value of nFinalValue in a FOR loop has no effect
.

Code:
PUBLIC oFrm
ofrm=CREATEOBJECT("MyForm")
oFrm.Show()

DEFINE CLASS MyForm as Form
	width=600
	height=420
	ADD OBJECT List1 as olecontrol WITH oleclass="Mscomctllib.treectrl.2",width=400,height=400
	ADD OBJECT cmddel as commandbutton WITH left=420,caption="Del nodes"
	

	PROCEDURE init
		This.List1.Nodes.Add(, 1, 'TX', 'Taxonomy', 0)
		This.List1.Nodes.Add(, 1, 'ID', 'Identifiers', 0)

		This.List1.Nodes.Add('TX', 4, 'Genus', 'Genus', 0)
		This.List1.Nodes.Add('TX', 4, 'Species','Species',0)

		This.List1.Nodes.Add('ID', 4, 'Genotype', 'Genotype', 0)
		This.List1.Nodes.Add('ID', 4, 'Organ', 'Organ', 0)
		This.List1.Nodes.Add('ID', 4, 'Treatment', 'Treatment', 0)
		This.List1.Nodes.Add('ID', 4, 'Location', 'Location', 0)
		This.List1.Nodes.Add('ID', 4, 'Unit', 'Hormone unit', 0)
		This.List1.Nodes.Add('ID', 4, 'Date', 'Date', 0)
		This.List1.Nodes.Add('ID', 4, 'Remarks', 'Remarks', 0)
	ENDPROC
	
	PROCEDURE cmddel.click
		For n = 1 TO Thisform.List1.Nodes.Count
			If Substr(ThisForm.List1.Nodes.Item(n).FullPath, 1, 11) == 'Identifiers' AND ThisForm.List1.Nodes.Item(n).key<>'ID'
				* First delete the Genotype node. 
				* Second delete the Treatment node. The collection was already shortened, so apparently the Organ node was skipped
				MESSAGEBOX("Before deletion"+CHR(13)+;
					"Step n="+TRANSFORM(n)+CHR(13)+;
					"Total number of nodes Thisform.List1.Nodes.Count="+TRANSFORM(Thisform.List1.Nodes.Count)+CHR(13)+;
					"Nodes("+TRANSFORM(n)+")="+ThisForm.List1.Nodes.Item(n).FullPath) 
				ThisForm.List1.Nodes.Remove(n)
				* After deleting a node, his place in the list is taken by the next one.
				* By deleting the Genotype node, the number 5 node becomes the Organ node and the number 6 node becomes the Treatment node.
				MESSAGEBOX("After deletion"+CHR(13)+;
					"Step n="+TRANSFORM(n)+CHR(13)+;
					"Total number of nodes Thisform.List1.Nodes.Count="+TRANSFORM(Thisform.List1.Nodes.Count)+CHR(13)+;
					"Nodes("+TRANSFORM(n)+")="+ThisForm.List1.Nodes.Item(n).FullPath) 
			Endif
		Next		
	ENDPROC
	
ENDDEFINE

Once again, it's easy to correct, by changing the for loop. Now your test should work.
Code:
PUBLIC oFrm
ofrm=CREATEOBJECT("MyForm")
oFrm.Show()

DEFINE CLASS MyForm as Form
	width=600
	height=420
	ADD OBJECT List1 as olecontrol WITH oleclass="Mscomctllib.treectrl.2",width=400,height=400
	ADD OBJECT cmddel as commandbutton WITH left=420,caption="Del nodes"
	

	PROCEDURE init
		This.List1.Nodes.Add(, 1, 'TX', 'Taxonomy', 0)
		This.List1.Nodes.Add(, 1, 'ID', 'Identifiers', 0)

		This.List1.Nodes.Add('TX', 4, 'Genus', 'Genus', 0)
		This.List1.Nodes.Add('TX', 4, 'Species','Species',0)

		This.List1.Nodes.Add('ID', 4, 'Genotype', 'Genotype', 0)
		This.List1.Nodes.Add('ID', 4, 'Organ', 'Organ', 0)
		This.List1.Nodes.Add('ID', 4, 'Treatment', 'Treatment', 0)
		This.List1.Nodes.Add('ID', 4, 'Location', 'Location', 0)
		This.List1.Nodes.Add('ID', 4, 'Unit', 'Hormone unit', 0)
		This.List1.Nodes.Add('ID', 4, 'Date', 'Date', 0)
		This.List1.Nodes.Add('ID', 4, 'Remarks', 'Remarks', 0)
	ENDPROC
	
	PROCEDURE cmddel.click
		For n = Thisform.List1.Nodes.Count TO 1 STEP -1
			If Substr(ThisForm.List1.Nodes.Item(n).FullPath, 1, 11) == 'Identifiers' AND ThisForm.List1.Nodes.Item(n).key<>'ID'
				ThisForm.List1.Nodes.Remove(n)
			Endif
		Next		
	ENDPROC
	
ENDDEFINE

P.S. The following links explains the deletion process from a collection. The subject of the debate was a container, but the principles is the same.

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
Thank you, Vilhelm.

Actually, my code is more lengthy and complex and I did some tricks like counting how many children I have before the actual deleting. Indeed, I thought first that the nCount is dynamical changed, but it's not. Still, I have the same problem, when I do these steps:

1. I display all the nodes and I see everything is OK.
2. I delete all the children belonging to one parent (i'm not deleting the parent).
3. I add again the children to that node.
4. I display the no of nodes and I see that it is correct.
5. I display all the nodes and I see that the parent exists, but the added children are missing, in spite of the fact that the no of nodes is correct.

Do I need to do kind of Refresh method for nodes? Or any other thing? Like maybe erasing the whole Tree and recreating it?

All the best,
Neculai
 
Hi Neculai,

Pehaps the problem is as follows:

[tt]If Substr(ThisForm.List1.Nodes.Item(n).FullPath, 1, 11) == 'Identifiers'[/tt]

As well as deleting the children of Identifiers, you are also deleting the parent node, that is, Identifiers itself. Then, when you later try to add back the children of Identifiers, they no longer have a parent. I'm not sure that completely explains the behaviour you are seeing, but it seems likely that it will cause things to go wrong.

Rather than deleting the nodes based on a substring of their Fullpath, better to delete those where [tt].Parent.Key == "ID"[/tt]

Mike



__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Maybe do you want to expand the node after adding childs to it.
I mean do you need this line:
Thisform.List1.Nodes('ID').Expanded=.T.

Code:
PUBLIC oFrm
ofrm=CREATEOBJECT("MyForm")
oFrm.Show()

DEFINE CLASS MyForm as Form
	width=600
	height=420
	ADD OBJECT List1 as olecontrol WITH oleclass="Mscomctllib.treectrl.2",width=400,height=400
	ADD OBJECT cmddel as commandbutton WITH left=420,caption="Del nodes"
	ADD OBJECT cmdadd as commandbutton WITH left=420, top=50,caption="Add nodes"
	

	PROCEDURE init
		This.List1.Nodes.Add(, 1, 'TX', 'Taxonomy', 0)
		This.List1.Nodes.Add(, 1, 'ID', 'Identifiers', 0)

		This.List1.Nodes.Add('TX', 4, 'Genus', 'Genus', 0)
		This.List1.Nodes.Add('TX', 4, 'Species','Species',0)

		This.List1.Nodes.Add('ID', 4, 'Genotype', 'Genotype', 0)
		This.List1.Nodes.Add('ID', 4, 'Organ', 'Organ', 0)
		This.List1.Nodes.Add('ID', 4, 'Treatment', 'Treatment', 0)
		This.List1.Nodes.Add('ID', 4, 'Location', 'Location', 0)
		This.List1.Nodes.Add('ID', 4, 'Unit', 'Hormone unit', 0)
		This.List1.Nodes.Add('ID', 4, 'Date', 'Date', 0)
		This.List1.Nodes.Add('ID', 4, 'Remarks', 'Remarks', 0)
	ENDPROC
	
	PROCEDURE cmddel.click
		For n = Thisform.List1.Nodes.Count TO 1 STEP -1
			If Substr(ThisForm.List1.Nodes.Item(n).FullPath, 1, 11) == 'Identifiers' AND ThisForm.List1.Nodes.Item(n).key<>'ID'
				ThisForm.List1.Nodes.Remove(n)
			Endif
		Next		
	ENDPROC
	
	PROCEDURE cmdadd.click
		Thisform.List1.Nodes.Add('ID', 4, 'Genostrain', 'Genotype', 0)
		Thisform.List1.Nodes.Add('ID', 4, 'Organ', 'Organ', 0)
		Thisform.List1.Nodes.Add('ID', 4, 'Treatment', 'Treatment', 0)
		Thisform.List1.Nodes.Add('ID', 4, 'Location', 'Location', 0)
		Thisform.List1.Nodes.Add('ID', 4, 'Unit', 'Hormone unit', 0)
		Thisform.List1.Nodes.Add('ID', 4, 'Date', 'Date', 0)
		Thisform.List1.Nodes.Add('ID', 4, 'Remarks', 'Remarks', 0)
		
		Thisform.List1.Nodes('ID').Expanded=.T.
	ENDPROC
ENDDEFINE

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
If you are wanting to dynamically add child nodes as they are requested by a user so that you are not loading the entire TreeView at init event, then I do the following:

1. Add the root nodes as normal.
2. For each added node, add a child node with empty text

Now wait for the user to interact with the TreeView control. When the user clicks to expand a node:

1. In the Expand event of the TreeView, check if the node to be expanded has its child with an empty node text
2. If the child node has empty node text, then remove the child and add the 'true' child nodes; for each child node
being added, add an empty child node to it.
3. If the node to be expanded has a child with node text, then allow the expand to occur as normal.
 
Thank you all, I finally made The TreeView work. My mistake was that I was trying to delete nodes in a For-Next loop, and the initial counter was not dynamically decreased automatically. So I used the For-Next in a Do while-Enddo loop with few more variables.

One more question. I try to change FontName and FontSize for TreeView nodes. In my form, at TreeCtrl Properties, I have only one Tab (General), I don't see the other 2 Tabs (Font and Picture). I use Microsoft TreeView 6.0 (MsComctlLib.TreeCtrl.2).

I can change the ForeColour, but not the FontName and FontSize. Any way to do it programatically?

This.List1.Nodes(i).BackColor = RGB(255,255,255)
This.List1.Nodes(i).ForeColor = RGB(0,0,128)

Thank you.

Neculai
 
Use the object browser to see reference about the treeview and node classes. A Node object has not font property, it's only font related property seems the "bold" property. So you can only set one font for all nodes, each individual node only can override the general bold setting. If you need more variety you should consider buying an advanced treeview control from dbi or exontrol.

The treeview object has a font property, the description says it's an object. You can't drill down what properties that font object has in the object browser, also intellisense doesn't show the treeview.Font properties at design time. But you can inspect the properites of the treeview.font object at runtime:

Code:
oFont = Thim.List1.Font
set step on

Now type oFont. in the command window and see what you have, in my version it's Bold, Charset, Italic, Name, Size, StrikeThrough, Underline and Weight. You can at least set these properties in code, eg:
Code:
 Thim.List1.Font.Name = "Courier New"

Bye, Olaf.
 
Neculai,

You're out of luck. You can change the font name and size for the tree as a whole, but not for specific nodes.

The only relevant properties that you can change for an individual node are Bold, ForeColor and BackColor. (So, even though you can set a node to bold, you can't set it to Italic, for example.)

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Thanks, Olaf

For i = 1 To This.List1.Nodes.Count
This.List1.Nodes(i).BackColor = RGB(255,255,255)​
This.List1.Nodes(i).ForeColor = RGB(0,0,128)​
Next

oFont = This.List1.Font
With oFont
.Name = 'Arial'​
.Size = '8'​
Endwith

It works perfectly.

All the best,
Neculai
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top