Steuerelement-Array's serialisieren und DataBinding
Frank Dzaebel, erstellt am: 2.8.2008, zuletzt geändert:  2.8.2008
Kategorie: Windows Forms, .NET-Version: 2.0, [Download]

Will man Steuerelemente automatisch serialisieren, so kann man dies z.B. über ihren Namen erreichen. Hier werden die typsicheren Properties.Settings zur Serialisierung benutzt. Dabei wird eine List<TextBox> Klasse benutzt, die über IXmlSerializable in der Lage ist, die Controls mit einer vorher definierten Anzahl an Eigenschaften, in die BenutzerSettings zu serialisieren. Die Änderungen an den TextBox-Inhalten des Benutzers, werden automatisch gesichert. Dies passiert über DataBindings und BindingSource.
Für .NET 1.1 siehe auch hier. 
Anzeige in der Haupt-Form. Typsichere Properties über den Visual Studio Designer

#region usings ...
#endregion

namespace TwoLineBinding
{
  /// <summary>http://Dzaebel.NET/TwoLineBinding.htm
  /// [Download mit DataBindings] </summary>
  public class Form1 : System.Windows.Forms.Form
  {
    TextBoxen autoTbs = new TextBoxen();
    TextBoxen manuTbs = new TextBoxen();
    Properties.Settings Props = Properties.Settings.Default;
    TextBoxen propAuto, propManu;
    enum Felder { Automatisch, Manuell }

    private void Form1_Load(object sender, System.EventArgs e)
    {
      InitControls(this);
      propAuto = Props.TextfelderAuto;
      propManu = Props.TextfelderManu;
      SetDataBindingProps(propAuto, autoTbs);
      SetDataBindingProps(propManu, manuTbs);
    }

    private void SetDataBindingProps(TextBoxen propTextBoxen, TextBoxen formTextBoxen)
    {
      if (propTextBoxen == null)  propTextBoxen = formTextBoxen;
      BindingSource bs = new BindingSource(propTextBoxen, null);
      for (int i = 0; i < formTextBoxen.Count; i++)
        formTextBoxen[i].DataBindings.Add("Text", bs[i], "Text");
    }

    #region InitControls
    private void InitControls(Control control)
    {
      foreach (Control ctl in control.Controls)
      {
        if (ctl.Controls.Count > 0) InitControls(ctl);
        TextBox tb = ctl as TextBox;
        if (tb != null)
        {
          if (tb.Name.EndsWith("a")) manuTbs.Add(tb);
          if (!tb.Name.EndsWith("a")) autoTbs.Add(tb);
        }
      }
    }
    #endregion


    #region Vom Windows Form-Designer generierter Code . . .
    #endregion

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
      Props.Save();
    }
  }

  public class TextBoxen : List<TextBox>, IXmlSerializable 
  {
    PropertyDescriptorCollection eigenschaftenZuSerialisieren =
      new PropertyDescriptorCollection(new PropertyDescriptor[] {});
    string[] propsToSerialize = new string[] { "Text", "Name" };

    public TextBoxen() 
    {
      List<string> propList = new List<string>(propsToSerialize);
      PropertyDescriptorCollection pdcControl =
        TypeDescriptor.GetProperties(typeof(TextBox));
      foreach (PropertyDescriptor pd in pdcControl)
        if (propList.Contains(pd.Name))
          eigenschaftenZuSerialisieren.Add(pd);
    }

    public XmlSchema GetSchema() { return null; }

    public void ReadXml(XmlReader reader)
    {
      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 = FindControl(Application.OpenForms[0], val);
          name = reader.Name;
          val = reader.ReadElementString();
          
          while (name != "")
          {
            PropertyDescriptor pd = eigenschaftenZuSerialisieren[name];
            object v = pd.Converter.ConvertFromInvariantString(val);
            pd.SetValue(o, v);
            name = reader.Name;
            if (name == "Control" &&
              reader.NodeType == XmlNodeType.EndElement)
            {
              this.Add((TextBox)o); break;
            }
            val = reader.ReadElementString();
          }
        }
      }
      catch (Exception exp) { Fehlermeldung((Control)o, exp, name); }
    }

    Control FindControl(Control control, string controlName)
    {
      if (control.Name == controlName) return control;
      foreach (Control ctl in control.Controls)
      {
        if (ctl.Name == controlName) return ctl;
        if (ctl.Controls.Count > 0)
        {
          Control c = FindControl(ctl, controlName);
          if (c != null) return c;
        }
      }
      return null;
    }

    public void WriteXml(XmlWriter writer)
    {
      foreach (Control ctl in this)
      {
        string name = "ungesetzt";
        try
        {
          if (!(ctl is TextBox)) continue;
          writer.WriteStartElement("Control");
          writer.WriteElementString("Name", ctl.Name);

          foreach (PropertyDescriptor pd in eigenschaftenZuSerialisieren)
          {
            if ((pd.PropertyType.IsValueType || pd.PropertyType == typeof(string)) &&
              pd.PropertyType != typeof(IntPtr) && !pd.IsReadOnly && pd.Name != "Name")
            {
              writer.WriteStartElement(pd.Name);
              object val = pd.GetValue(ctl);
              string v = pd.Converter.ConvertToInvariantString(val);
              writer.WriteString(v); writer.WriteEndElement();
              Debug.WriteLine(pd.Name + " = " + v);
            }
          }
          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);
    }
  }
}