Controls automatisch serialisieren  
Frank Dzaebel, erstellt am: 22.3.2008, zuletzt geändert:  22.3.2008
Kategorie: Serialisierung, .NET-Version: 2.0, [Download]

Controls sind standardmässig nicht serialisierbar. Wie man Einstellungen von Controls dennoch automatisch persistieren kann, zeigt dieser Artikel in einem pragmatischen Ansatz, der nur öffentliche Wertetypen serialisiert. Die Serialisierung wird über den TypeDescriptor mit einer invarianten Kultur über IXmlSerializable implementiert. Achtung, es gibt weitere Dinge zu berücksichtigen, dies ist nur ein Ansatz. Ein Vorteil ist, dass hier die typsicheren "Properties.Settings" benutzt werden, sodass die Persistierung gleich ins richtige Benutzer-Verzeichnis kommt.
Es wird zudem die Technik von: [Position und Größe von Forms sichern] benutzt, damit grossgezogene Controls (während der Laufzeit) nicht beim Neustart der Form die falsche Grösse haben.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Configuration;
using System.Diagnostics;
using System.Reflection;
using System.Xml;
using System.Drawing;

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

    Properties.Settings Props = Properties.Settings.Default;

    private void Form1_Load(object sender, EventArgs e)
    {
      // implizit ReadXml invoken, und so die Control neu setzen.
      object o = Props.ChangeControls; 
    }

    private void Form1_FormClosing(object sender,
      FormClosingEventArgs e)
    {
      Props.ChangeControls = new ChangeCollection();
      
      // die Controls, deren Eigenschaften gesichert werden sollen:
      Props.ChangeControls.AddRange(new Control[]{
        splitContainer1, btnFarbe});
      Props.Save();
    }

    private void btnFarbe_Click(object sender, EventArgs e)
    {
      if (btnFarbe.BackColor == SystemColors.Control)
        btnFarbe.BackColor = Color.LightYellow;
      else
        btnFarbe.BackColor = SystemColors.Control;
    }
  }

  public static class Util
  {
    public static void UserConfigTestweiseAnsehen()
    {
      // Verweise: System.Configuration.dll einbinden
      Configuration config = ConfigurationManager.
        OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
      Process.Start(config.FilePath);
    }
  }

  public class ChangeCollection : List<Control>, IXmlSerializable
  {
    public ChangeCollection() { }

    public System.Xml.Schema.XmlSchema GetSchema()
    {
      return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
      // Util.UserConfigTestweiseAnsehen();
      string name = "ungesetzt";
      object o = new Control();
      try
      {
        while (reader.Read())
        {
          if (reader.Name == "ChangeCollection" &&
            reader.NodeType == XmlNodeType.EndElement)
          {
            break;
          }
          string typeName = reader.Name;
          if (typeName != "Control") break;
          string val = reader.ReadElementString();
          o = Application.OpenForms[0].Controls[val];
          PropertyDescriptorCollection props = TypeDescriptor.GetProperties(o);
          name = reader.Name;
          val = reader.ReadElementString();

          while (name != "")
          {
            PropertyDescriptor pd = props[name];
            object v = pd.Converter.ConvertFromInvariantString(val);
            pd.SetValue(o, v);
            name = reader.Name;
            if (name == "Control" &&
              reader.NodeType == XmlNodeType.EndElement)
            {
              this.Add((Control)o);
              break;
            }
            val = reader.ReadElementString();
          }
        }
      }
      catch (Exception exp) { Fehlermeldung((Control)o, exp, name); }
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
      foreach (Control ctl in this)
      {
        string name = "ungesetzt";
        try
        {
          PropertyInfo[] props = ctl.GetType().GetProperties();
          if (props.Length == 0) continue;
          writer.WriteStartElement("Control");
          writer.WriteElementString("Name", ctl.Name);

          foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(ctl))
          {
            if (pd.PropertyType.IsValueType &&
              pd.PropertyType != typeof(IntPtr) &&
              !pd.IsReadOnly)
            {
              writer.WriteStartElement(pd.Name);
              object val = pd.GetValue(ctl);
              string v = pd.Converter.ConvertToInvariantString(val);
              writer.WriteString(v);
              writer.WriteEndElement();
            }
          }
          writer.WriteFullEndElement();
        }
        catch (Exception exp) { Fehlermeldung(ctl, exp, name); }
      }
    }

    void Fehlermeldung(Control ctl, Exception exp, string propertyName)
    {
      MessageBox.Show(exp.Message + "\r\nControl:" + ctl.Name +
        ", Eigenschaft:" + propertyName);
    }
  }
}