親コンポーネント上にある子コンポーネント間の変化を相互に反映させ合うには?

解決


ing  2009-01-06 16:20:33  No: 33014

コンポーネント開発について学習を始めました。

トラックバーが動くとエディットに整数値が表示され、
エディットに整数値が入力されるとトラックバーのつまみが移動する
という、変化を相互に反映させ合うコンポーネントを作ってみようと考えました。

親コンポ(TPanel)の上に子コンポ(TTrackBarとTEdit)を置きました。

ここで2つ分からないことがあります。
①トラックバーが変化したとき、トラックバーのつまみの位置をエディットに反映させるのことになる。そのイベントやプロパティ、プロシージャは?
②エディットに整数値を入れたとき、トラックバーのつまみの位置にどのようにして反映させることになる。そのイベントやプロパティ、プロシージャは?

教えてください。

以下がそのソースです。

unit PanelTrackBarEdit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls,ComCtrls,StdCtrls;

type
  TPanelTrackBarEdit = class(TPanel)
  private
    { Private 宣言 }
    FTrackBar: TTrackBar;
    FEdit: TEdit;
  protected
    { Protected 宣言 }
  public
    { Public 宣言 }
    constructor Create( AOwner: TComponent); override;
  published
    { Published 宣言 }
  end;

procedure Register;

implementation

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

constructor TPanelTrackBarEdit.Create( AOwner: TComponent);
begin
  inherited Create(AOwner);
  width:= 190;
  height:= 30;//100;

//TrackBarを作成
  FTrackBar:= TTrackBar.Create(self);
  FTrackBar.Parent:= self;
  FTrackBar.Top:= 2;
  FTrackBar.Left:= 2;

//Editを作成
  FEdit:= TEdit.Create(self);
  FEdit.Parent:= self;
  FEdit.top:=2;
  FEdit.Left:= 150;
  FEdit.Width:= 30;
  FEdit.Text:= inttostr(FTrackBar.position);
end;

end.


Mr.XRAY  URL  2009-01-06 19:01:15  No: 33015

Mr.XRAYです.今年もよろしくお願いします.

複数のコンポーネントを持つコンポーネントにはいろいろな種類がありますが,
ingさんのように,そのコンポーネント(PanelTrackBarEdit)を使用したアプリから
TTrackbar,TEditを操作する必要のないコードであれば,以下の考え方でイベント処理が可能です.

1. 内部で生成するコンポーネントに必要なイベントを定義
2. そのイベント内で必要な処理を書く

以下は,Editの値を変更する場合です.(TEditのOnChangeを使用)

//***  が追加した行です(6行)

unit PanelTrackBarEdit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls,ComCtrls,StdCtrls;

type
  TPanelTrackBarEdit = class(TPanel)
  private
    { Private 宣言 }
    FTrackBar: TTrackBar;
    FEdit: TEdit;
  protected
    { Protected 宣言 }
    procedure EditChange(Sender: TObject);     //***
  public
    { Public 宣言 }
    constructor Create( AOwner: TComponent); override;
  published
    { Published 宣言 }
  end;

procedure Register;

implementation

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

constructor TPanelTrackBarEdit.Create( AOwner: TComponent);
begin
  inherited Create(AOwner);
  width:= 190;
  height:= 30;//100;

//TrackBarを作成
  FTrackBar:= TTrackBar.Create(self);
  FTrackBar.Parent:= self;
  FTrackBar.Top:= 2;
  FTrackBar.Left:= 2;

//Editを作成
  FEdit:= TEdit.Create(self);
  FEdit.Parent:= self;
  FEdit.top:=2;
  FEdit.Left:= 150;
  FEdit.Width:= 30;
  FEdit.OnChange := EditChange;    //***
  FEdit.Text:= inttostr(FTrackBar.position);
end;

procedure TPanelTrackBarEdit.EditChange(Sender: TObject);  //***
begin                                                      //***
    FTrackBar.Position := StrToIntDef(FEdit.Text,0);     //***
end;                                                       //***

end.

参考になるかどうかはわかりませんが...
http://mrxray.on.coocan.jp/Delphi/plSamples/160_CreateComponent.htm#110


ing  2009-01-07 06:57:26  No: 33016

Mr.XRAY さんへ

