0

我不确定从哪里开始。我正在使用我的实体组件模型库为 XNA 编写一个 GUI 管理器。我希望能够将 XML 文档动态导入 GUI 表单。我的想法是拥有一个"type"包含控件类型的属性。例如type="Image"会告诉解析器制作一个图像。一旦有了它,它就可以调用它自己的 XML 解析方法来填充值。但是,我被困在这部分。

考虑这个文件:

<Gui>
  <BGImage type="Image">
    <Body>
      <Position>
        <X>0</X>
        <Y>0</Y>
      </Position>
    </Body>
    <ImageRender>
      <Texture>background</Texture>
      <Color>
        <R>255</R>
        <G>255</G>
        <B>255</B>
        <A>255</A>
      </Color>
    </ImageRender>
  </BGImage>
  <CheckBox type="Checkbox">
    <Body>
      <Position>
        <X>20</X>
        <Y>20</Y>
      </Position>
    </Body>
    <TileRender>
      <Index>0</Index>
      <Texture>checkbox</Texture>
      <Color>
        <R>255</R>
        <G>255</G>
        <B>255</B>
        <A>255</A>
      </Color>
    </TileRender>
    <TextRender>
      <Text>Checkbox</Text>
      <Font>spritefont</Font>
    </TextRender>
  </CheckBox>
</Gui>

我要做的是让一个类解析这个 XML 文档并执行以下操作。任何带有 type 属性的标签都作为它们所代表的类型添加到表单中。例如,我有一个代表图像的 Image 类,示例中的 BGImage 应该制作一个 Image 并添加到表单中。我需要一种将 type="Image" 与 Image 类相关联的方法。

我已经有一种用于向游戏添加实体的方法,我的问题在于一种将字符串设为类型并从中实例化新实体的方法。

我要做的是从字符串返回一个类型,我不确定这是否可能。有没有办法在 C# 中做到这一点,还是我从错误的角度攻击这个?

4

3 回答 3

3

是的,您可以完全按照您的描述使用 C# 类型限定名称。

例如,限定名称System.Drawing.Image是“System.Drawing.Image, System.Drawing”。限定名称由几部分组成。在我的示例中,它是 [类型名称]、[程序集名称]。(参考文章来自msdn

只要将包含该类型的程序集加载到您的应用程序域中(在本例中,程序集是 System.Drawing),您就可以执行以下操作来实例化它:

var imageType = Type.GetType("System.Drawing.Image, System.Drawing");
System.Drawing.Image image = Activator.CreateInstance(imageType );
于 2013-01-07T22:10:25.613 回答
0

帮自己一个忙,将映射从 xml 中的类型存储到其他资源的程序集中的具体类型。特别是如果您开始定义/扩展自己的类型。

例如

<Assembly name="System.Drawing", namespace = "System.Drawing">
<Type name = "PrettyPicture", Type = "Image"/>
</Assembly>

ETC

将完全限定的名称放在布局文件中,如果您想更改任何内容,您将无处不在。

dtyron 在 Type.GetType 和 Activator.CreateInstance 的观点几乎为您提供了一个良好的开端。

于 2013-01-07T22:32:52.833 回答
0

你可以写一个完整的解析器。它不像其他解决方案那么容易,但从长远来看它可能会更简单。

    Control Parse(XElement element)
    {
        var root = new XElementControlPair(element, ControlWrapper.Create(element));

        var stack = new Stack<XElementControlPair>();

        stack.Push(root);

        while (stack.Any()) //here we recursively search for any child elements
        {
            var elem = stack.Pop();

            var children = from child in elem.XElement.Elements() 
                           let ctl = ControlWrapper.Create(child)                               
                           where child.Attribute("type") != null 
                           select new XElementControlPair(child, ctl);


            foreach (var child in children)
            {
                stack.Push(child);
                elem.Control.Controls.Add(child.Control);
            }

        }

        return root.Control.MakeControl();

    }

class XElementControlPair
{
    public XElement XElement { get; private set; }
    public ControlWrapper Control { get; private set; }

    public XElementControlPair(XElement elem, ControlWrapper ctl)
    {
        this.XElement = elem;
        this.Control = ctl;
    }
}

abstract class ControlWrapper
{
    public List<ControlWrapper> Controls { get; private set; }
    protected readonly XElement element;

    public ControlWrapper(XElement element)
    {
        this.element = element;
    }

    public static ControlWrapper Create(XElement element)
    {
        var type = element.Attribute("type").Value.ToLower();

        switch (type)
        {
            case "image":
                return new ImageWrapper(element);

            case "textbox":
                return new TextBoxWrapper(element);

            case "checkbox":
                return new CheckBoxWrapper(element);

            //etc...
        }
    }

    protected abstract Control _MakeControl(); //here is where you tell it how to construct a particular control given an XElement

    public Control MakeControl()
    {
        var ctl = _MakeControl();

        foreach (var child in Controls)
            ctl.Children.Add(child.MakeControl());

        return ctl;
    }

}

然后为每种类型的控件创建一个包装类来处理将 XML 转换为特定控件。例如:

sealed class ImageWrapper : ControlWrapper
{
    public ImageWrapper(XElement element) { } : base(element)

    protected override Control _MakeControl()
    {
        var image = new Image();

        var pos = element.Element("Position");

        var x = int.Parse(pos.Element("X").Value);
        var y = int.Parse(pos.Element("y").Value);

        image.Position = new Point(x, y);

        //continue setting other properties...

        return image;

    }
}
于 2013-01-07T23:10:39.527 回答