Can more than one treeviewnode in a tree be selected at once?

BlitzPlus Forums/BlitzPlus Programming/Can more than one treeviewnode in a tree be selected at once?

JoshK(Posted 2004) [#1]
Is it possible to select multiple treeview nodes at the same time? I don't think B+ supports this, but can it be done with a workaround?

What about list boxes? Can more than one of these items be selected at once?


JoshK(Posted 2004) [#2]
There's got to be a way to use SendMessage() to do it. Anyone know what the parameters are?


BlitzSupport(Posted 2004) [#3]
Halo, take a look at the docs here:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/listview/listview.asp

Click (in the left section) List-View Controls Reference -- the messages (use SendMessage and the QueryObject window handle), constants, etc, are there. On a quick glance, I get the impression Blitz's 'ListBoxes' have the 'single-selection only' flag on (LVS_SINGLESEL); it *might* be possible to remove this via SetWindowLong...

There's similar stuff for TreeViews here, though I don't think you can select more than one item ever in these...

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/treeview/reflist.asp


JoshK(Posted 2004) [#4]
Thanks man. I tried this, but don't see any difference. The listview is the one that really matters.

I am starting a new project right now. Getting this to work is fundamental, and I am losing time and money as long as it doesn't work.

Any ideas? I found something somewhere saying that constant equals $0004.

w=640
h=480
Global window=CreateWindow(PROGRAMTITLE,(ClientWidth(Desktop())-w)/2,(ClientHeight(Desktop())-h)/2,w,h,0)

list=CreateListBox(x,y,ClientWidth(window),ClientHeight(window)-y,window)
SetGadgetLayout list,1,1,1,1
For n=1 To 100
	AddGadgetItem list,"item "+n
	Next

hwnd=QueryObject(list,1)
SetWindowLong hwnd,$0004,1

Repeat
	Select WaitEvent()
		Case $803
			End
		End Select
	Forever



JoshK(Posted 2004) [#5]
hmmm...GetWindowLong(hwnd,$0004) returns 0, no matter what I do.

w=640
h=480
Global window=CreateWindow(PROGRAMTITLE,(ClientWidth(Desktop())-w)/2,(ClientHeight(Desktop())-h)/2,w,h,0)

list=CreateListBox(x,y,ClientWidth(window),ClientHeight(window)-y,window)

hwnd=QueryObject(list,1)
SetWindowLong(hwnd,4,1)
RuntimeError GetWindowLong(hwnd,4)



jondecker76(Posted 2004) [#6]
I had the same problem a few months ago and it set me back for a long time on my current project. unfortunately, it boiled down to me writing my own treeview system and treeview renderer (which renders the tvw to a canvas). I highly suggest this method as you have full control over everything - currently mine supports color schemes, Icons and multiple selections. and it is really fast - rendering takes around 0.05 secs.
Unfortunately I'm not quite ready to release the source to this as it is the key ingredient to my current project and it is partially intertwined with my internal engine. However, once my project is finished I do plan on cleaning up the tvw system and making it more general-purpose.

Just to give you an idea of the great quality you can get using this method, check out the screenshot below




CodeGit(Posted 2004) [#7]
I tried creating a grid with editable cells, and found that B+ had no real easy way of doing this.

The LIST control is sadly lacking, and I would have thought by now this would have been addressed. Sadly B+ has not moved forward at all, since its initial release.

My solution was to buy PowerBasic, but I would have preferred using B+ :(


JoshK(Posted 2004) [#8]
Well, since SetWindowLong is having no effect on GetWindowLong(), it appears that there is no way to alter the gadget.

The treeview doesn't matter so much, but I HAVE to be able to select multiple items on the listview.

That's pretty sad, because it means I can't use BlitzPlus for this project, just because of one little technical windows thing. It would have been easy, because I have a whole 3D engine and stuff set up from the CShop code. If I use PureBasic, it will take two or three times as long to finish.

:(


Snarty(Posted 2004) [#9]
The reason it's not working is because you need it set at -16 not 4. (GWL_STYLE=-16)

This should sort out your woes, have fun :)


JoshK(Posted 2004) [#10]
Thanks, but that just freezes the gadget.


Snarty(Posted 2004) [#11]
Ok, this is all you should need to set for multi-select listview.

SetWindowLong hWnd,-16,$40000000 or $10000000 or MYLBS

Where MYLBS is a combination of what you need below:
Const LBS_DISABLENOSCROLL=4096
Const LBS_EXTENDEDSEL=$800
Const LBS_HASSTRINGS=64
Const LBS_MULTICOLUMN=512
Const LBS_MULTIPLESEL=8
Const LBS_NODATA=$2000
Const LBS_NOINTEGRALHEIGHT=256
Const LBS_NOREDRAW=4
Const LBS_NOSEL=$4000
Const LBS_NOTIFY=1
Const LBS_OWNERDRAWFIXED=16
Const LBS_OWNERDRAWVARIABLE=32
Const LBS_SORT=2
Const LBS_STANDARD=$a00003
Const LBS_USETABSTOPS=128
Const LBS_WANTKEYBOARDINPUT=$400


$4000000= Child Window
$1000000= Visable

Hope this helps


Snarty(Posted 2004) [#12]
My bad, the above is for listboxes... DOH! I can't find the LVS_ constants atm, I'll dig deeper.


Snarty(Posted 2004) [#13]
Here you go, these are the LVS_ constants
Const LVS_ICON=$0
Const LVS_REPORT=$1
Const LVS_SMALLICON=$2
Const LVS_LIST=$3
Const LVS_TYPEMASK=$3
Const LVS_SINGLESEL=$4
Const LVS_SHOWSELALWAYS=$8
Const LVS_SORTASCENDING=$10
Const LVS_SORTDESCENDING=$20
Const LVS_SHAREIMAGELISTS=$40
Const LVS_NOLABELWRAP=$80
Const LVS_AUTOARRANGE=$100
Const LVS_EDITLABELS=$200
Const LVS_OWNERDATA=$1000
Const LVS_NOSCROLL=$2000
Const LVS_TYPESTYLEMASK=$fc00
Const LVS_ALIGNTOP=$0
Const LVS_ALIGNLEFT=$800
Const LVS_ALIGNMASK=$c00
Const LVS_OWNERDRAWFIXED=$400
Const LVS_NOCOLUMNHEADER=$4000
Const LVS_NOSORTHEADER=$8000



JoshK(Posted 2004) [#14]
We're on the right track. Check this out! You can make a Windows Explorer-type display:

w=640
h=480
Global window=CreateWindow(PROGRAMTITLE,(ClientWidth(Desktop())-w)/2,(ClientHeight(Desktop())-h)/2,w,h,0) 

list=CreateListBox(x,y,ClientWidth(window),ClientHeight(window)-y,window) 
SetGadgetLayout list,1,1,1,1 
For n=1 To 100
	AddGadgetItem list,"item "+n
	Next

hwnd=QueryObject(list,1)
SetWindowLong hwnd,-16,$40000000 Or $10000000 Or $4

Repeat
	Select WaitEvent()
		Case $803
			End
		End Select
	Forever



JoshK(Posted 2004) [#15]
Sweet! I got it. Thank you thank you thank you!

hwnd=QueryObject(list,1)
flags=GetWindowLong(hwnd,-16)
SetWindowLong hwnd,-16,flags+LVS_SINGLESEL


Now I need to figure out how to check if an item is selected.


Snarty(Posted 2004) [#16]
Anytime, got it the same time as you, lol

See Below..


JoshK(Posted 2004) [#17]
Anyone know what LVM_SETITEMSTATE equals? It's something like 0x1000+43, whatever that means.

Also, you need to use LVS_SHOWSELALWAYS when doing this.


Snarty(Posted 2004) [#18]
Have fun with this :) not sure if it's what you need.
Const PROGRAMTITLE$="Testing Listview"

; Window Style Constants
Const WS_BORDER=$800000
Const WS_CAPTION=$c00000
Const WS_CHILD=$40000000
Const WS_CHILDWINDOW=$40000000
Const WS_CLIPCHILDREN=$2000000
Const WS_CLIPSIBLINGS=$4000000
Const WS_DISABLED=$8000000
Const WS_DLGFRAME=$400000
Const WS_GROUP=$20000
Const WS_HSCROLL=$100000
Const WS_ICONIC=$20000000
Const WS_MAXIMIZE=$1000000
Const WS_MAXIMIZEBOX=$10000
Const WS_MINIMIZE=$20000000
Const WS_MINIMIZEBOX=$20000
Const WS_OVERLAPPED=0
Const WS_OVERLAPPEDWINDOW=$cf0000
Const WS_POPUP=$80000000
Const WS_POPUPWINDOW=$80880000
Const WS_SIZEBOX=$40000
Const WS_SYSMENU=$80000
Const WS_TABSTOP=$10000
Const WS_THICKFRAME=$40000
Const WS_TILED=0
Const WS_TILEDWINDOW=$cf0000
Const WS_VISIBLE=$10000000
Const WS_VSCROLL=$200000

; Listview Style Constants
Const LVS_ICON=$0
Const LVS_REPORT=$1
Const LVS_SMALLICON=$2
Const LVS_LIST=$3
Const LVS_TYPEMASK=$3
Const LVS_SINGLESEL=$4
Const LVS_SHOWSELALWAYS=$8
Const LVS_SORTASCENDING=$10
Const LVS_SORTDESCENDING=$20
Const LVS_SHAREIMAGELISTS=$40
Const LVS_NOLABELWRAP=$80
Const LVS_AUTOARRANGE=$100
Const LVS_EDITLABELS=$200
Const LVS_OWNERDATA=$1000
Const LVS_NOSCROLL=$2000
Const LVS_TYPESTYLEMASK=$fc00
Const LVS_ALIGNTOP=$0
Const LVS_ALIGNLEFT=$800
Const LVS_ALIGNMASK=$c00
Const LVS_OWNERDRAWFIXED=$400
Const LVS_NOCOLUMNHEADER=$4000
Const LVS_NOSORTHEADER=$8000

Const GWL_STYLE=-16

Type Item

	Field Mask
	Field Index
	Field SubIndex
	Field State
	Field stateMask
	Field ItemText$
	Field MaxLen
	Field Icon
	Field lParam

End Type

w=640
h=480
Global window=CreateWindow(PROGRAMTITLE,(ClientWidth(Desktop())-w)/2,(ClientHeight(Desktop())-h)/2,w,h,Desktop(),9)

Global List=CreateListBox(x,y,ClientWidth(window)/2,ClientHeight(window)-y,window)
Global DispList=CreateListBox(GadgetWidth(List),y,GadgetWidth(List),GadgetHeight(List),window)
SetGadgetLayout list,1,1,1,1

For n=0 To 99
	AddGadgetItem list,"item "+n
Next

hwnd=QueryObject(list,1)
PREVAL=GetWindowLong(hwnd,GWL_STYLE)
SetWindowLong hwnd,GWL_STYLE,PREVAL-LVS_SINGLESEL

Repeat
	Select WaitEvent()
		
		Case $401
			Select EventSource()
			
				Case List
					FindSelected(hwnd)
			
			End Select
		
		Case $803
			End
	
	End Select

Forever

Function FindSelected(hWND)

	CItem.Item=New Item
	Pointer=Int(CItem)+20
	
	ClearGadgetItems DispList
	
	For t=0 To 99
		CItem\Index=t
		CItem\SubIndex=0
		CItem\Mask=8
		CItem\StateMask=2
		Suc=SendMessage(hWND,$1000+5,0,Pointer)
		If Suc
			If CItem\State
				AddGadgetItem DispList,GadgetItemText(List,t)
			EndIf
		EndIf
	Next	
	Delete CItem

End Function



BlitzSupport(Posted 2004) [#19]
Halo, sorry for the delay in getting back to this one. It's LVM_FIRST + 43, ie. you just need to know what LVM_FIRST is ($1000) and add 43.


BlitzSupport(Posted 2004) [#20]
BTW If you can't find a constant, just stick 'const THIS_CONSTANT' into Google and you'll usually find it.


Snarty(Posted 2004) [#21]
Here's the function for checking a single item, or just use the batch checking in the code above.
Function IsItemSelected(ListView,Index)

	; ListView: A ListBox gadget
	; Index: Item in the ListBox to check

	hWND=QueryObject(ListView,1)

	CItem.Item=New Item
	Pointer=Int(CItem)+20
	CItem\Index=t
	CItem\SubIndex=0
	CItem\Mask=8
	CItem\StateMask=2
	
	SendMessage hWND,$1000+5,0,Pointer
	State=CItem\State
	
	Delete CItem
	Return State

End Function



BlitzSupport(Posted 2004) [#22]
I haven't tried this yet, but a 'cleaner' way to check for multiple selections looks to be:

Repeat

    item = SendMessage (listview, LVM_GETNEXTITEM, -1, LVNI_SELECTED)

Until item = -1 ; No more selected items



Snarty(Posted 2004) [#23]
Yup, it should work fine. Obviously by replacing -1 with a marker being the last found item. :)


BlitzSupport(Posted 2004) [#24]
This works (and remember you can also Ctrl-select multiple groups).

The only problem is that if you click and drag in the blank area right at the start, without selecting any items, it selects them all! (Maybe you can just pre-select the first item to kick things off.)

And, yes, Snarty -- that had me stumped for a little while... :)

w = 640
h = 480

window = CreateWindow ("Multiple listview item selection", (ClientWidth (Desktop ()) - w) / 2, (ClientHeight (Desktop ()) - h) / 2, w, h, 0)

list = CreateListBox (0, 0, ClientWidth (window), ClientHeight (window) - 25, window) 
SetGadgetLayout list, 1, 1, 1, 1 

For n = 0 To 99
	AddGadgetItem list, "item " + n
Next

button = CreateButton ("Click for result", 0, ClientHeight (window) - 25, ClientWidth (window), 23, window)

hwnd = QueryObject (list, 1)
flags = GetWindowLong (hwnd, -16)
SetWindowLong hwnd, -16, flags + $4

Const LVM_FIRST = $1000
Const LVNI_SELECTED = $2
Const LVM_GETNEXTITEM = LVM_FIRST + 12
Const LVM_SETSELECTIONMARK = LVM_FIRST + 67

Repeat
	
	Select WaitEvent()

		Case $803
			End
			
		Case $401
		
			If EventSource () = button
			
				m$ = "" ; For dialog box string...
				item = -1 ; Start checking from the first item (0)...
				
				Repeat
				
					; Note the 'item' in the parameters -- we search *from* item each time round...
					
					item = SendMessage (hwnd, LVM_GETNEXTITEM, item, LVNI_SELECTED) 
					
					If item <> -1
						m$ = m$ + "Item: " + item + Chr (10)
					EndIf
					
				Until item = -1 ; No more selected items
				
				Notify m$

			EndIf
						
	End Select

Forever



JoshK(Posted 2004) [#25]
You guys rock.

This is pretty useful knowledge for BlitzPlus, in general.


BlitzSupport(Posted 2004) [#26]

Black Box - just as big.


You can't do that and '404' us!


JoshK(Posted 2004) [#27]
Hey, is there a way to NOT alphabetize/sort the lists?


...ah, here it is:

flags=GetWindowLong(hwnd,-16)-LVS_SORTASCENDING


BlitzSupport(Posted 2004) [#28]
Just take a good look through those docs for sending messages and setting up the listview (ie. SetWindowLong), and you'll cover most complaints you might think you have!


JoshK(Posted 2004) [#29]
Hmmmm...any idea how to select or deselect an item?

I tried "SendMessage hwnd,LVM_SETITEMSTATE,item,state" but it didn't do anything,

Many thanks.


BlitzSupport(Posted 2004) [#30]
I got a similar result when trying to pre-select the first item (I used LVM_SETSELECTIONMARK, though it's not clear if this actually means the selection box thing).

I even tried activating the listview afterwards (via SetActiveWindow) in case it was related to whether selected items are still shown as selected when the listview doesn't have focus (adding LVS_SHOWSELALWAYS to the window style flags should do this BTW)...

Short answer: I couldn't figure it out.

Did you create the actual 'LVITEM' structure for that 'state' parameter BTW?


JoshK(Posted 2004) [#31]
No, I just did like 0 and 1.


JoshK(Posted 2004) [#32]
Here's one way. This will clear the list, then you just use the Blitz command to select the gadget items that should be selected:

hwnd=QueryObject(list,1)
flags=GetWindowLong(hwnd,-16)
SetWindowLong hwnd,-16,flags-LVS_SINGLESEL
SelectGadgetItem list,0
SetWindowLong hwnd,-16,flags


How about making it so the list doesn't scroll to the bottom of the selection? I find that extremely annoying!


JoshK(Posted 2004) [#33]
I got better results by adding a gadget at the end, selecting it, and deleting it. However, this causes the list to scroll to the very end. Anyone know how to move the list, or set it so it doesn't auto-scroll? NOSCROLL just turns off the ability to scroll at all!


Snarty(Posted 2004) [#34]
You need to use the Type access like in my example way up there ^^


JoshK(Posted 2004) [#35]
Here ya go. This works really well.

This concludes my problem. Thanks, both of ya:

Function SelectListViewItem(gadget,index,state=1)
hWND=QueryObject(gadget,1) 
CItem.Item=New Item 
Pointer=Int(CItem)+20 
CItem\Index=index 
CItem\SubIndex=0 
CItem\Mask=8 
CItem\StateMask=2
CItem\State=state*2
SendMessage hWND,$1000+43,index,Pointer
Delete CItem
End Function



BlitzSupport(Posted 2004) [#36]
Bollix... screwed-up post.


BlitzSupport(Posted 2004) [#37]
Good work... I can't believe I forgot about this though!


you just use the Blitz command to select the gadget items that should be selected



Doh!


JoshK(Posted 2004) [#38]
Yeah, but doing that scrolls the list, which I don't want. You're better off using the func I posted.


CodeGit(Posted 2004) [#39]
@Halo

Is there any chance that you would share this code with the community?

thanks