Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Resolved How can I override the style of a ListView item?

Discussion in 'UI Toolkit' started by Chuck_S, Mar 15, 2021.

  1. Chuck_S

    Chuck_S

    Joined:
    Dec 11, 2018
    Posts:
    15
    I found this post asking how to set the style of a ListView element, and it was supremely helpful, but I've got an issue where setting the style in the MakeItem function doesn't appear to be overriding the style set by the item itself.

    I've got a testItem where some text is displayed, but to keep things legible I put the text on a white background. The white background is set with a style sheet for the test item.

    I've tried overriding the testItem style sheet two ways: the first was to add a list view subsection to the actual testItem style sheet. I thought this would work as described in the post I linked, but that doesn't work. My testItem style "test-item-style" has

    Code (USS):
    1. .test-item-style {
    2.     background-color: rgba(255, 255, 255, 255);
    3. }
    4. .test-item-style.unity-list-view__item {
    5.     background-color: rgba(255, 0, 0, 255);
    6. }
    7.  
    but that doesn't set the testItem style when the testItem becomes a "child" of the ListView.

    I then tried adding a "listview-item" style sheet when the item is made

    Code (USS):
    1. .listview-item.unity-list-view__item {
    2.     background-color: rgba(255, 0, 0, 255);
    3. }
    4.  
    Code (CSharp):
    1. private VisualElement MakeItem()
    2. {
    3.     var testItem = testItemTemplate.CloneTree();
    4.     testItem.AddToClassList("listview-item");
    5.     return testItem;
    6. }
    If I have the ListView's itemHeight set too large then I can see that the background behind my testItem is doing what I want, but the "listview-item" style sheet isn't overriding the actual testItem's style sheet.

    The only way I can seem to get the background set correctly for the items in the ListView is if I remove the style sheet from the TestItem. Then there's nothing setting the background color, so the style set in the MakeItem function "wins" and the background is as-intended.

    This is all great, but I want to use the TestItem in other places, too, not just the ListView, and I need the background white (or some other readable color) where it's used in those other places. I don't want to have to go around and set the background everywhere I use the TestItem, I'd like to just override the TestItem's background style when I need to, but again I'm not sure how I get at it to override it.

    :EDIT:

    I was reading a bit more about this while investigating with the UIDebugger. I found that the instantiated template is going into a "TemplateContainer" and the style I'm assigning in the MakeItem function is being applied to the TemplateContainer. @uDamian explained here that they are instantiated as TemplateContainers, "because the UXML can have multiple elements at its root."

    This would be fine for me, except I can't assign stylesheets at the UXML/TemplateContainer level. This leaves me with no clean way to override the style sheet except by instantiating the VisualTreeAsset, then knowing what inside there needs to get overwritten. I guess it's doable but doesn't feel right.

    If there's a better way I'd love to hear it.

    :EDIT 2:

    Wrote that last night, tried it today, doesn't work quite exactly as I thought. It's not enough to know what inside the TemplateContainer needs to get overwritten - I need to actually go in and remove the style sheet I want to "override." Here's how I got the functionality I want.


    Code (CSharp):
    1. private VisualElement MakeItem()
    2. {
    3.     var testItemTemplateContainer = testItemTemplate.CloneTree();
    4.     testItemTemplateContainer .AddToClassList("listview-item");
    5.     var testItem = testItemTemplateContainer.Q<TestItemViewModel>("test-item");
    6.     testItem.RemoveFromClassList("test-item");
    7.     return testItemTemplateContainer;
    8. }
    I tried it a different way first, by specifying a "child" style: ".listview-item.test-item" and that was able to override the "test-item" style, but the ".listview-item.unity-list-view__item" style was never applied. When I set the ".listview-item.test-item" background to transparent, I got the "default" appearance of the ListView hover/selected states instead of the ".listview-item.unity-list-view__item:hover" and ".listview-item.unity-list-view__item:selected" backgrounds I had set. If I went into the element and removed its "test-item" style, and then also removed the ".listview-item.test-item" style, THEN I got my unity-list-view__item states to display correctly.


    This is my first foray into "front-end" development, and it's been kicking my butt. I've never used CSS or anything like this before, so I don't know what the expected behaviors are for stuff like this. I can't tell if this is a bug or not. By the name "cascading style sheets," I would expect the last-applied thing to take precedent, so maybe this is on purpose? I.e., we draw the VisaulElement frame, then the ListView container, then the ListView item, and then my TestItem is (inside of?) the ListView item.

    Having to reach into the TestItem, know in advance what its style sheet is, and then remove it feels dirty to me, but again I'm a novice on this end of things so maybe that's just "how it's done." I would love some feedback on this, whatever the case.
     
    Last edited: Mar 16, 2021
  2. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    666
    Have you tried a selector like this:
    .listview-item > .test-item
     
  3. Chuck_S

    Chuck_S

    Joined:
    Dec 11, 2018
    Posts:
    15
    @JuliaP_Unity YES! That got it. Kind of. I tried doing the following:

    Code (CSharp):
    1. .listview-item > .test-item
    2. .listview-item > .test-item:hover
    3. .listview-item > .test-item:selected
    and, while it worked for the general appearance and hover, it did NOT work for :selected. What I wound up having to do (maybe this is the wrong way to do it?) was to use the .listview-item > .test-item and set the background color there to transparent. When I did that, then the .listview-item.unity-list-view__item, .listview-item.unity-list-view__item:hover, and importantly the .listview-item.unity-list-view__item:selected and .listview-item.unity-list-view__item--selected:hover all worked correctly.

    Unrelated question - how are you getting your inline code formatting? The editor option for "inline code" here insists on putting "inline" code on new lines.

    Also, to anyone else that comes along and reads this, the spaces on either side of the > are critical. If you try .listview-item>.test-item, when you push enter it'll convert it to .listview-item.test-item, which does NOT get the functionality discussed. When you enter it with a space on either side then it gets converted in the StyleSheets box to be [.list-item] > [.test-item], with the ">" in a white color.
     
  4. i_cassell

    i_cassell

    Joined:
    Apr 6, 2019
    Posts:
    21
    1.I can't add the child class in Style ClassList, I have already add this selector in my .uss
    upload_2021-4-8_11-46-35.png
    2.When I use .
    Code (CSharp):
    1. .AddToClassList
    it also don't work.Should I add parent class? upload_2021-4-8_11-49-24.png
     
  5. uMathieu

    uMathieu

    Unity Technologies

    Joined:
    Jun 6, 2017
    Posts:
    384
    There is no :selected pseudo selector in uss, the listView will set the "unity-list-view__item--selected" class to the selected item's corresponding element.

    The Style Class List can't contain complete selectors, it only contains classes specific to this element only.

    Spaces in selectors have semantic meaning so they're incredibly important:
    • .LogType > .Error will match any element that contains the class "Error" and that has a direct parent that has the class "LogType" in its classlist
    • .LogType .Error will match any element that contains the class "Error" and that has an ancestor that has the class "LogType" in its classlist
    • .LogType.Error will match any element that contains both the class "Error" and "LogType" in its classlist
     
    craftsmanbeck likes this.