I am creating this guide since I had trouble grasping the listview at first, seeing many have the same struggle and there not being any good information about them in a blueprint context.
The listview should be pretty straight forward to use once you got the basic grasp of it.
Note!:If a widget is generated for one instance more than once, the engine will crash. So any instance should only be in the listview once.
Note! If the node “Is List Item Selected”, part of the entrywidget class, is used, the engine will crash.
Note! Multi Select Mode seems to be bugged, clicking an item clears the selection so it is not possible to multi-select by click. (implementing custom logic would work).
Following bug reports has been made:
- Is list Item Selected / Expanded crash: Unreal Engine Issues and Bug Tracker (UE-73025) (removed in 4.23)
- Multi select, click clears selection: Unreal Engine Issues and Bug Tracker (UE-72610) (backlogged)
- EditorWidget listview with actor items, if an actor is deleted, trying to update the listview crash: Unreal Engine Issues and Bug Tracker (UE-73014) (fixed)
- When a list view item in UMG is selected using “Set Item Selection” it will get the item of the first entry in the list or the last entry manually selected by the user. Unreal Engine Issues and Bug Tracker (UE-86431)
For versions: 4.21, 4.22
For changes to 4.23 and 4.24, see Reply #13
For using the Listview or Tileview for “inventory”, see Reply #14, after learning the basics.
Contents: (Reply#, Subject index. subject - description)
1,1. Basic Setup - Setting up the EntryWidget Interface and adding it to the ListView.
1,2. **L****istview overview - **Examples of some useful functions.
1,3. **The Pitfall **- Common mistake adding members to the Listview.
1,4. Listview generation - Explaining how EntryWidget works and events firing.
1,5. **Creating a proper list **- Proper method for setting up a very simple list.
1,6. Going advanced - Actor representation - Displaying Actors in the list.
1,7. **Going advanced - Selection **- How selection is displayed in the list.
1,8. **On Item Selection Changed - ListView Or EntryWidget? **- Differences between the selection changed event between the Listview and Entrywidget.
1,9. Going advanced - World selection - World selection and Listview syncing.
1,10. **Going Advanced - Drag & Drop **- Basic Drag and Drop example.
1,11. **Multiselect - **Making a custom multiselect solution.
1,12. **Node Information **- Additional information about specific nodes.
2,1. Treeview - Getting Started - Basic Treeview setup, how to set hierarchy/get children
2,2. **Treeview - Spacing [Backtracking through parent] **- Adding some spacing to children depending on [Actor parenting] hierarchy depth.
2,3. Treeview - Expandable symbol - Changing or hiding a symbol, based on the expanded state and if Item has children.
2,4. **Treeview - Spacing [When getting the items children] **- More universal way of tracking depth, does not rely on Actor parenting.
2,5. **Treeview - Expand only if Selected [revert expansion method] - **Workaround to default expansion behaviour (not recommended)
2,6. Treeview - Don’t change expansion [Override click method] - Workaround to default expansion behaviour (recommended)
12,1. Changes in 4.23
13.1 Listview & Inventory Items - Solution specifically for “inventory Items” often found in rpg games. Display.
13.2 **Drag & Drop solution with mediators **- Solution to have Drag & Drop behaviour with inventory using mediator objects.
1. Basic Setup
Create a widget to hold the listview. For this guide, this is the widget that is added to viewport. Add a listview. You can’t compile now because it wants an entry widget.
Create a widget to be the EntryWidget. Delete the canvas panel and add a Border and a Text block. Then, follow the image steps.
- Note that the function in step 3 called “Get List item Object” has been removed in later versions.
- Go to the Entrywidgets class settings.
- Add the interface “UserobjectListEntry”.
- Note how the Entrywidget now has an interface function. This may be very useful but we need to return a value.
- Add the event “Event On List Item Object Set” which is also part of the interface we added. Right click on the List Item Object and promote it to variable. I renamed it to Item.
- Go back to the function in 3. and return the Item.
- Go to the listview widget, select the listview and the entrywidget you created can now be selected as the entry widget class.
That’s the basic setup done and you are now ready to use the listview.
This is what the EntryWidget looks like:
But from now on, basic umg knowledge is presumed and we will just focus on the listview.
A disclaimer for new users to UE is that this tutorial does not follow best practices and can be inconsistent. My goal is to have as few nodes on as little screen space as possible for screenshot purposes. Some parts of this tutorial uses variable bindings on Text Block text or the Border brush, which is generally not good. Other parts Sets the text for the Text Block or the colour on the Border, which is generally better. Bindings is like a Tick, always checking its value. If you make a big game using this a lot in your UI, you’ll run into performance issues. Setting the value only happens during that event.
There are some other things like how the Item is set during the On List Item Object Set. Maybe, cast it here so you don’t have to cast at multiple other places? Either way, the result is the same, just slightly different execution. The goal here is to teach the overall methodology which once learned, you can use your own solution for.
2. Listview overview
First of all, the listview works with instances. You create any object (Uobject, Actor etc) to add as an item to the listview and the listview will, if needed, create an entrywidget to represent that item.
Here’s a selection of functions that the listview has. Only one for the generated widgets. It is designed to work with the items, the widgets are just there to represent the item.
3. The Pitfall
Now, you might be tempted to use the entrywidget you created to use as an item, changing some properties and expecting the changes to be displayed.
Something like this:
(How to expose variables):
Select the variable and check the options “Instance editable” and “expose on spawn”.
If the node does not display the exposed variables - because you had already added the create node and afterwards checked the boxes - Right-click the node and select “Refresh Nodes”, or create a new node and see if that has it.
You might expect that this will create a listing of numbers from 0 to 10 as the displayed text. But instead the following is the actual result:
In this example a widget was created and added as an Item. The listview then generates widgets to represent those items.The **Item **has some properties changed (the text), the entrywidget - the widget that is displayed - remains default.
While widgets can be used as items, I think it’s mostly confusing for learning. Lets start with something else. (the entrywidget could be used as a slot to which the widget item is added. But that’s a little more advanced as of now).
4. Listview generation
The listview generates a widget for each item that needs to be displayed. When an entrywidget is generated, two events are fired. One in the listview called On Entry Generated. The other in the entrywidget which we saw earlier - “Event On List Item Object Set”.
When scrolling, entrywidgets are dynamically On Entry **Released, **On Entry **Generated, **triggering **On List Item Object Set. **Also **On Item Selection Changed **- IF that item is selected.
5. Creating a proper list
Here the item will be an UObject with a single text property which is set during a loop.
To update the entrywidget, go to the entrywidget and on the event “Event On List Item Object Set” which is currently storing the item, cast the item to the Objecttype added as item and get its text.
Here, an UObject is used to just hold some data. But the item doesn’t have to “just hold data”. The ltem can be any instance and even different classes. So I could have an item list of UObjects, Actors, characters and vehicles. How you format the entrywidget visually is really up to you, with what kind of elements to display.
Now that the basics are out of the way, lets do something more interesting. An Item can be any object, so what if we added some actors and these actors to be listed in the view?
6. Going advanced - Actor representation
For me the actor is just named “Ball” and has a static mesh sphere. These are dragged into the scene, about 10, and in the listviews construct event “get all actors(ball)”. I wouldn’t recommend this for practical use but it is good enough for an example.
Now, we could loop through the array, adding each instance with the “add item”. Or, we can just use the listviews “Set list items”.
The entrywidget will need to cast the item to ball class. For now, the entrywidget only has a textblock and I’m going to set its text to the objects “get display name”.
We could make it more interesting, like randomizing the ball size and adding another textblock to the entrywidget, displaying the instance size. But that’s just more data pulling which we have already learned.
What if, instead, we were able to click on the listview and the corresponding ball is selected? Or Vice verse?
7. Going advanced - Selection
The listview has some inbuilt selection functionality. You can even choose between single and multiple selection in its properties. Single selection for now.
The following image shows a simple setup. When “Event On Item Selection Changed” is called because you clicked on one of the items in the list, one entrywidget will return false and another will return true. One was deselected, the other selected. When debugging, if we only printed the boolean, how would we know which was selected and which was deselected? The impulse might be to include the widget reference as debug data, but that really provides poor information. Instead, try to work with the Items, which is done in the image.
Running the above will show that it works but we really need better representation.
The Entrywidget gets a new variable - “Border Color”, a linear Color which the border brush color is bound to.
The ball has also gotten an event that can be called.
The idea is that when the Item is clicked, change the scale of the Ball to show that it is selected in the world.
Running the above will change the size of the ball actor when a list item is clicked.
But there is a problem. If you scroll down you will eventually see a widget appear, marked as if it is selected.
That is because the widgets are not destroyed when they are scrolled out of view, they are On Entry **Released **and then On Entry **Generated **when needed again. So they still have their old properties. The display name text changed, because we do that “Event On List Item Object Set”, but the border color remains untouched.
So how do we fix that? Well, there are a many ways. There could be a “Reset Properties”-function that is run when Event On List Item Object Set? If we think about this execution logic, it would look like this:
- widget**(s)** generated, setting Item, reseting border color.
- Item selected, changing border color to green. and changing ball size.
- widget scrolled out of view, released, possibly generated again for some other item but that’s fine.
- Selected Item is scrolled back into view - widget generated, setting Item, reseting border color!
- But there’s another event that happens after that. The listview knows that the item is selected so it calls Event On Item Selection Changed on the widget. (only for those items that are selected, not for the ones which are not).
A reset function could work in this case, but would still be an issue for other types of executions. Imagine if, say a tree, would **grow **each time it was **selected. **But then it would also grow each time i was scrolled back into view…
The reason for pointing all of this out, is that it is important to **test **just what all the functions do and that different solutions is needed depending on the situation.
Update: Use the “Event On Item Released” to reset the entrywidget to its default visual.
If an item is selected - changing the ball size - and you scroll down and select another item, the ball size of the previous selected should reset, but it doesn’t!
Because we have implemented behaviour into the EntryWidget, and the entrywidget points at different items - items which might not be represented at the moment - the widgets may not be able to communicate with the relevant item.
This case example should show us that the EntryWidget may not be a proper place to implement item changes to.
So the border color stays, the ball size change goes.
To where? To the listview to handle.
The listview has the event “On Item Selection Changed”, which provides the item and if it is selected or not. But we also want to reset the previously selected ball size. Well, the listview only provides so many functionalities. This is a behaviour we need to implement ourselves. Current setup:
Remember that this is just for single selection. If we wanted multiple selected items the node setup would have to be modified.
8. On Item Selection Changed - ListView Or EntryWidget?
At first glance the Selection Changed events of the treeview and the entry widget may appear very similar, but they are in fact very different.
The EntryWidgets event should be used for setting up visual representation of the entrywidget.
The ListViews event should be used for logic such as informing the Item about its selection state. If the deselected item is relevant, you need to track it manually using a variable, which we have done in a previous example.
9. Going advanced - World selection
So we can select an item in the list to show that it is selected, and store the current selection in a variable for any class to use. But what about clicking on the actor itself?
Well, how do you want to handle that? should it be with the spheres on clicked event? Or a linetrace from the camera?
If we do it with the spheres On Clicked event, then the sphere needs to either send information to -some class- that it was clicked, or have -some class- bind to the ball being clicked.
I’m going to go with a linetrace from the third person character, in the third person template, because that’s just the easy way to do it. The third person character in my case is also the class that creates the listview widget.
Now there is a slight issue.
We know that by clicking on an entrywidget, that item will be selected, telling that actor to change size. To demonstrate the issue, the ball will now print the value of an int, which is incremented if selected and decremented when deselected. So deselected =0, selected =1.
Say I do the following:
- Detect ball through a linetrace.
- Tell the ball that it has been selected.
- Inform the listview to mark the ball as selected.
What now happens is that the listview tells the ball to be selected, again. As you can see a 2 int.
When it comes to selection - if the item needs to know that it is selected - in my experience it is better to go through the listview to have IT perform the selection.
Let’s follow some execution flow with some requirements.
- The character needs to be updated about the selection, say saving the reference in a variable.
- The Item needs to be informed that it has been selected, say becoming highlighted.
- The List needs to be updated about the selection.
First is a selection in the listview.
Second, the item selected though the world. For example, a linetrace from the character. If the Item and character are informed of the selection after the trace, and then the Listview is told to select the item it creates unnecessary logic, which might be problematic.
Third, the item selected though the world. For example, a linetrace from the character. The character informs the Listview to select the Item which triggers the execution of the flow in the first example.
But in the end the setup depends on what you require the selection to do.
10. Going Advanced - Drag & Drop
Drag & drop is as easy to implement for a listview as anything else. If we want to be able to reorder the items then, instead of having the listview handle the items, we need to take more control in the form of an array that we can remove and insert items into.
The entrywidget now has a “Item dropped on” dispatcher which the listview binds to when the entry is generated
Bindings in the Entry Widget
If your Entry Widget has a event dispatcher that the widget holding the listview binds to during the “on entry generated” event, you do not have to unbind them when the widgets are released. Seems like the entrywidget internally unbinds any bindings during the released event.
However, if the Entry Widget binds to event dispatchers - say the item they represent - those have to be unbound manually.
So what about multi select? Well, it’s not looking good: Unreal Engine Issues and Bug Tracker (UE-72610)
I’d expect that holding shift while clicking items would be the default behavior but such a feature is backlogged.
So I have to do it myself. This is going to be somewhat of a brute force and will not be pretty.
There are several methods I have considered but many have met one issue or another. So first of all I’ll list the issues.
- If the function “Set Item Selection” is used, the “On item selection changed” of the ListView will usually not provide the item that was changed, but the first one in the list or the item most recently clicked on.
- When the EntryWidget is pressed ( Mouse button down), it will clear the selection of other items - silently. When mouse button is released, all the items will be marked correctly. That means every time I click, the list flickers/blinks.
there’s also some other annoyances that would take too much space to write about. But to circumvent these problems, first the entrywidgets default clicks can not be used. We need a Button to consume the click. We also have to provide some additional logic to the listview so that we don’t get the wrong Item when “On Item Selection Changed” event fires.
Entrywidget is standard setup.
The following THREE pictures for the setup for the listview.
- A List of Objects to easily track selected Items.
- You have a choice between two methods.
Either you use a boolean to know if the “On Item Selection Changed” might provide the wrong item, which it may when “Set Item Selection” is used.
Or you make your own function to know when Item selection is changed. Both examples are provided but you only need one.
Know that when “On Item Selection Changed” provides the wrong Item, it provides the wrong Items selected state, so another boolean for if the clicked item is selected is needed.
Explanation after the images.
On Entry generated -> Bind to the buttons click event when the entry is generated.
On Clicked ->The Clicked dispatcher provides the Item clicked and we save it as a variable, so that we know what item to use, when we use “Set Item Selection”.
Then Query if shift [or whatever method to enable multi select] is used. If it is, change the selected state. If it isn’t, set the clicked item to be the only selected item.
Change Item Selection -> Selected becomes deselected, deselected becomes selected.
Deselect Item -> Remove the Item from the Selected Items list. Check that the item “On Item Selection Changed” provides may be wrong. Set the clicked items selection boolean to false. Then call “Set Item Selection”, followed by the custom “On Item Selection Changed”.
Select Item -> pretty much same as above.
Select Item (Single) -> an Item was clicked on without holding shift. That should select only that item. So first clear the selection. Know here that using “Clear Selection” for the ListView will make the “On Item Selection Changed” event fire with a null item, therefore the isvalid check later. Then, add the clicked item to the selection and once again prepare for the “On Item Selection Changed” event to fire with a wrong item.
My Custom On Item Selection Changed -> If used, the event that is used to perform whatever logic is needed with the correct Item.
On Item Selection Changed -> If used, Query if the Item is valid. It will not be when calling “Clear Selection” on the Listview. Then query of this event was called internally, or manually using “Set Item Selection”. If it is a valid item, proceed. If not, the Item was set Manually and it was the last clicked item.
Well, that was messy. Hope it makes sense. I’ve played around with it a bit and can’t see any obvious issues. But if you really want MultiSelect, this is probably the easiest solution. By the way, the Listviews selection mode between Single and Multi doesn’t seem to matter. Same behaviour in both cases but change it to Multi anyway.
12. Node information