【メモ】Delphi コンポーネントで、構造型配列プロパティ

# Delphi で構造型配列プロパティをつくるメモ
コンポーネントを自作するとき、色々なデータを、コンポーネントとメインフォームの間でやり取りします。

| 種類 | 宣言する場所 | メモ |
|:-:|:-:|:-:|
|単純変数|publish宣言|オブジェクトインスペクタで編集可能|
|配列変数|public宣言|オブジェクトインスペクタは使えません|

構造体を配列にして、プロパティにしたいと思ったら、案外、メンドクサイ。
というわけで、レコード構造型を配列にして使うメモです。 
あ、グローバル変数にしちゃえば、簡単なんですが……　範囲外チェックや、関連する処理はまとめて、コンポーネント側で片付けたいですよね？


# コンポーネント側

### 構造型を宣言します。
最初にデータを仕舞う構造型を宣言します。
private宣言には、Fデータフィールドを置きます。データフィールドは隠蔽が基本です。
プロパティにデータを代入した時に、処理を担当する書き込みメソッドも、ここです。

public宣言には、プロパティの宣言を置きます。配列プロパティは、オブジェクトインスペクタに置けないため、publishにはしません。
読み出しは、データフィールド直接ですが、書き込みはSet○○のメソッドを経由させます。

```pascal
type
  TSampleDat = record
    private
      FPos:longint;  //F○○ : 型; データフィールドを宣言
      FMes:String;
      procedure SetPos(pos:longint);  //procedure Set○○ (○○ :型); 書き込みメソッド宣言
      procedure SetMes(Mes:string);
    public
      property Pos : longint read FPos write SetPos;  //property ○○ : 型 read F○○ write Set○○;
      property Mes : String  read FMes write SetMes;  //プロパティ宣言
  end;                                                //読みはFデータフィールド直接　
                                                      //書きは、Set○○で、メソッド手続き必要
```

### コンポーネントの宣言
構造型で、配列変数を宣言します。ここでは固定長にして、データ領域を確保しています。
動的配列にした場合は、別途、Setlength(変数名,長さ); で領域を確保してください。

```pascal
type
  TCustomControl1 = class(TCustomControl)  //コンポーネントを宣言
  private
    { Private 宣言 }
  protected
    { Protected 宣言 }
  public
    { Public 宣言 }
    Sam:array[0..1024] of TSampleDat;  //構造体の配列としてデータフィールドをつくります。
  published                            //この配列変数名が、プロパティ名になります。
    { Published 宣言 }
  end;

```

### 実現部
書き込みメソッドをつくります。
例なので、単純にデータフィールドに代入していますが、範囲外の外れ値を補正したり、データが変化するときに関連する処理があれば、ここで済ませます。

```pascal
procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Samples', [TCustomControl1]);
end;

procedure TSampleDat.SetPos(pos: LongInt);  //書き込みメソッドの実現部です
begin
  FPos:=pos; 
end;

procedure TSampleDat.SetMes(Mes: string);  //書き込みメソッドの実現部です
begin
  FMes:=mes;
end;

end.
```

# メインフォーム側

### uses節にコンポーネントのファイル名を
Uses 節の末尾に、上記のカスタムコンポーネントのファイル名を足します。
Pathが気になる時は、このメインフォームと同じディレクトリに置いてください。
あと、ファイル名からは、.pas は省略します。

### 宣言部について
FormCreate, FormDestroy イベントをオブジェクトインスペクタの該当箇所をクリックして増やします。

publicに、メインフォーム側で使用する名で、カスタムコンポーネントを宣言を書き込みます。

```pascal
unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,SamDatCpt;　//コンポーネントを足します


type
  TForm2 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    SamAs:TCustomControl1;  //使用する名 : Tカスタムコントロール; で宣言
  end;

```

### Create と Destroy のイベントを書き込みます。
```pascal

procedure TForm2.FormCreate(Sender: TObject);
begin
  SamAs:=TCustomControl1.Create(Self); //宣言した名 := Tカスタムコンポーネント.Create(Self);
end;                                   //コンポーネントを作成。

procedure TForm2.FormDestroy(Sender: TObject);
begin
      SamAs.Free;                      //宣言した名.Free; コンポーネントを廃棄。  
end;

```

### で、準備が整いましたので、配列コンポーネントを使います。

```pascal
procedure TForm2.Button1Click(Sender: TObject);
var
  i:longint;
begin
  memo1.text:='';

  //書き込み
  with SamAs do
  begin
    Sam[0].pos:=254;
    Sam[0].mes:='ささにしき';
    Sam[1].pos:=651;
    Sam[1].mes:='こしひかり';
    Sam[2].pos:=805;
    Sam[2].mes:='あきたこまち';

    //読み出し
    for i:=0 to 2 do
      memo1.lines.add(IntToStr(Sam[i].pos)+'  '+Sam[i].Mes);
  end;
end;

```

# あとがき
ごく単純に、構造型配列をプロパティにして、
コンポーネントの名.構造型の変数名[index].プロパティ := 代入したいデータ; をやろうとすると、
E2064 代入できない左辺値です (Delphi)　って、いわれるのです。

で、拡張された構造型を使えば、何とかなりそう？　って、Dokiwiki読んだり、ネットを探し回ったりしてました。
いまいち、ピッタリな情報がなかったので、何とかなったから、忘れる前にメモメモ！