ありがとうございます。
感動しました。
「すばらしい」の一言に尽きます。

Mr.XRAYさんのコードを参考にして、
「//*」のついている行を付け足してみました。
期待していたとおりの動作です。

これをもとに、コンポーネントのサイズを変えたときに、トラックバーとエディットの位置がうまくいくようにするとか、エディットの広さを変えるとか、いろいろなプロパティやメソッドを付け加えていきながら、勉強を重ねたいと思います。

本当にありがとうございます。

以下が、Mr.XRAYさんのコードをまねして付け加えたものです。

unit PanelTrackBarEdit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls,ComCtrls,StdCtrls;

type
  TPanelTrackBarEdit = class(TPanel)
  private
    { Private 宣言 }
    FTrackBar: TTrackBar;
    FEdit: TEdit;
  protected
    { Protected 宣言 }
    procedure EditChange(Sender: TObject);     //***
    procedure TrackBarChange(Sender: TObject);     //*
  public
    { Public 宣言 }
    constructor Create( AOwner: TComponent); override;
  published
    { Published 宣言 }
  end;

procedure Register;

implementation

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

constructor TPanelTrackBarEdit.Create( AOwner: TComponent);
begin
  inherited Create(AOwner);
  width:= 190;
  height:= 30;//100;

//TrackBarを作成
  FTrackBar:= TTrackBar.Create(self);
  FTrackBar.Parent:= self;
  FTrackBar.Top:= 2;
  FTrackBar.Left:= 2;
  FTrackBar.OnChange:= TrackBarChange; //*

//Editを作成
  FEdit:= TEdit.Create(self);
  FEdit.Parent:= self;
  FEdit.top:=2;
  FEdit.Left:= 150;
  FEdit.Width:= 30;
  FEdit.OnChange := EditChange;    //***
  FEdit.Text:= inttostr(FTrackBar.position);
end;

procedure TPanelTrackBarEdit.EditChange(Sender: TObject);  //***
begin                                                      //***
    FTrackBar.Position := StrToIntDef(FEdit.Text,0);     //***
end;                                                       //***

procedure TPanelTrackBarEdit.TrackBarChange(Sender: TObject);  //*
begin                                                      //*
     FEdit.Text:= intTostr(FTrackBar.Position);     //*
end;                                                       //*

end.


Mr.XRAY  URL  2009-01-07 21:55:52  No: 33017

>これをもとに、コンポーネントのサイズを変えたときに、トラックバーとエディット

もちろんこの方法でいいのですが,また別の方法も書いておきますね.
内部で使用するコンポーネントのクラスを定義して使用する方法です.
この方法は,内部のコンポーネントに対して細かい制御が必要となった場合に有効です.

詳しい説明は省略しますが,テストするのであれば以下の手順がいいでしょう.

1. コンポーネントのコードをバスの通った(とりあえずテスト用のプロジェクト等)に置く
2. テスト用のプロジェクトを作成.コンポはこのテスト用プログラムで生成する.
    usesにplPanelTrackBarEditを追加します
    
ビジュアルなコンポのテストは,実行時に生成するといいです.
コンポーネントの再インストールが必要ありません.もちろん,設計時のデザイン
テストの場合は再インストールが必要です.

-----  コンポのコード    plPanelTrackBarEdit.pasで保存する  --------

unit plPanelTrackBarEdit;

interface

uses
  SysUtils, Classes, Controls, ExtCtrls, StdCtrls, ComCtrls, Graphics;

