Unity3D :绑定到没有列表视图的列表
推荐:将NSDT场景编辑器就加入你的3D工具链
3D工具集:NSDT简石数字孪生
绑定到没有列表视图的列表
版本: 2021.3+
可以绑定到没有列表视图的列表。为此,请将每个元素绑定到序列化对象的数组中的项,并跟踪数组大小的值。在某些情况下,数组大小可能会更改,例如撤消或重置操作。
此示例演示如何在没有 ListView 的情况下绑定到列表。
示例概述
本示例创建一个 TexturePreviewElements 列表,并将该列表绑定到 Texture2D 对象的基础列表。

您可以在此 GitHub 存储库中找到此示例创建的已完成文件。
先决条件
本指南适用于熟悉 Unity 编辑器的开发人员,用户界面
工具包和 C# 脚本。在开始之前,请熟悉以下内容:
- 可视化树
 - 用户体验
 BindProperty()TrackPropertyValueScrollView
创建包含列表的对象
创建包含列表的 C# 类。此列表是绑定的目标。
- 使用任何模板在 Unity 中创建项目。
 - 在你的项目窗口
,创建一个名为以存储所有文件的文件夹。bind-to-list-without-ListView 
创建一个名为的 C# 脚本,并将其内容替换为以下内容:TexturePackAsset.cs
using System.Collections.Generic;
using UnityEngine;
namespace UIToolkitExamples
{
    [CreateAssetMenu(menuName = "UIToolkitExamples/TexturePackAsset")]
    public class TexturePackAsset : ScriptableObject
    {
        public List<Texture2D> textures;
        public void Reset()
        {
            textures = new() { null, null, null, null };
        }
    }
}创建自定义控件并设置其样式
使用 C# 创建自定义控件,该控件表示对 2D 纹理资产的引用,并使用 USS 设置其样式。
- 在该文件夹中,创建一个名为 的文件夹。
Editor - 在“编辑器”文件夹中,创建一个名为 的 C# 脚本。
TexturePreviewElement.cs - 将 的内容替换为以下内容:
TexturePreviewElement.cs 
using System;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;
namespace UIToolkitExamples
{
    public class TexturePreviewElement : BindableElement, INotifyValueChanged<Object>
    {
        public new class UxmlTraits : BindableElement.UxmlTraits { }
        public new class UxmlFactory : UxmlFactory<TexturePreviewElement, UxmlTraits> { }
        public static readonly string ussClassName = "texture-preview-element";
        Image m_Preview;
        ObjectField m_ObjectField;
        Texture2D m_Value;
        public TexturePreviewElement()
        {
            AddToClassList(ussClassName);
            // Create a preview image.
            m_Preview = new Image();
            Add(m_Preview);
            // Create an ObjectField, set its object type, and register a callback when its value changes.
            m_ObjectField = new ObjectField();
            m_ObjectField.objectType = typeof(Texture2D);
            m_ObjectField.RegisterValueChangedCallback(OnObjectFieldValueChanged);
            Add(m_ObjectField);
            styleSheets.Add(Resources.Load<StyleSheet>("texture_preview_element"));
        }
        
