What I’m after.
The bold yellow item is the item I double clicked and is now marked with a different style and bold text.
ListBox is a somewhat limited dotnet control. I suggest switching to ListView to get the control you’re after.
Is there a way to ListView so the user can click and drag the cursor to select multiple items in the listview? Rather than the user having to hold ctrl and click multiple items?
Alright so here is what I have so far.
What works:
- Double click bolds layer
- Layers being activated and deactivated by double clicking on them
Questions:
- How to do click and drag selection to select multiple items in the list
- How to keep highlighted (selected items) in list when the rollout has lost focus?
try(destroydialog ::rlListView)catch()
rollout rlListView "Listview"
(
-- This is array holds the content for our Listview items. An oversimplification, probably.
local _itemsArr = for i = 1 to 10 collect #("Item "+ i as string,false)
local activeFont = dotNetObject "System.Drawing.Font" "Microsoft Sans Serif" 11 (dotNetClass "System.Drawing.FontStyle").Bold (dotNetClass "System.Drawing.GraphicsUnit").Pixel
local inactiveFont = dotNetObject "System.Drawing.Font" "Microsoft Sans Serif" 11 (dotNetClass "System.Drawing.FontStyle").Regular (dotNetClass "System.Drawing.GraphicsUnit").Pixel
--pickbutton pbtnSourceObj "Source Object" pos:[10,10] width:180 height:20
dotNetControl lvTracks "System.Windows.Forms.ListView" width:180 height:220 pos:[10,40]
button btnActivate "Activate" width:90 height:20 pos:[10,10]
button btnDeactivate "Deactivate" width:90 height:20 pos:[100,10]
fn fnListviewTracksSetup = (
lvTracks.clear() -- wipe out the contents of the entire listview so that we're redrawing it from scratch each time this function is called
lvTracks.View = (dotNetClass "System.Windows.Forms.View").Details -- this is what allows the grid-like format to be used
lvTracks.fullRowSelect = true -- When item is clicked, all columns in that item are selected
lvTracks.gridLines = true-- turn on the grid lines
lvTracks.HideSelection= false -- When this ListView loses the focus, it will still show what's selected
lvTracks.BorderStyle=lvTracks.BorderStyle.FixedSingle -- make the border a flat solid color instead of the Windows 3D look
lvTracks.HeaderStyle=lvTracks.HeaderStyle.Nonclickable -- Flattens the headers a bit (although they're still somewhat 3D) and keeps them from being clickable
lvTracks.backColor=lvTracks.backColor.FromArgb 250 250 250 -- Soften the background intensity a bit
lvTracks.Columns.add "Tracks" 180 -- create a couple of columns and optionally specify their width
-- Create the items that will go in the list view
theRange = #()
for i = 1 to _itemsArr.count do (
li = dotNetObject "System.Windows.Forms.ListViewItem" _itemsArr[i][1] --collect name
append theRange li
)
lvTracks.Items.AddRange theRange -- and then put them there
)
on lvTracks MouseDoubleClick s a do
(
clearlistener()
if (item = s.GetItemAt a.x a.y) != undefined do
(
itm = (lvTracks.HitTest (dotNetObject "System.Drawing.Point" a.x a.y))
trackName = itm.item.text
idx = (itm.item.index + 1) --dotnet lists start with 0
if _itemsArr[idx][2] == false then
(
itm.item.font = activeFont -- new bold font
_itemsArr[idx][2] = true
)else(
itm.item.font = inactiveFont -- new bold font
_itemsArr[idx][2] = false
)
)
)
on rlListView open do
(
fnListviewTracksSetup()
)
)
createdialog rlListView 200 270
Well I have not got the drag selection to work yet.
How do I get a list of the selected items “indexes” when I press a button?
Drag selection seems to work fine for me…
You can get the selected indices by using the .SelectedIndices property.
Also, a few very minor suggestions for the sake of code simplicity (I admit, some if might be a matter of taste)
-
Don’t hold a separate array for the item’s ‘bolded’ state, use the .tag property of the listviewitem.
-
All of this:
theRange = #()
for i = 1 to _itemsArr.count do (
li = dotNetObject "System.Windows.Forms.ListViewItem" _itemsArr[i][1] --collect name
append theRange li
)
lvTracks.Items.AddRange theRange -- and then put them there
can then be condensed to:
lvTracks.Items.AddRange (for i in _itemsArr collect (dotNetObject "ListViewItem" i))
- In this line:
itm = (lvTracks.HitTest (dotNetObject "System.Drawing.Point" a.x a.y))
there’s no need to create a new point, you already have the property a.location which is a point.
Also keep in mind that the hit test might return a hit area which contains no item, so you must check if the item is undefined first.
I’m all about condensing the code. So I do appreciate the snippets of code which explain and condense.
I’m not quite sure I’m completely understanding the a.location part that you mentioned.
Aren’t i testing it for being undefined when doing this…
if (item = s.GetItemAt a.x a.y) != undefined do ...
As for the drag selection, that only works if the users has a empty cell to click in. But if you click in a cell where and item exists and then try to drag to make a larger selection it doesn’t work. How would that be fixed?
the variable a in your code is of type MouseEventArgs. This type has a property .location which itself is already the System.Drawing.Point you are creating, so it is redundant to call dotNetObject “System.Drawing.Point” a.x a.y when instead you can just call a.location.
Oh right, so you are… in that case, why do you need the hitTest at all?
I get what you’re saying but for some reason for me it seems to work even in that case, I’m not sure why. In any case if it’s not working for you, you can always implement custom selection logic.
The listview stuff is just for some reason not clicking for me.
I’m not understanding the simple method of getting the selected indexes?
on btnActivate pressed do
(
_arr = lvTracks.SelectedIndices
_selIdxs = for i = 1 to _arr.count collect _arr[i]
print _selIdxs
)
Aside from that I cleaned up the a.location part of it the script.
on lvTracks MouseDoubleClick s a do
(
itm = s.GetItemAt a.x a.y
if itm != undefined do
(
trackName = itm.text
idx = (itm.Index + 1) --dotnet lists start with 0
if _itemsArr[idx][2] == false then
(
itm.font = activeFont -- new bold font
)else(
itm.font = activeFont -- new bold font
)
)
)
Where is the best place to set the tag property? When the list is first populated?
I see no reason to pre-initialize it:
on lvTracks MouseDoubleClick s a do
(
itm = s.GetItemAt a.x a.y
if itm != undefined do
(
itm.tag = if itm.tag != true then true else false
itm.font = if itm.tag then activeFont else inactiveFont
)
)
You have to make a distinction between an array, which is automatically converted to a mxs array and can be indexed in base-1 using standard maxscript syntax, and a collection, which is a dotnet construct, and must be accessed in base-0 (first index is 0) using its own syntax.
you can see that .selectedIndices is a collection on msdn:
http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.selectedindices.aspx
notice the type is SelectedIndexCollection.
this is how you would print out the selected indices:
on btnActivate pressed do
(
_arr = lvTracks.SelectedIndices
_selIdxs = for i = 1 to _arr.count collect _arr[B].item[/B][i-1] --need the i-1 because the collection is 0-based.
print _selIdxs --note that the returned indices are also 0-based because the .items of the listview are also a dotnet collection!
)