.NET ROOM.NET ROOMトップへCOMRADE総合トップへ
「5:コントロールの設定値を永続化する」へ

自動生成される内容

 まず自動生成されるコードを確認します。
Windowsアプリケーションを作成して、自作のコントロールを追加し、必要であれば、
プロパティウィンドウでいくつか操作をした後、InitializeComponentメソッドの中身を確認してみます。
以下の説明では、必要に応じて確認しながら読み進めてください。

  VS.NETのデザイン画面で変更を加えた際に、InitializeComponentメソッドの中に
自動生成される内容はさまざまですが、大きく分けると以下のようなパターンになります。
それぞれのパターンごとに、パターンに属する型の例、共通する特徴、
生成されるコードのサンプルを記します。

■パターン1・定数がそのまま保存される■
例: string、int、float、enumなど

特徴: 単純なValue型

サンプルコード: this.label1.Text = "label1";


■パターン2・新規インスタンスがコンストラクタで指定される■
例: Font、Size、Pointなど

特徴: コンストラクタのある型

サンプルコード: this.comboBox1.Size = new System.Drawing.Size(121, 20);


■パターン3・staticなメソッド、メンバ、プロパティなどでインスタンスが指定される■
例: Colorなど

特徴: コンストラクタのないStructureやClassなどの型

サンプルコード:
this.label1.BackColor = System.Drawing.Color.Blue;
this.label1.ForeColor = System.Drawing.Color.FromArgb(
  ((System.Byte)(192)), ((System.Byte)(192)), ((System.Byte)(255)));


■パターン4・内容がAddもしくはAddRangeメソッドで追加される■
例: Control.ControlCollection、ComboBox.ObjectCollectionなど

特徴: コレクション型。なお、コレクション自体はReadOnlyで、
読み書き可能なアクセサが用意されていて、
コレクションの中の値を変更できるプロパティの場合が多い

サンプルコード:
this.comboBox1.Items.AddRange(new object[] {
 "Item1",
 "Item2",
 "Item3"});


■パターン5・リソースに保存される■
例: Image型など

特徴: System.Resources.ResourceManagerのGetObjectメソッドを使用。
内容はRESXファイルにXMLデータとして保存される。
ただし、バイナリデータはBase64形式でエンコードされて埋め込まれる

サンプルコード:
this.pictureBox1.Image =
 ((System.Drawing.Image)(resources.GetObject(
 "pictureBox1.Image")));


ページトップへ


各パターンの自動生成の実装方法

 次に、上記のそれぞれのパターンの実装方法を説明してゆきます。


■パターン1・定数がそのまま保存される■

 定数がそのまま保存されるパターンは、上記のとおり、
string、int、float、enumなどの単純な値型のプロパティでは特に意識せずに実現できます。

 パターン1のサンプルは、付録のConstantPersistedPropertySampleディレクトリに収録しています。


■パターン2・新規インスタンスがコンストラクタで指定される■

 このパターンでは、インスタンスの値をコンストラクタで表わした状態で、コードが生成されます。
これを実現しているのは、その名もずばり、「InstanceDescriptor」というクラスです。
ただし、InstanceDescriptorを設定するために、TypeConverterを利用します。

 リスト1に自動生成コードにおいて、コンストラクタによってインスタンスが表わされるクラスを示します。
このように、TypeConverterのCanConverToメソッドのオーバーライドでInstanceDescriptorへの変換を実装し、ConvertToメソッドで実コードを記述します。

 パターン2のサンプルは、付録のPropertyPersistedByConstructorSampleディレクトリに収録しています。

リスト1:コンストラクタによってインスタンスが表わされるクラス
[Serializable]
[TypeConverter(typeof(
 ConstructorPersistedClass._ConstructorPersistedClassConverter))]
public class ConstructorPersistedClass {
 internal class _ConstructorPersistedClassConverter :
  ExpandableObjectConverter {
  public override bool CanConvertTo(
   ITypeDescriptorContext context, Type dst_type) {
   if (dst_type == typeof(InstanceDescriptor))
    return true;
   return base.CanConvertTo(context, dst_type);
  }
  public override object ConvertTo(
   ITypeDescriptorContext context,
   CultureInfo culture, object value,
   Type dst_type) {
   if (dst_type == typeof(InstanceDescriptor)) {
    ConstructorPersistedClass cpc =
     value as ConstructorPersistedClass;
    ConstructorInfo ci = typeof(
     ConstructorPersistedClass).GetConstructor(
     new Type[]{typeof(int)});
    return new InstanceDescriptor(
     ci, new object[]{cpc.Data});
   }
   return base.ConvertTo(context, culture, value, dst_type);
  }
 }
 int data;
 public ConstructorPersistedClass(int data) {
  this.data = data;
 }
 public int Data {
  get { return data; }
  set { data = value; }
 }
}


