Notifications
Clear all

[Closed] dotNet Treeview TreeViewNodeSorter Property

Thanks Pjanssen, that does look like a good way to go about sorting huge treeview lists. Once I’ve figured this one out I’ll look into the examples you have given some more.

I’ve got slightly closer to making this treeview sorter work. It doesn’t error any more, but the sorting is wrong and I have no idea why…

The list should go from top to bottom: b1, b2, a1, a2. (a1 and a2 have an imageindex of 1, b1 and b2 have an imageindex of 0)

But instead the list goes b2, a2, b1, a1.

Does anyone know why it would be sorting incorrectly?

Here is the code:

 
--Sort Function
fn CompileNodeSorterClass forceRecompile:false =
 (
 	if forceRecompile OR dotnetclass "TreeViewTools.NodeSorter" == undefined do
 	(
 		source = ""
 		append source "using System;
"
 		append source "using System.Collections;
"
 		append source "using System.Windows.Forms;
"
 		append source "namespace TreeViewTools
"
 		append source "{
" -- open namespace TreeViewTools
 		append source "public class NodeSorter : IComparer
"
 		append source "{
" -- open class
 		append source "public int Compare(object x, object y)
"
 		append source "{
" -- open method
 		append source "TreeNode tx = x as TreeNode;
"
 		append source "TreeNode ty = y as TreeNode;
"
 		append source "int a = tx.ImageIndex - ty.ImageIndex;
"
		append source "if (a == 0) return string.Compare(tx.Text, ty.Text);
"
 		append source "return a;
"
 		append source "}
" -- end method
 		append source "}
" -- end class
 		append source "}
" -- end namespace TreeViewTools
 		csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
 		compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
 		compilerParams.ReferencedAssemblies.Add "System.Windows.Forms.dll"
 		compilerParams.GenerateInMemory = on
 		compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
 	)
 	dotnetClass "TreeViewTools.NodeSorter"
 )

--Rollout
rollout test "Test Rollout" (
	dotNetControl tv "Treeview" width:180 height:190
	on test open do (
		tv.sorted = true
		CompileNodeSorterClass()
		tv.TreeViewNodeSorter = dotnetObject "TreeViewTools.NodeSorter"
		tv.sort()
	)
)
createDialog test 200 200


newNode = test.tv.nodes.add "a1"
newNode.name = "a1"
newNode.imageIndex = 1

newNode = test.tv.nodes.add "b1"
newNode.name = "b1"
newNode.imageIndex = 0

newNode = test.tv.nodes.add "a2"
newNode.name = "a2"
newNode.imageIndex = 1


newNode = test.tv.nodes.add "b2"
newNode.name = "b2"
newNode.imageIndex = 0


global TreeViewAssembly
fn makeTreeViewAssembly forceRecompile:on =
(
	if forceRecompile or not iskindof TreeViewAssembly dotnetobject or (TreeViewAssembly.GetType()).name != "Assembly" do
	(
		source = ""
		source += "using System;
"
		source += "using System.Collections;
"
		source += "using System.Windows.Forms;
"
		source += "namespace TreeViewOps
"
		source += "{
"
		source += "	public class SortByImageIndex : IComparer
"
		source += "	{
"
		source += "		public int Compare(object x, object y)
"
		source += "		{
"
		source += "			return ((x as TreeNode).ImageIndex - (y as TreeNode).ImageIndex);
"
		source += "		}
"
		source += "	}
"
		source += "}
"

		csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
		compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
		compilerParams.ReferencedAssemblies.Add "System.Windows.Forms.dll"
		compilerParams.GenerateInMemory = on
		compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
		compilerResults.CompiledAssembly
	)
)
TreeViewAssembly = makeTreeViewAssembly()
 
-- step #1: Make Rollout - NO SORT
try(destroydialog test) catch()
rollout test "Test Rollout" 
(
	dotNetControl tv "Treeview" width:180 height:190
	on test open do 
	(
		test.tv.nodes.add "" "c" 2
		test.tv.nodes.add "" "a" 4
		test.tv.nodes.add "" "d" 1
		test.tv.nodes.add "" "b" 3
	)
)
createDialog test 200 200

-- step #2: SORT BY INDEX
test.tv.TreeViewNodeSorter = TreeViewAssembly.CreateInstance "TreeViewOps.SortByImageIndex"

-- step #3: BACK TO DEFAULT SORT (BY TEXT)
test.tv.TreeViewNodeSorter = undefined
test.tv.sort()