type
  //前方参照
  //TplTracBarとTplEditSPはコードでは後方で定義しているため,そのままでは参照
  //できない
  TplTrackBar = class;
  TplEditSP   = class;

  TplPanelTrackBarEdit = class(TPanel)
  private
    { Private 宣言 }
    FTrackBar : TplTrackBar;
    FEdit     : TplEditSP;
  protected
    { Protected 宣言 }
    procedure SetParent(AParent: TWinControl); override;
  public
    { Public 宣言 }
    constructor Create( AOwner: TComponent); override;
    destructor  Destroy; override;
    property TrackBar : TplTrackBar read FTrackBar write FTrackBar;
    property Edit     : TplEditSP   read FEdit     write FEdit;
  published
    { Published 宣言 }
  end;

  //内部コントロールのTTrackBar
  //TplPanelTrackBarEditのプロパティ等を利用可能にするためにプロパティを定義
  TplTrackBar = class(TTrackBar)
  private
    FPanelTrackBarEdit : TplPanelTrackBarEdit;
  protected
    procedure Changed; override;
  public
    property PanelTrackBarEdit : TplPanelTrackBarEdit read FPanelTrackBarEdit;
  end;

  //内部コントロールのTEdit
  //TplPanelTrackBarEditのプロパティ等を利用可能にするためにプロパティを定義
  TplEditSP = class(TCustomEdit)
  private
    FPanelTrackBarEdit : TplPanelTrackBarEdit;
  protected
    procedure Change; override;
    procedure DoEnter; override;
    procedure KeyPress(var Key: Char); override;
  public
    property PanelTrackBarEdit : TplPanelTrackBarEdit read FPanelTrackBarEdit;
  end;

procedure Register;

implementation

uses DebugWndUnit;

procedure Register;
begin
  RegisterComponents('plXRAY', [TplPanelTrackBarEdit]);
end;

{ TplTrackBarEdit }

//=============================================================================
//  コンポのCreate処理
//  ビジュアルな内部コンポーネントはSetParentで生成する
//  非ビジュアルな内部コンポーネントはCreateで生成してよい
//=============================================================================
constructor TplPanelTrackBarEdit.Create(AOwner: TComponent);
begin
    inherited;

    Caption := '';
    Width   := 250;
    Height  := 85;
    Color   := $00AFBBA6;

    //Delphi6以降でXPManを使用する場合はこれがないとTPanelのColorプロパティ
    //の設定が反映されない
    Self.ParentBackground := False;
end;
//=============================================================================
//  コンポのDestroy処理
//  内部コンポーネントを解放する
//=============================================================================
destructor TplPanelTrackBarEdit.Destroy;
begin
    if Assigned(FTrackBar) then FreeAndNil(FTrackBar);
    if Assigned(FEdit)     then FreeAndNil(FEdit);

    inherited;
end;
//=============================================================================
//  コンポのSetParent処理
//  ビジュアルな内部コンポーネントはSetParentで生成する
//  SetParentメソッドは何回も呼出されるので注意
//=============================================================================
procedure TplPanelTrackBarEdit.SetParent(AParent: TWinControl);
begin
    inherited;

    //コンポ破棄の時もSetParentは呼ばれるため,Parentが無い場合は処理をしない
    if AParent = nil then exit;

    Caption := '';

     if not Assigned(FTrackBar) then begin
      FTrackBar := TplTrackBar.Create(Self);
      FTrackBar.Parent    := Self;
      FTrackBar.Left      := 10;
      FTrackBar.Top       := 10;
      FTrackBar.Height    := 35;
      FTrackBar.Width     := Self.Width-FTrackBar.Left-10;
      FTrackBar.Min       := 0;
      FTrackBar.Max       := 100;
      FTrackBar.Frequency := 10;
      FTrackBar.TickStyle := tsNone;
      FTrackBar.FPanelTrackBarEdit := Self;
    end;

    if not Assigned(FEdit) then begin
      FEdit := TplEditSP.Create(Self);
      FEdit.Parent := Self;
      FEdit.Left   := FTrackBar.Left+5;
      FEdit.Top    := FTrackBar.Top+FTrackBar.Height+3;
      FEdit.Width  := 45;
      FEdit.FPanelTrackBarEdit := Self;
    end;
    FEdit.SetFocus;
end;

{ TplTrackBar }

//=============================================================================
//  TTrackBarコンポ内部でのOnChageイベント処理
//  ChangedメソッドをOverrideして作成しておくと自動的にこのメソッドが呼ばれる
//=============================================================================
procedure TplTrackBar.Changed;
begin
   inherited;

   FPanelTrackBarEdit.FEdit.Text:= IntTostr(Self.Position);
end;

{ TplEditSP }

//=============================================================================
//  TEditコンポ内部でのOnChageイベント処理
//  ChangeメソッドをOverrideして作成しておくと自動的にこのメソッドが呼ばれる
//=============================================================================
procedure TplEditSP.Change;
begin
    inherited;

    FPanelTrackBarEdit.FTrackBar.Position :=
              StrToIntDef(FPanelTrackBarEdit.FEdit.Text,0);
