将对象列表转换为枚举项C#

时间:2018-11-18 19:53:33

标签: c# xml unity3d

如何将Class对象列表传递给枚举?

当前,我有一个从XML文件中提取数据的类结构。它们是这样的:

[XmlRoot("ManufacturerContainer")]
public class ManufacturerContainer
{
    [XmlArray("Manufacturers")]
    [XmlArrayItem("Manufacturer")]
    public List<Manufacturer> Manufacturers { get; set; }
}

public class Manufacturer
{
    [XmlAttribute("name")]
    public string Name { get; set; }

    [XmlElement("Model")]
    public List<Model> Models { get; set; }    
}

public class Model
{
    [XmlAttribute("name")]
    public string Name { get; set; }

我想做的是创建2个枚举下拉框,“模型”下拉列表取决于制造商。您将选择制造商,并且型号将更新。您如何动态更改枚举?

谢谢!

1 个答案:

答案 0 :(得分:0)

一个不能/不应该在运行时尝试更改enum。简单地说,enum相当于constant字典,您无法在运行时对其进行修改。

即使可能,任何代码(例如switch-case或取决于该枚举的if-else可能也会失败)。


没有简单的快速解决方案。对于您想做的事情,您将必须深入了解Unity Editor脚本并使用string[]作为选项(例如,此处的名称)和

创建自定义Popup

首先,我将向您的类添加一些getter方法(所有示例中的Name都是唯一的,并且任何制造商或一个制造商内的模型都不具有相同的名称):

[XmlRoot("ManufacturerContainer")]
public class ManufacturerContainer
{
    [XmlArray("Manufacturers")]
    [XmlArrayItem("Manufacturer")]
    public List<Manufacturer> Manufacturers { get; set; }

    // Get all available Manufacturers' names
    public IEnumerable<string> GetAvailableManufacturers()
    {
        // use Linq to get a list of only the names
        return Manufacturers.Select(manufacturer => manufacturer.Name);
    }

    // Get a specific Manufacturer by name
    public Manufacturer GetManufacturerByName(string name)
    {
        // a little sanaty check
        if (GetAvailableManufacturers().Contains(name))
        {
            // again use Linq to return the Manufacturer by name
            // (provided that they are unique of course)
            return Manufacturers.Find(manufacturer => string.Equals(name, manufacturer.Name));
        }

        Debug.LogWarningFormat("No Manufacturer with name {0} found!", name);
        return null;
    }

    // and finally get a specific Model by manufacturer and model name
    public Model GetModelByName(string manufacturerId, string modelId)
    {
        // first get the according manufacturer
        var manufacturer = GetManufacturerByName(manufacturerId);

        if (manufacturer != null)
        {
            // get the model by name (see method below)
            return manufacturer.GetModelByName(modelId);
        }

        Debug.LogWarningFormat("Couldn't get Manufacturer with id {0}", manufacturerId);
        return null;

        // then the according model
    }
}

public class Manufacturer
{
    [XmlAttribute("name")] public string Name { get; set; }

    [XmlElement("Model")] public List<Model> Models { get; set; }

    // Get all available Models' names
    public List<string> GetAvailableModels()
    {
        return Models.Select(model => model.Name).ToList();
    }

    // The same as for getting a Manufacturer by name
    public Model GetModelByName(string name)
    {
        // a little sanaty check
        if (GetAvailableModels().Contains(name))
        {
            // again use Linq to return the Model by name
            // (provided that they are unique of course)
            return Models.Find(model => string.Equals(name, model.Name));
        }

        Debug.LogWarningFormat("No Manufacturer with name {0} found!", name);
        return null;
    }
}

public class Model
{
    [XmlAttribute("name")] public string Name { get; set; }
}

现在您可以

  • 获取制造商及其型号的所有可用选项
  • 以后按ID查找特定的制造商/型号(例如,如果您需要有关它们的更多信息等)

所以下一步就是根据您的需要为您的班级写一个CustomEditorCustomPropertyDrawer

我将为您提供有关PropertyDrawer外观的非常简化的想法。不幸的是,编辑器脚本总是会变得越来越庞大,并且您必须手动执行许多操作:

[CustomPropertyDrawer(typeof(ModelSelection))]
public class ModelSelectionDrawer : PropertyDrawer
{
    // Lets just say we want the property to have a height of 3 lines
    // one for the label, and one for each dropdown
    // It makes it easier for now to place the dropdowns
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return EditorGUIUtility.singleLineHeight * 3;
    }

