Dynamisches Erzeugen von Controls per Reflection und XML
Frank Dzaebel, erstellt am: 8.04.2007, zuletzt geändert: 22.04.2007
Kategorie:Reflection, .NET-Version:2.0, [Download] [Download2]

Dieses Beispiel zeigt, wie Controls dynamisch über XML-Definitionen aus einer Datei zur Laufzeit in eine Form eingebunden werden. Normal werden nur Eigenschaften der Controls über Reflection gesetzt, für die Ausführung einer Methode wurde beispielhaft eine fest codierte Pseudo-Eigenschaft "Loop" implementiert.

(Im Download2 finden Sie ein erweitertes Beispiel mit UserControl und Setzen von Form-Eigenschaften etc.)
Gängig sind in solchen Szenarien auch Codebehind-Dateien, um beliebigen Code ausführen zu können. So zum Beispiel bei XAML (.NET 3.0), obwohl die Codebehind-Technik als solches schon in früheren ASP.NET-Versionen anzutreffen ist.

Controls.xml:


using System;
using System.Windows.Forms;
using System.Xml;
using System.Reflection;
using System.Drawing;
using System.ComponentModel;
using System.IO;

namespace DynamicCreate
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    string winformAssemblyName = Assembly.GetAssembly(typeof(Form)).FullName;

    private void Form1_Load(object sender, EventArgs e)
    {
      XmlDocument doc = new XmlDocument();
      string path = Path.Combine(Application.StartupPath, "Controls.xml");
      doc.Load(path);
      XmlNodeList parts = doc.SelectNodes(@"//Control");
      AlleControlsAusparsen(parts);
    }

    private void AlleControlsAusparsen(XmlNodeList parts)
    {
      foreach (XmlNode part in parts)
      {
        XmlAttribute typeAtt = part.Attributes["type"];
        CheckOK(typeAtt == null, part, "Das type-Attribute
          fehlt");
        string tp = part.Attributes["type"].Value;
        if (tp.StartsWith("System.Windows.Forms."))
          tp = tp + ", " + winformAssemblyName;
        Type type = Type.GetType(tp);
        CheckOK(type == null, part, "Der type
          '{0}' ist nicht auflösbar.", tp);
        Control control = null;
        try { control = (Control)Activator.CreateInstance(type); }
        catch (Exception exp)
        {
          ThrowExp(exp, part, "Eine Instanz des Typs '{0}' ist nicht erstellbar.", type.AssemblyQualifiedName);
        }
        Controls.Add(control);
        EinzelneAttributeAusparsen(part, type, control);
      }
    }

    private void EinzelneAttributeAusparsen(XmlNode part, Type type, Control control)
    {
      foreach (XmlAttribute attribute in part.Attributes)
      {
        if (attribute.Name == "type") continue;
        PropertyInfo pi = type.GetProperty(attribute.Name, BindingFlags.Instance | BindingFlags.Public);
        if (FestCodierterTag(part, type, ref control, attribute)) continue;
        CheckOK(pi == null, part, "Die Eigenschaft
          '{0}' ist nicht in Typ '{1}' vorhanden.", attribute.Name, type.Name);
        try
        {
          if (pi.PropertyType == typeof(string))
          {
            attribute.Value = attribute.Value.Replace("|StartupPath|", Application.StartupPath);
            pi.SetValue(control, attribute.Value, null);
          }
          else
          {
            TypeConverter tc = TypeDescriptor.GetConverter(pi.PropertyType);
            object val = tc.ConvertFromString(attribute.Value);
            pi.SetValue(control, val, new object[] { });
          }
        }
        catch (Exception exp)
        {
          ThrowExp(exp, part, "Beim Setzen der Eigenschaft '{0} {1}' auf den
            Wert '{2}' ist ein Fehler aufgetreten.", pi.PropertyType, attribute.Name, attribute.Value);
        }
      }
    }

    private bool FestCodierterTag(XmlNode part, Type type, ref Control control, XmlAttribute attribute)
    {
      if (control.GetType().Name == "AxWindowsMediaPlayer" && attribute.Name == "Loop")
      {
        ((AxWMPLib.AxWindowsMediaPlayer)control).settings.setMode("loop", bool.Parse(attribute.Value));
        return true;
      }
      return false;
    }

    #region CheckOK
    private void CheckOK(bool failed, XmlNode part, string msg, params object[] args)
    {
      if (failed)
        throw new Exception(String.Format(msg, args) + "\r\nStelle:" + part.OuterXml);
    }

    private void CheckOK(bool failed, XmlNode part, string msg, object arg)
    {
      CheckOK(failed, part, msg, new object[] { arg });
    }

    private void CheckOK(bool failed, XmlNode part, string msg, object arg1, object arg2)
    {
      CheckOK(failed, part, msg, new object[] { arg1,arg2 });
    }

    private void CheckOK(bool failed, XmlNode part, string msg)
    {
      CheckOK(failed, part, msg, new object[] { });
    }
    #endregion

    #region ThrowExp
    private void ThrowExp(Exception exp, XmlNode part, string message, object arg1, object arg2, object arg3)
    {
      string msg = exp.Message;
      if (exp.InnerException != null)
        msg += "\r\n" + exp.InnerException.Message;
      throw new Exception(String.Format(message, arg1,arg2,arg3) + "\r\nStelle:" + part.OuterXml + "\r\n\r\n" + msg);      
    }
    private void ThrowExp(Exception exp, XmlNode part, string message, object arg1, object arg2)
    {
      ThrowExp(exp, part, message, arg1, arg2, null);
    }
    private void ThrowExp(Exception exp, XmlNode part, string message, object arg)
    {
      ThrowExp(exp, part, message, arg, null,null);
    }
    #endregion
  }
}