■パターン3・staticなメソッド、メンバ、プロパティなどでインスタンスが指定される■

 このパターンも先ほどの新規インスタンスがコンストラクタで指定されるパターンと同様、
TypeConverterでInstanceDescriptorへの変換を実装することで実現できます。

  InstanceDescriptorのコンストラクタは、MemberInfoを引数のひとつとして受け取ります。
MemberInfoは、先ほどの例で使用した、ConstructorInfo以外にも、MethodInfo、
FieldInfo、PropertyInfoという派生クラスが存在します。
ただし、MethodInfo、FieldInfo、PropertyInfoは、staticなメンバである必要があります。

 パターン3のサンプルは、付録のPropertyPersistedByStaticInstanceSampleディレクトリに収録しています。


■パターン4・内容がAddもしくはAddRangeメソッドで追加される■

 このパターンは、コレクション型の保存のバリエーションです。

 コレクションの保存に関しては、以下の2つが考えられます。

  @コレクションごとリソースに保存する
  Aコレクションの中身を保存する

  ただし、@のコレクションごとリソースに保存する方法は、
残念ながらVS.NETの2002でも2003でもできませんでした。
以下のように、ラッパークラスでコレクションを包むことによって、
リソースに保存できるようになります。

   [Serializable]
   [TypeConverter(typeof(ExpandableObjectConverter))]
   public class CollectionWrapper {
    IntCollectionClass col;
    public CollectionWrapper() {
     col = new IntCollectionClass();
    }
    public IntCollectionClass Collection {
     get { return col; }
    }   
   }

[TypeConverter(typeof(ExpandableObjectConverter))]は、
プロパティウィンドウでCollectoinWrapperクラスの中の
Collectionプロパティを設定できるようにするために指定しました。

 続いて、Aのコレクションの中身を保存する方法では、

    [DesignerSerializationVisibility(
    DesignerSerializationVisibility.Content)]
   public IntCollectionClass IntCollection {...}

のように、「DesignerSerializationVisibilityAttribute」を使用します。
プロパティのデフォルトは、DesignerSerializationVisibilityAttributeが
「DesignerSerializationVisibility.Visible」になっているので、“中身を保存させる”という意味で、
「DesignerSerializationVisibility.Content」に設定します。

 適切な型のAddメソッド、もしくはAddRangeメソッドを実装することで、
コレクションの中身の保存のためのコードが正しいほうを利用したものになります。
Addメソッドを実装してAddRangeメソッドを実装しなかった場合は
コレクションの中身はAddメソッドを使用して記述され、AddRangeメソッドを正しく実装している場合は
AddRangeメソッドが使用されます。

  先ほどのラッパークラスを利用してコレクションをリソースに保存するテクニックを使用するメリットとしては、
「コレクションの中身が多い場合でも、自動生成されるコードが少なくて済む」ということがあげられます。
デメリットとしては、このラッパークラスを使用したプロパティは、
以下のように必ずset可能にする必要があるということです。

   public CollectionWrapper Wrapper {
    get { return wrap; }
    set { wrap = value; }
   }

  以下のようにしてもコードは自動生成されません。
なお、AddもしくはAddRangeメソッドを実装したコレクションクラスをラップしている場合でも、
ソースコードは自動生成されません。

   [DesignerSerializationVisibility(
    DesignerSerializationVisibility.Content)]
   public CollectionWrapper ContentPersistedWrapper {
    get { return cpw; }
   }

パターン4のサンプルは、付録のCollectionPersistanceSampleディレクトリに収録しています。


■パターン5・リソースに保存される■

 前回説明した、シリアル化を実装してあるクラスは、VS.NETによって自動的に保存されます。
大まかな流れとしては、RESXファイルにインスタンスのバイナリシリアライズされた値が
MIMEエンコードされた形で保存され、ResourceManagerクラスを利用して取り出されます。
ただし、この部分の仕組みは詳しく理解しなくても、前回説明した各種の方法で、
クラスのシリアライズを実装すればよいだけです。


前へ | ページトップへ | 次へ