denisT, Thanks so much for posting that. Works like a charm

I’ve modified the compiling part slightly to sort by imageIndex, and then sort the imageIndex nodes alphabetically. Below is the code as a reference for anybody else trying to do this, also below is a list of things that caught me out:

  1. If nodes are added to the treeview after tv.treeViewNodeSorter has been defined, run tv.sort() to sort the treeview. Before sort() is run, they will be added at the bottom.
  2. Use tv.beginUpdate() and tv.endUpdate() to prevent ui flashing/scrollbar going crazy.
  3. tv.sort() cannot be used in the afterLabelEdit event as it creates an infinite loop. Instead use it in a keypress or mousepress event after a label has been edited.

Here is the code:



global TreeViewAssembly
fn makeTreeViewAssembly forceRecompile:on =
(
	if forceRecompile or not iskindof TreeViewAssembly dotnetobject or (TreeViewAssembly.GetType()).name != "Assembly" do
	(
			source = ""
			source += "using System;
"
			source += "using System.Collections;
"
			source += "using System.Windows.Forms;
"
			source += "namespace TreeViewOps
"
			source += "{
"
			source += "	public class SortByImageIndex : IComparer
"
			source += "	{
"
			source += "		public int Compare(object x, object y)
"
			source += "		{
"
			source += "			int a = (((y as TreeNode).ImageIndex - (x as TreeNode).ImageIndex));
"
			source += "			if (a == 0) return string.Compare((x as TreeNode).Text, (y as TreeNode).Text);
"
			source+= "			return a;
"
			source += "		}
"
			source += "	}
"
			source += "}
"
		
		csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
		compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
		compilerParams.ReferencedAssemblies.Add "System.Windows.Forms.dll"
		compilerParams.GenerateInMemory = on
		compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
		compilerResults.CompiledAssembly
	)
)
TreeViewAssembly = makeTreeViewAssembly()
 
-- step #1: Make Rollout - NO SORT
try(destroydialog test) catch()
rollout test "Test Rollout" 
(
	dotNetControl tv "Treeview" width:180 height:190 
	on test open do 
	(
		test.tv.nodes.add "" "c3" 0
		test.tv.nodes.add "" "c1" 0
		test.tv.nodes.add "" "c2" 0
		test.tv.nodes.add "" "b1" 4
		test.tv.nodes.add "" "b4" 4
		test.tv.nodes.add "" "b2" 4
		test.tv.nodes.add "" "b3" 4
		test.tv.nodes.add "" "a3" 2
		test.tv.nodes.add "" "a2" 2
		test.tv.nodes.add "" "a1" 2
	)
)
createDialog test 200 200 30 30

-- step #2: SORT BY INDEX
test.tv.TreeViewNodeSorter = TreeViewAssembly.CreateInstance "TreeViewOps.SortByImageIndex"

-- step #3: RETURN TO DEFAULT SORT IF DESIRED (SORT BY TEXT)
--test.tv.TreeViewNodeSorter = undefined
--test.tv.sort()

You should be able to create multi-line strings in one go in maxscript:

str = ("
//some interesting stuff
//some more stuff")

That should make things a bit easier to write and read.

Also, you can use this for a bit nicer string comparing:

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
static extern int StrCmpLogicalW(String x, String y);

That sorts things like “node5” before “node12”, whereas the regular String.Compare does it the other way around.

Thanks Pjanssen, really useful stuff. This forum rocks Will have to spend some more time here!

maybe i’m missing something but how is your solution different from the one i provided here along with correction here ?

Hi Gravey,

Hardly anything is different. I used the if statement you had provided and added that to the solution denisT provided. I was a little confused by your correction as it told me to delete the wrong line, and it kept erroring as a result. I posted a final fully working solution with a rollout and dialog etc in case anyone is looking to do the same.

I really do appreciate the help, and I apologise if it appears that I was trying to take credit for both your and denisT’s solution. They both work fantastically

Tim

ahh you are right about the correction – another copy paste error on my part. My bad – again… At the end of the day, “taking credit” as you say is not a big concern for me. I was more confused and interested that our solutions could somehow be different for better or worse in some minor detail that I was overlooking. Knowledge is king

One difference I have noticed is the ability to recompile without restarting 3ds Max. With the version you provided I had to restart 3ds max if I changed any text in the dotNet part, even with forceRecompile set to on. With the version denisT provided I could simply re-evaluate the script and the dll would update. Handy when you don’t know what you’re doing, and trying the ‘brute force’ method

Page 2 / 2