end;
//=============================================================================
//  TEditコンポ内部でのOnEnterイベント処理
//  DoEnterメソッドをOverrideして作成しておくと自動的にこのメソッドが呼ばれる
//=============================================================================
procedure TplEditSP.DoEnter;
begin
    inherited;

    //かな漢字入力を不可にしてしまう
    SetImeMode(Handle,imDisable);
end;
//=============================================================================
//  TEditコンポ内部でのOnKeyPressイベント処理
//  KeyPressメソッドをOverrideして作成しておくと自動的にこのメソッドが呼ばれる
//  KeyPressは継承元のTWindControl.pasで定義されている
//=============================================================================
procedure TplEditSP.KeyPress(var Key: Char);
begin
    //数値以外は入力させない
    //英字は入力できない. #8は[BackSpace]. #27は[Escape]
    if ((Key>='0') and (Key<='9')) or (Key=#8) or (Key=#27) then begin
      inherited KeyPress(Key);
    end else begin
      Key:=#0;
    end;
end;

end.

------  テストプログラムのコード

//=============================================================================
//  開発中のコンポーネントをフォーム表示開始で生成
//  ビジュアルなコンポーネントは,概観を変更すると再インストールが必要になる
//  実行時のテストであれば,実行時に生成してテストすると再インストールが不要
//  ただし,設計時の外観変更は再インストールが必要
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
var
    APanelTrackBarEdit : TplPanelTrackBarEdit;
begin
    APanelTrackBarEdit := TplPanelTrackBarEdit.Create(Self);
    APanelTrackBarEdit.Parent := Self;
    APanelTrackBarEdit.Top    := 20;
    APanelTrackBarEdit.Left   := 20;
end;

>コンポーネント開発について学習を始めました。    

ということですので,ついでに.

コンポーネント本体の名前,また内部で生成するコントロールやクラス名には
極力ユニークな名前をつけるといいです.
これは他のユニットを使用した時に名前の衝突を避けるためです.
私はプリフィックスとして先頭にplを付けています.


ing  2009-01-19 15:30:08  No: 33018

Mr.XRAY  さま

しばらくの間、2009/01/06(火) 10:01:15  に回答していただいたことについて学習を進めており、このページを開いていませんでした。

追加情報をありがとうございます。
ただ、当方初心者ゆえ、少々難解です。
繰り返し学習して、少しずつ理解していきたいと思います。

さて、上記ソース(plPanelTrackBarEdit.pas)をコンパイルしようとしたところ、
uses DebugWndUnit;
の行で、致命的エラーがでます。
外部参照のユニットだと思われますが、どこを参照すればよいでしょうか。


Mr.XRAY  URL  2009-01-19 18:07:54  No: 33019

Mr.XRAYです.

>uses DebugWndUnit;
>の行で、致命的エラーがでます。

これは,私がデバックのために使用しているユニットです.削除してください.
時々削除を忘れるんです.
実行プログラムをUPしています.もしよろしかったら.

http://mrxray.on.coocan.jp/Delphi/plSamples/160_CreateComponent.htm#112
このページの先頭に[DownLoad]ボタンがあります.


Mr.XRAY  URL  2009-01-19 19:24:59  No: 33020

スミマセン.間違いました.今回のは実行時の生成ですから,こちらですね.

http://mrxray.on.coocan.jp/Delphi/plSamples/160_CreateComponent.htm#111

いずれにしても,複数のコンポーネント(コントロール)を持つコンポーネントの
作成は結構難しいと思います.
がんばってください.


ing  2009-01-21 06:17:59  No: 33021

Mr.XRAY  さん
わずかの間に2つも書き込みしていただき、感謝しています。
紹介していただいたページは、印刷し、何度も見返して学習していきたいと思います。

思いつきで、できもしない課題を設定してしまいました。

Delphi初心者のくせに、高いハードルに挑んで、みなさんにご迷惑をおかけしております。

私にとって雲の上の存在(かってにそう思っています)であるMr.XRAYさんから「がんばってください」と声援を頂き、とてもうれしいです。
これからも、こつこつがんばりたいです。
ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加