    // Kind of like the Inspectors update method
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // Using BeginProperty / EndProperty on the parent property means that
        // prefab override logic works on the entire property.
        EditorGUI.BeginProperty(position, label, property);

        // Draw label
        EditorGUI.LabelField(position, label);
        // go to next line
        position.y += EditorGUIUtility.singleLineHeight;

        // Don't make child fields be indented
        var indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;


        // first of all link the according variables
        var _manufacturerId = property.FindPropertyRelative("ManufacturerId");
        var _modelId = property.FindPropertyRelative("ModelId");

        // You somehow need access to the reference of ManufacturerContainer
        // I will assume here you now how to make it accessable all over your scene 
        // and pretend it is already stored in this variable

        // I just create one right here in order to have something to show already
        var container = new ManufacturerContainer
        {
            Manufacturers = new List<Manufacturer>
            {
                new Manufacturer
                {
                    Name = "Man_1",
                    Models = new List<Model>
                    {
                        new Model
                        {
                            Name = "Model_A"
                        },
                        new Model
                        {
                            Name = "Model_B"
                        }
                    }
                },

                new Manufacturer
                {
                    Name = "Man_2",
                    Models = new List<Model>
                    {
                         new Model
                         {
                             Name = "Model_C"
                         },
                         new Model
                         {
                             Name = "Model_D"
                         }
                    }
                }
            }
        };

        // Now you would get the available manufacturers
        var availableManufacturers = container.GetAvailableManufacturers().ToArray();

        // Make the dropdown field for manufacturer
        // Get the position for this field
        var field1_rect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
        // add a label
        field1_rect = EditorGUI.PrefixLabel(field1_rect, new GUIContent("Manufacturer"));
        position.y += EditorGUIUtility.singleLineHeight;

        // get index of currently selected manufacturer
        var currentManIndex = availableManufacturers.ToList().IndexOf(_manufacturerId.stringValue);
        // clamp to minimum 0 (it returns -1 if the string is incorrect/not found in the list)
        currentManIndex = Mathf.Max(currentManIndex, 0);

        // problem it returns an int -> index
        // => if you add or delete an item later in middle the XML it breaks
        // you would have to fix this on your own
        var newManIndex = EditorGUI.Popup(field1_rect, currentManIndex, availableManufacturers.ToArray());

        // now we have the new index but we have to write back the selected object to the target class
        _manufacturerId.stringValue = availableManufacturers[newManIndex];



        // Now that you have a manufacturer selected you can get and select the model
        var availableModels = container.GetManufacturerByName(_manufacturerId.stringValue).GetAvailableModels().ToArray();

        // Make the dropdown field for manufacturer
        // Get the position for this field
        var field2_rect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
        // add a label
        field2_rect = EditorGUI.PrefixLabel(field2_rect, new GUIContent("Model"));
        position.y += EditorGUIUtility.singleLineHeight;

        // get index of currently selected manufacturer
        var currentModIndex = availableModels.ToList().IndexOf(_modelId.stringValue);
        // also clamp this to minimum 0
        currentModIndex = Mathf.Max(currentModIndex, 0);


        // problem it returns an int -> index
        // => if you add or delete an item later in middle the XML it breaks
        // you would have to fix this on your own
        var newModIndex = EditorGUI.Popup(field2_rect, currentModIndex, availableModels.ToArray());

        // now we have the new index but we have to write back the selected object to the target class
        _modelId.stringValue = availableModels[newModIndex];

        // Set indent back to what it was
        EditorGUI.indentLevel = indent;

        EditorGUI.EndProperty();
    }
}

如前所述,这只是一个示例,肯定还有许多其他检查和应添加的内容。但是您稍后将在诸如此类的任何类中使用此属性

public class Blarf : MonoBehaviour
{
    public ModelSelection selection;
}

,它在编辑器中将如下所示:

enter image description here

剩下的取决于您选择了这两个值之后您要做什么。您可以例如只是想使用{p> 1获得相应的Model

// reference to your container
container.GetModelByName(selection.ManufacturerId, selection.ModelId);