ListView (.NET)
From Tech Artists Wiki
Contents |
[edit] Introduction
[edit] What is a ListView?
A thorough explanation of a ListView can be found in the MSDN Reference. The "Remarks" section has a thorough explanation. In terms of 3dsmax, a ListView:
- Like all .NET controls, is only available in 3dsmax 9 and above
- Indices are 0 based (like the rest of the programming world), not 1 based, like 3dsmax
- Gives you rows of text data that can be divided into columns
- Row contents must be text
- Individual cells cannot be selected individually, only rows
- First column can be checkboxes
- Can be replaced with an image instead of a checkbox
- Can also have text
- Checkboxes can only be created in the first column
[edit] Why use a ListView?
The main benefit of a ListView as opposed to something like a Listbox (standard 3dsmax control), is it gives you many more options and much more control over how your data is displayed and manipulated. Given a certain amount of coding and ingenuity, I suppose anything is possible- but if you need things such as columns, drag and drop, checkboxes, colored or unique fonts, etc., a ListView is the way to go. The only drawback is it isn't backwards compatible with Max8 and earlier.
If your tool needs anything other than text, or you absolutely need to change text of the cell you click on (like a spreadsheet), you should look into a DotNet DataGrid.
[edit] Using the ListView
[edit] Creating
The existing 3dsmax tutorials for ListViews, and the sample scripts linked to at the end of this tutorial, go over the actual code required to create a ListView. The conceptual aspect will be covered here.
You create a ListView as you would any other UI Control, with the syntax
dotNetControl lv "System.Windows.Forms.ListView"
To really use it, though, you need to initialize it with a function or event. Basically, you create new columns and give them names. Then, you create a list item and fill in each successive column with a subitem. This idea of items and subitems is important, since it is how you will access and change data in the ListView later on.
When you initialize, you also want to set properties for the entire ListView. Some of these properties are only applicable to the entire ListView- each individual item can also have its own properties, and each subitem its own. For example, the LV's .backColor can be Red:
lv.backColor = lv.backColor.Red()
and the second Item's .backColor can be Green:
lv.Items.Item[1].backColor = lv.backColor.Green() -- Remember that listview arrays are 0-based
and the second Item's fourth SubItem can be yellow:
lv.Items.Item[1].SubItems.Item[3].backColor = lv.backColor.Yellow()
You will understand more of what that code means in the next section.
[edit] Accessing Data
What is described above is really the core of accessing data in the listview. Our lv will have properties:
lv.fullRowSelect lv.backColor lv.checkBoxes lv.Items
This .Items property is where all the items for the listview are stored. This .Items property also has properties, such as
lv.Items.count
which will give you the number of items in the listview. However, .Items also has a property, .Item, which is an array of what it holds. You access this with a number (0-based):
lv.Items.Item[0] lv.Items.Item[3]
This will give you a row in the listview. The .Item member also has properties:
lv.Items.Item[0].checked lv.Items.Item[0].backColor
Setting these will set the property for the entire row. However, .Item[] also has a property, which is a collection of the data in that row:
lv.Items.Item[0].SubItems
And, like with .Items, you access the individual subitems with .Item. So:
lv.Items.Item[0].SubItems.Item[1]
will give you the subitem at that index. However, to get the text, you need:
lv.Items.Item[0].SubItems.Item[1].text
So that is how you will read and write text to a listview, usually. There are also listview methods (such as .AddRange) that do this.
One thing to keep in mind is that when using checkboxes, you get/set whether a row is checked with
lv.Items.Item[0].checked
You do not access it through the 0 subitem.
[edit] Events
DotNet controls use event handlers, very similarly to how 3dsmax controls use them. Using 'showproperties()' on the control, or checking out the ListView Properties/Methods/Events page at the end of this page, you can see exactly what the event handlers are.
All DotNet event handlers have an EventArguments, usually abbreviated e or arg in code (the documentation uses the former). However, this arg is rarely (maybe never?) an actual value- it is a DotNet reference. I usually don't use these args in anything except MouseDown events, and even then it isn't necessary. The same way you don't need to actually use the event arguments for MXS controls, you can usually access them- it is just more convenient to use the event argument values. In this case, though, it is probably simpler to not use them. I may revise my position on this when I learn more about DotNet in MXS.
A unique thing about DotNet controls and event handlers to keep in mind is that the event handlers will be called during initialization as well. This is because, even though you 'make' the dotNetControl, you don't make it useful until your initialization code/function. But at that point, the event handlers are active and will be firing off. For example, when you fill in the listview with data, and you check items, the ItemChecked event handler will fire. Something to keep in mind.
[edit] Manipulating Data
[edit] Working with Rows
For collecting data for manipulation, it is trivial to convert a row of data to an array of strings:
itemArray = for i = 0 to lv.Items.Item[0].SubItems.count - 1 collect lv.Items.Item[0].SubItems.Item[i].text
It would probably be worthwhile to put that command, and its inverse, into two functions:
fn itemToArr lv itemInd =
(
itemArray = for i = 0 to lv.Items.Item[itemInd].SubItems.count - 1 collect lv.Items.Item[itemInd].SubItems.Item[i].text
(itemArray)
)
fn arrToItem lv itemInd itemArray =
(
for i = 0 to itemArray.count - 1 do
(
lv.Items.Item[itemInd].SubItems.Item[i].text = itemArray[i + 1]
)
(ok)
)
You can use these functions as a part of another function to turn your entire listview into an array, and back. These and other common functions will be available at the end of the tutorial.
Once you get the data into arrays, or a master array, you can qsort them however you want. There is probably a way to do sorting with some dotNet methods, but I don't know yet- the LV has a .Sorting property and I can get it to sort Ascending and Descending, but not sure what it is based on. I will update the tutorial when I have more info.
[edit] Changing Data
Unfortunately, it is not possible to edit the text in a ListView directly. A real pain. There are two options of differing complexity. The first option is simply to put an EditText (or DotNet Textbox) somewhere on the UI to replace values, one for each Column. Then you can just replace the appropriate subitem text of the selected Item with whatever is entered into the text field. Or you can use spinners, etc. It is very simple to set up.
The second option is to create a text control that covers the SubItem the user clicks (functions to get the Item and SubItem at the Mouse's position included at the end of the tutorial), then replace the text of that item with whatever is entered. This takes substantially more code and skill, and will not be covered in any detail here. This is one of the main ListView restrictions/drawbacks, and it may be worthwhile to look into DataGrids if this is an important requirement.
[edit] How to
[edit] Drag and Drop
[edit] Change Font
[edit] Common Functions
[edit] External Links
- Please see the Tutorials in the MaxScript Help about ActiveX and DotNet ListViews
- See the NET_ListViewWrapper.ms script in stdplugs\stdscripts\ for more examples
- MSDN Reference on ListView
- ListView Properties/Methods/Events