        void OnObjectFieldValueChanged(ChangeEvent<Object> evt)
        {
            value = evt.newValue;
        }
        public void SetValueWithoutNotify(Object newValue)
        {
            if (newValue == null || newValue is Texture2D)
            {
                // Update the preview Image and update the ObjectField.
                m_Value = newValue as Texture2D;
                m_Preview.image = m_Value;
                // Notice that this line calls the ObjectField's SetValueWithoutNotify() method instead of just setting
                // m_ObjectField.value. This is very important; you don't want m_ObjectField to send a ChangeEvent.
                m_ObjectField.SetValueWithoutNotify(m_Value);
            }
            else throw new ArgumentException($"Expected object of type {typeof(Texture2D)}");
        }
        public Object value
        {
            get => m_Value;
            // The setter is called when the user changes the value of the ObjectField, which calls
            // OnObjectFieldValueChanged(), which calls this.
            set
            {
                if (value == this.value)
                    return;
                var previous = this.value;
                SetValueWithoutNotify(value);
                using (var evt = ChangeEvent<Object>.GetPooled(previous, value))
                {
                    evt.target = this;
                    SendEvent(evt);
                }
            }
        }
    }
}- 在编辑器文件夹中,创建一个名为 的文件夹。
Resources 
2. 在“资源”文件夹中,创建一个名为的样式表,并将其内容替换为以下内容:texture_preview_element.uss
.texture-preview-element {
    width: 200px;
    height: 200px;
}
.texture-preview-element > .unity-image {
    flex-grow: 1;
}创建自定义编辑器并设置绑定
使用创建资产的 C# 脚本创建自定义编辑器。
若要在 UI 中的纹理数更改时更改纹理列表的大小,请调用该方法并遍历序列化列表中的条目列表。TexturePreviewElementsSetupList()
要将每个纹理绑定到纹理列表,请使用属性名称 调用 BindProperty()。TexturePreviewElementTexturePackAsset.textures
在“编辑器”文件夹中,创建一个名为的 C# 脚本,并将其内容替换为以下内容:TexturePackEditor.cs
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace UIToolkitExamples
{
    [CustomEditor(typeof(TexturePackAsset))]
    public class TexturePackEditor : Editor
    {
        [SerializeField]
        VisualTreeAsset m_VisualTreeAsset;
        public override VisualElement CreateInspectorGUI()
        {
            var editor = m_VisualTreeAsset.CloneTree();
            var container = editor.Q(className: "preview-container");
            SetupList(container);
            // Watch the array size to handle the list being changed        
            var propertyForSize = serializedObject.FindProperty(nameof(TexturePackAsset.textures) + ".Array");
            propertyForSize.Next(true); // Expand to obtain array size
            editor.TrackPropertyValue(propertyForSize, prop => SetupList(container));
            editor.Q<Button>("add-button").RegisterCallback<ClickEvent>(OnClick);
            return editor;
        }
        void SetupList(VisualElement container)
        {
            var property = serializedObject.FindProperty(nameof(TexturePackAsset.textures) + ".Array");
            var endProperty = property.GetEndProperty();
            property.NextVisible(true); // Expand the first child.
            var childIndex = 0;
            // Iterate each property under the array, and populate the container with preview elements
            do
            {
                // Stop if you reach the end of the array
                if (SerializedProperty.EqualContents(property, endProperty))
                    break;
                // Skip the array size property
                if (property.propertyType == SerializedPropertyType.ArraySize)
                    continue;
                TexturePreviewElement element;
                // Find an existing element or create one
                if (childIndex < container.childCount)
                {
                    element = (TexturePreviewElement)container[childIndex];
                }
                else
                {
                    element = new TexturePreviewElement();
                    container.Add(element);
                }
                element.BindProperty(property);
                ++childIndex;
            }
            while (property.NextVisible(false));   // Never expand children.
            // Remove excess elements if the array is now smaller
            while (childIndex < container.childCount)
            {
                container.RemoveAt(container.childCount - 1);
            }
        }
        void OnClick(ClickEvent evt)
        {
            var property = serializedObject.FindProperty(nameof(TexturePackAsset.textures));
            property.arraySize += 1;
            serializedObject.ApplyModifiedProperties();
        }
    }
}
2. 创建一个调用的 UI 文档,并将其内容替换为以下内容:texture_pack_editor.uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xmlns="UnityEngine.UIElements" example="UIToolkitExamples" editor-extension-mode="True">
    <ui:ScrollView>
        <ui:VisualElement class="preview-container" style="flex-wrap: wrap; flex-direction: row; justify-content: space-around;" />
    </ui:ScrollView>
    <ui:Button name="add-button" text="Add" />
</ui:UXML>3. 在“项目”窗口中,选择“纹理包编辑器.cs”。
4. 将 texture_pack_editor.uxml 拖到 可视化树资源检查员
.
测试绑定
- 从菜单中,选择“资产”>“创建> UIToolkit示例”>“纹理包资产”。这将创建一个名为“新建纹理包资源”的资产。
 - 在“项目”窗口中,选择“新建纹理包资源”。这将在检查器中显示四个 TexturePreviewElement 元素。
 - 将 2D 图像资源分配给这些元素,或使用“添加”按钮添加新元素。如果在检查器 UI 中进行更改,则对象的属性会更改。
TexturePackAsset.textures 
由3D建模学习工作室整理翻译,转载请注明出处!