c# - 将对象列表转换为枚举条目c#
问题描述
如何将 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 个枚举下拉框,模型下拉框取决于制造商。您将选择制造商,模型将更新。您如何动态更改枚举?
谢谢!
解决方案
不能/不应该尝试enum
在运行时更改。用简单的话说它enum
有点像一个constant
你不能在运行时改变的字典。
即使可以,任何代码(例如 switch-case 或任何取决于该枚举的 if-else 都可能失败)。
没有简单快速的解决方案。对于您想要做的事情,您必须深入了解 Unity 编辑器脚本并使用as 选项(例如此处的名称)创建您的自定义Popup和string[]
首先,我会为您的类添加一些 getter 方法(所有这些都假设Name
您的示例中的 s 是唯一的,并且一个制造商中的制造商或型号都没有相同的名称):
[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 查找特定的制造商/型号(例如,如果您需要有关它们的更多信息等)
因此,下一步是根据您的需要为您的类编写CustomEditor或CustomPropertyDrawer 。
我将为您提供一个关于 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;
}
它在编辑器中看起来像这样:
其余的取决于选择这两个值后您的目标是什么。例如,您可能只是想获得相应的Model
使用
// reference to your container
container.GetModelByName(selection.ManufacturerId, selection.ModelId);
推荐阅读
- r - 如何按类别分隔 R 中的数据
- python - 我正在将一个模块导入另一个模块以在 tkinter 中创建树视图,但它向我显示错误 NameError: name 'treeview' is not defined
- rust - 针对元组的模式匹配
- python - 为什么页面的 MuPDF MediaBox 比包含的图像小?
- vhdl - 不能将实数和整数相乘
- amazon-web-services - 如何使用 terraform 创建具有公共子网和私有子网的 EKS 集群?
- jquery - jQuery 我有发票计算部分它不能正常工作
- java - 关于 onCreate 方法要点的解释
- asp.net - 使用 MSAL 进行 Microsoft Graph 身份验证(问题)
- xamarin - Mvvmcross 7.0.0:未找到使用 MvvmCross.Forms.Platforms.Android.Core