旧Delphi FAQ - VCL(2)

Abstract: このQ&Aは、旧www.borland.co.jpに掲載されていた記事を転載したものです。記事は掲載時点の情報をあるがままに掲載しており、新バージョンでのご利用においては、コンポーネントやAPIの仕様変更等によりご利用いただけない場合がありますのでご留意ください。

    タイトルバーをクリックせずにフォームをドラッグするには

該当するバージョン:Delphi1.0,Delphi2.0,Delphi3.0/Delphi3.1,Delphi4

Q:

フォームのタイトルバーをクリックせずにクライアント領域をクリックしてフォームをドラッグするには、どうしますか?

A:

簡単な方法は、フォームのタイトルバーがクリックされたと Windowsに思わせることです。これは WM_NCHITTESTメッセージを処理して実現できます。

フローティングツールバーのようなタイトルもボーダーも無いウィンドウの場合は、フォームのタイトルを空の文字列に設定し、すべての BorderIconsを Falseに設定し、BorderStyleを bsNoneに設定します。

type
   TForm1 = class(TForm)
protected
  procedure WMNCHITTEST(var M: TWMNCHITTEST); message WM_NCHITTEST;
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WMNCHITTEST(var M: TWMNCHITTEST);
begin
  inherited;                      { 継承されたメッセージハンドラの呼び出し }
  if M.Result = htClient then     { クライアント領域がクリックされたか ? }
    M.Result := htCaption;        { もしそうであれば、タイトルバーが }
                                  { クリックされたと Windows に思わせる }
   end;

    OnHintイベントはどのように使用するのですか?

該当するバージョン: Delphi1.0, Delphi2.0, Delphi3.0/3.1, Delphi4

Q:

OnHint イベントはどのように使用するのですか?

A:

OnHintイベントは、ヘルプヒントを表示することができるコントロールまたはメニュー項目にマウスポインタを置くと発生するイベントで、例えば各コントロールにマウスを位置づけた時にステータスバーやパネルなどに、Hintの内容を表示する様な場合に使用することができます。

下の例では、Editコントロールにマウスを位置づけた場合に Panelに Edit.Hintの内容を表示するプログラムになりますので、参考にしてください。

オブジェクトインスペクタの設定:

Edit1.Hint:= '編集';
Edit1.ShowHint := True;

この設定により、Edit1 にマウスを置くとヒントが表示されます。

   procedure TForm1.DisplayHint(Sender: TObject);
   begin
     Panel1.Caption := Application.Hint;  { パネルにヒントを表示 }
   end;

   procedure TForm1.FormActivate(Sender: TObject);
   begin
     Application.OnHint := DisplayHint;
   end;

    TDBGrid 内をリターンキーで移動するには、どうしますか ?

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

アクティブなコントロールをリターンキーで移動するには、どうしますか ?

A:

以下のコードは、DBGrid だけでなく Edit などアプリケーション全体でリターンキーを処理します。

DBGrid については、else の部分で処理されます。

タブキーが最終行の最後のコラムで押されたら、次の行へ移動しますが、このコードでは、タブキーの振る舞いを真似るのではなく、先頭のコラムに戻ります。

フォームの KeyPreview プロパティは True に設定しておきます。これにより、Tab、BackTab、矢印キーなどの移動キーをフォームで捕捉することができます。

 procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
 begin
   if Key = #13 then                               //リターンキーであれば
      if not (ActiveControl is TDBGrid) then begin //TDBGridでなければ
        Key := #0;                                 //リターンキーを無効に
        Perform(WM_NEXTDLGCTL, 0, 0);              //次のコントロールに移動
      end
      else if (ActiveControl is TDBGrid) then      // TDBGrid であれば
        with TDBGrid(ActiveControl) do
          if selectedindex < (fieldcount -1) then  //フィールドをインクリメント
            selectedindex := selectedindex +1
          else
            selectedindex := 0;
 end;

    TabSet 名を指定してページを移動するには、どうしたら良いのですか ?

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

TabSet 名を指定してページを移動するには、どうしたら良いのですか ?

A:

フォームに TabSet(TabSet1) と Edit (Edit1) を配置します。

TabSet の Tabs プロパティを以下のように変更してください。

Hello
World
Of
Delphi

Edit1 の OnChange イベントを以下のように変更してください。

Tabs プロパティで指定した文字列のいずれかを Edit1 に入力すると適切なタブへフォーカスが移動します。

   procedure TForm1.Edit1Change(Sender: TObject);
   var
     i : Integer;
   begin
     for  i := 0 to tabset1.tabs.count-1 do
      if  UpperCase(edit1.text) = UpperCase(tabset1.tabs[i]) then
        tabset1.tabindex:=i;
   end;

    TMemo コントロールの現在の行番号を取得するには

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

TMemo コントロールの現在の行番号を取得するには、どうしたら良いのですか ?

A:

em_LineFromChar メッセージを使用することで行番号を取得できます。

   procedure TForm1.Button1Click(Sender: TObject);
   var
     iLine : Integer ;
   begin
      iLine := Memo1.Perform(em_LineFromChar, $FFFF, 0);
      // 最初の行は,0です。
      messageDlg('行番号: ' + IntToStr(iLine), mtInformation,  [mbOK], 0 ) ;
   end;

    TMemo や TListbox のデータを印刷するには、どうしたら良いですか ?

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

TMemoや TListboxのデータを印刷するには、どうしたら良いですか ?

A:

以下の関数は、TStrings オブジェクトを引数にとりすべての string を印刷します。TStrings を使用するため、この関数は TDBMemoや TOutlineなどのような TStrings型のプロパティのあるコンポーネントであれば使用できます。

uses Printers; を追加してください。

   procedure PrintStrings(Strings: TStrings);
   var
     Prn: TextFile;
     i: word;
   begin
     AssignPrn(Prn);
     try
       Rewrite(Prn);
       try
         for i := 0 to Strings.Count - 1 do
           writeln(Prn, Strings.Strings[i]);
       finally
         CloseFile(Prn);
       end;
     except
       on EInOutError do
         MessageDlg('印刷エラー', mtError, [mbOk], 0);
     end;
   end;

   procedure TForm1.Button1Click(Sender: TObject);
   begin
     PrintStrings(Memo1.Lines);
    //  PrintStrings(Listbox1.Items);
   end;

    フォントのスタイルを制御するには、どうしたら良いですか ?

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

フォントのスタイルを制御するには、どうしたら良いですか ?

A:

以下のサンプルは、ListBox から選択されたフォントのスタイルを Edit1 のフォントのスタイルとします。

ListBox1.Items に、以下の文字列を設定します。

fsBold
fsItalic
fsUnderLine
fsStrikeOut
   procedure TForm1.ListBox1Click(Sender: TObject);
   var
     X : Integer;
   type
     TLookUpRec = record
       Name: String;
       Data: TFontStyle;
     end;
   const
     LookUpTable: array[1..4] of TLookUpRec =
     ((Name: 'fsBold'; Data: fsBold),
      (Name: 'fsItalic'; Data: fsItalic),
      (Name: 'fsUnderline'; Data: fsUnderline),
      (Name: 'fsStrikeOut'; Data: fsStrikeOut));
   begin
     X := ListBox1.ItemIndex;
     Edit1.Text := ListBox1.Items[X];
     Edit1.Font.Style := [LookUpTable[ListBox1.ItemIndex+1].Data];
   end;

    OpenDialog で複数ファイル選択後のファイル名の取得

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

OpenDialog コンポーネントの Opetions プロパティで ofAllowMultiSelect を選択すると複数のファイルが選択できます。この複数ファイルの中から任意のファイル名を取り出す事ができますか

A:

Opetions プロパティ が ofAllowMultiSelect になっている場合、選択された複数ファイルは TString 型 のFiles プロパティに入ります。

以下のコードを参考にしてください。

  • 指定したファイルをEditコンポーネントに表示する
   procedure TForm1.Button1Click(Sender: TObject);
   begin
     Edit1.Text := OpenDialog1.Files[2]; 
   end;

    アプリケーションの実行時にコンポーネントをマウスで移動させる

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

アプリケーションの実行中に、フォーム上のコンポーネントをマウスで移動させたいのですが、どのようにすればよいでしょうか。

A:

MouseDown、MouseMove、MouseUp イベントと、SetBounds メソッドを組み合わせて、アプリケーションの実行中にマウスで移動させる事ができます。

以下の例を実行すると、TShapeコンポーネントを実行中に移動させる事ができます。

Shape1 の MouseUP、MouseDown、MouseMove イベントに記述します。

変数は Form のクラス内部に定義します。

  isDrag: boolean;
  CurrentPos: TPoint;
  origin: integer;

procedure TForm1.Shape1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  isDrag := False;
end;

procedure TForm1.Shape1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  isDrag := True;
  CurrentPos := ClientToScreen( Point( X , Y ) );
end;

procedure TForm1.Shape1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  p: TPoint;
begin
  p := ClientToScreen( Point( X , Y ) );
  if IsDrag then
  begin
    with Shape1 do begin
      SetBounds( Left + ( p.x - CurrentPos.X ),
                         Top + ( p.y - CurrentPos.Y ), Width, Height);
    end;
  end;
end;

    ファイルとパスが含まれた文字列からファイル名とパスを取り出す

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

ファイルのパスが含まれた文字列からファイル名とパスを別々に取り出すことができますか。

A:

ファイル管理ルーチンに ExtractFileNameと ExtractFilePathがあります。

これらの関数を使用してパスとファイル名を取り出す事ができます。

begin
  Edit1.Text
        := ExtractFileName('C:\Program Files\Borland\Delphi 2.0\Bin\Delphi32.exe');
  Edit2.Text
        := ExtractFilePath('C:\Program Files\Borland\Delphi 2.0\Bin\Delphi32.exe');
end;

    Memoの中で特定の文字列を置換する

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

Memoの中で特定の文字列を別の文字列で置換する事はできますか ?

A:

以下のコードを参考にしてください。MemoコンポーネントのLinesを検索して、「フォーム」という文字列があれば「Form」に変換します。

procedure TForm1.Button1Click(Sender: TObject);
var
  i : integer;
  place : integer;
  s1 : string;
  SearchStr : string;
  ReplaceStr : string;
begin
  //'フォーム'を 'Form' に置換します。
  SearchStr := 'フォーム';
  ReplaceStr := 'Form';

  for i := 0 to Memo1.Lines.Count -1 do begin
    s1 := Memo1.Lines[i];
    Repeat
      Place := pos(SearchStr, s1);
      if place > 0 then begin
        Delete(s1, Place, Length(SearchStr));
        Insert(ReplaceStr, s1, Place);
        Memo1.Lines[i] := s1;
      end;
    until place = 0;
  end;
end;

    PageControl とTabControl を動的に作成する

該当するバージョン: Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

PageControlとTabControlを動的に作成するにはどのようにすればよいでしょうか

A:

以下のコードを参考にしてください。ボタンが押されたときに、PageControl、TabSheet、TabSheet上の Buttonを作成します。

unit PgCtrl;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls,ComCtrls;  // uses 節には ComCtrls を追加します。

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure TestMethod(Sender: TObject);  // Button のイベントハンドラ
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
  PageControl : TPageControl;
  TabSheet : TTabSheet;
begin
  // PageControl を作成します。
  PageControl := TPageControl.Create(Self);
  PageControl.Parent := Self;

  // 1ページ目を作成してPageControlに関連づけます。
  TabSheet := TTabSheet.Create(Self);
  TabSheet.Caption := 'Tabsheet 1';
  TabSheet.PageControl := PageControl;

  // 1ページ目に Button コントロールを追加します。

  with TButton.Create(Self) do
  begin
    Caption := 'Button 1';
    OnClick := TestMethod;  // イベントハンドラを指定します。
    Parent := TabSheet;
  end;

  // 2ページ目を作成して PageControl に関連づけます。

  TabSheet := TTabSheet.Create(Self);
  TabSheet.Caption := ' Tabsheet 2';
  TabSheet.PageControl := PageControl;
end;

procedure TForm1.TestMethod(Sender: TObject);
begin
  ShowMessage('Hello');
end;

end.

    ListBoxで複数選択した内容を取得する

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

ListBoxの MultiSelectプロパティが True の時に、複数選択した内容はどのように取得すればよいのでしょうか ?

A:

以下のコードを参考にしてください。ボタンが押されたときに選択された ListBoxの内容を ShowMessageを使用して表示します。

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  for Loop := 0 to Listbox1.Items.Count - 1 do begin
    if Listbox1.Selected[i] then
      ShowMessage(Listbox1.Items.Strings[i]);
  end;
end;

    Memoコンポーネントの現在表示されている行数を取得する

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

Memoコンポーネントの現在表示されている行数を取得する事はできますか ?

A:

以下のコードを参考にしてください。実行時に関数の引数として Memo コンポーネントを渡すと行数が戻り値として取得できます。

注: 関数はフォームの private部等に宣言します。

function TForm1.MemoLinesShowingLong(Memo: TMemo): integer;
Var
  Oldfont: HFont;  // Font
  DC: THandle;     //デバイスコンテキストハンドル
  i: integer;      
  Tm: TTextMetric; // TextMetric 構造体
  TheRect: TRect;
begin
  DC := GetDC(Memo.Handle); // Memo のデバイスコンテキストを取得する
  try
   //MemoのFontを選択する
    OldFont := SelectObject(DC, Memo.Font.Handle);
    try
      GetTextMetrics(DC, Tm);  //TextMetric 情報を取得する
      Memo.Perform(EM_GETRECT, 0, longint(@TheRect));
      Result := (TheRect.Bottom - TheRect.Top) div
         (Tm.tmHeight + Tm.tmExternalLeading);
    finally
      SelectObject(DC, Oldfont); // フォントを選択する
    end;
  finally
    ReleaseDC(Memo.Handle, DC); // デバイスコンテキストを開放する
  end;
end;

    文字列操作に関する関数は、Delphiで提供される他にないのですか ?

該当するバージョン: Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

文字列操作に関する関数は、Delphiで提供される他にないのですか ?

A:

以下に文字列操作の関数をお知らせします。

// 文字が数字かどうかを判定します
function IsDigit(ch: char): boolean;
begin
  Result := ch in ['0'..'9'];
end;

// 文字が英大文字かどうかを判定します
function IsUpper(ch: char): boolean;
begin
  Result := ch in ['A'..'Z'];
end;

// 文字が英小文字かどうかを判定します
function IsLower(ch: char): boolean;
begin
  Result := ch in ['a'..'z'];
end;

// 文字を大文字に変換します
function ToUpper(ch: char): char;
begin
  Result := chr(ord(ch) and $DF);
end;

// 文字を小文字に変換します
function ToLower(ch: char): char;
begin
  Result := chr(ord(ch) or $20);
end;

// sに渡されたすべての文字列の最初の文字を大文字に変換します
function Proper(const s: string): string;
var
  i: Integer;
  CapitalizeNextLetter: Boolean;
begin
  Result := LowerCase(s);
  CapitalizeNextLetter := True;
  for i := 1 to Length(Result) do
  begin
    if CapitalizeNextLetter and IsLower(Result[i]) then
      Result[i] := ToUpper(Result[i]);
    CapitalizeNextLetter := Result[i] = ' ';
  end;
end;

// これより以下の関数はDelphi2.0では提供されていますが、
// Delphi 1.0では提供されていません

// TrimRight関数は指定された文字列 Sから
// 末尾部分の空白と制御文字を取り除きます。
function TrimRight(const s: string): string;
var
  i: integer;
begin
  i := Length(s);
  while (I > 0) and (s[i] <= ' ') do Dec(i);
  Result := Copy(s, 1, i);
end;

// TrimLeft関数は指定された文字列 sから
// 先頭部分の空白と制御文字を取り除きます。
function TrimLeft(const S: string): string;
var
  I, L: Integer;
begin
  L := Length(S);
  I := 1;
  while (I <= L) and (S[I] <= ' ') do Inc(I);
  Result := Copy(S, I, Maxint);
end;

// Trim関数は指定された文字列 sから
// 先頭部分および末尾部分の空白と制御文字を取り除きます。
function Trim(const S: string): string;
var
  I, L: Integer;
begin
  L := Length(S);
  I := 1;
  while (I <= L) and (S[I] <= ' ') do Inc(I);
  if I > L then Result := '' else
  begin
    while S[L] <= ' ' do Dec(L);
    Result := Copy(S, I, L - I + 1);
  end;
end;

    StringGridのセル内の文字を異なる色で表示するにはどうしますか ?

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

StringGridのセル内の文字を異なる色で表示するにはどうしますか ?

A:

以下のコードは、Fontの色を変更して、TextWidthのピクセル分だけずらした位置から書き込みます。

procedure TForm1.StringGrid1DrawCell(Sender: TObject; Col, Row: Longint;
  Rect: TRect; State: TGridDrawState);
const
  CharOffset = 3;
begin
  with StringGrid1.canvas do
  begin
    font.color := clMaroon;
    textout(rect.left + CharOffset, rect.top + CharOffset, 'D');
      font.color := clNavy;
    textout(rect.left + CharOffset + TextWidth('L'),
      rect.top + CharOffset, 'elphi');
  end;
end;

    マウスでコンポーネント(たとえば、TPanel)を実行時にドラッグ

該当するバージョン: Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

マウスでコンポーネント(たとえば、TPanel)を実行時にドラッグするにはどうしますか ?

A:

以下のコードをコンポーネントの OnMouseDownイベントに記述してください。

procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
const
  SC_DragMove = $F012; 
//コントロールがドラッグされる時にWindowsが送るメッセージ
begin
  ReleaseCapture;
  panel1.perform(WM_SysCommand, SC_DragMove, 0);
end;

    アプリケーションのアイコンを実行時に指定する

該当するバージョン:Delphi 2.0 Delphi 3.0/Delphi 3.1 Delphi 4

Q:

アプリケーションのアイコンを実行時に指定する方法はありますか ?

A:

アプリケーションのアイコンを実行時に指定する方法は 2通りあります。

  1. アイコンファイルを直接指定する場合:

LoadFromFile メソッドを使用してアイコンファイルを直接指定できます。以下のように記述します。

  Application.Icon.LoadFromFile('アイコンファイル名');
  1. アイコンファイルがリソースファイルで定義されている場合:

WindowsAPI 関数 LoadIcon() を使用してアイコンのハンドルを取得して、Application.Icon の Handle に設定します。

リソースファイル MyRes.Res の中でリソース名 'ICON_1'のアイコンが定義されているとき、以下のように記述します。

// リソースファイル指令でリソースを設定します
{$R MyRes.Res}

//フォーム作成時にアイコンを変更します
procedure TForm1.FormCreate(Sender: TObject);

var
   HI:HICON;
begin
   HI := LoadICON(HInstance, 'ICON_1');  { WinAPI }
   if HI <> 0 then
     Application.Icon.Handle := HI;
end;

    ヘルプヒント表示に使われているフォントを変更するには ?

該当するバージョン:Delphi1.0

Q:

Delphiのヘルプヒント表示に使われているフォントを変更するには、どうすればよいでしょうか ?

A:

THintWindowから新しいクラスを派生し、フォームの OnCreateイベントハンドラや initialization部で HintWindowClassにクラスを指定します。このとき、

Application.ShowHint := False; Application.ShowHint := True; 

として代入したクラスを有効にする必要があります。

Delphi(CD-ROM版)では \EXTRAS\EXAMPLES\HINTWINディレクトリに、ヒント文字列のフォントを変更するプログラム例が含まれています。

    デフォルトのメッセージハンドラをオーバーライドするには ?

該当するバージョン:Delphi 2.0,Delphi 3.0/Delphi3.1,Delphi4

Q:

アプリケーションに対するデフォルトのメッセージハンドラをオーバーライドするには、どうすればよいのでしょうか。

A:

オーバーライドしたいメッセージ定数に対する動的メソッドを作成します。

もし、CM_DIALOGKEYメッセージをオーバーライドしたい場合は、まずクラス定義で次のメソッドを定義します。

procedure CMDialogKey(var Msg: TCMDialogKey); message CM_DIALOGKEY;

次に、このメッセージに対応するイベントハンドラを implementation部に記述します。

procedure TForm1.CMDialogKey(var Msg: TCMDialogKey);
begin
  with Msg do
    if CharCode = VK_RETURN then
    begin
      PostMessage(Handle, WM_NEXTDLGCTL, 0, 0);
      Result := 1;
      Exit;
    end;
    inherited;
end;

注意

このプログラム例は、前項目のリターンキーでコントロールを移動する方法に似ていますが、ボタンにフォーカスがある場合でもリターンでコントロールを移動できます。逆に、ボタン上でリターンを押しても、ボタンが押されたことにならないので注意してください。

    アプリケーションを実行中に、フォームをアイコン状態のままにしておきたい

該当するバージョン: Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

アプリケーションを実行中に、フォームをアイコン状態のままにしておきたい場合は、どうすればよいでしょうか ?

A:

WM_QUERYOPENメッセージをトラップします。フォームのクラス定義に次のメソッドを定義します。

procedure WMQueryOpen(var Msg: TWMQueryOpen); message WM_QUERYOPEN;

// 次に、implementation部にイベントハンドラを記述します。

procedure TForm1.WMQueryOpen(var Msg: TWMQueryOpen);
begin
  Msg.Result := 0;
end;

これで、フォームはアイコン状態のままになります。なお、設計時のフォームのWindowState プロパティは wsMinimizedにしておいてください。

    TImageコンポーネントは処理が終わるまでイメージが更新されない

該当するバージョン: Delphi2.0, Delphi3.0/3.1, Delphi4

Q:

TImageコンポーネントの Canvasプロパティに描画しているのですが、TFormや TPaintBoxの Canvasプロパティでは描画中の状態が表示されるのに対し、TImageコンポーネントは処理が終わるまでイメージが更新されないようです。

TImageと TPaintBox/TFormの Canvasでは何が違うのでしょうか ?

A:

TImageコンポーネントの Canvasプロパティは、メモリデバイスコンテキストを使っています。したがって、TImageへの描画中はメモリへの書き込みとなるので画面上は何も更新されません。描画が終了すると、自動的にイメージが画面に表示されるため、新しい内容が更新されます。TImageの Pictureプロパティに何も指定していない場合でも、TImageの Canvasプロパティを使って描画しようとすると自動的にメモリデバイスコンテキストが作成されます。

たとえば、フォームに TImageコンポーネントを配置し、Canvasプロパティを使って描画した内容は、Pictureプロパティの SaveToFileメソッドを使って、ビットマップイメージとしてファイルに保存できます。

    Application.Runが実行される前に、Windowsメッセージを捕らえて処理したい

該当するバージョン: Delphi 1.0

Q:

Application.Runが実行される前に、Windowsメッセージを捕らえて処理したい場合は、どうすればよいでしょうか。

A:

以下のプロジェクトソースは、アプリケーションのウィンドウプロシージャが呼び出される前に Windowsメッセージを捕らえる方法を示しています。この処理が必要なことはまれです。通常は、Application.OnMessageに手続きを割り当てることで、同じことが実現できます。

program Project1;

uses
   Forms, Messages, WinTypes, WinProcs,
   Unit1 in 'UNIT1.PAS' {Form1};

{$R *.RES}

  var
    OldWndProc: TFarProc;

  function NewWndProc(hWndAppl: HWnd; Msg, wParam: Word;
                      lParam: Longint): Longint; export;
  begin
    Result := 0; // デフォルトの WndProc の戻り値
    //ここに Msg 引数で示されたメッセージの処理を記述する
    Result := CallWindowProc(OldWndProc, hWndAppl, Msg, wParam, lParam);
  end;

begin
  Application.CreateForm(TForm1, Form1);
  OldWndProc := TFarProc(GetWindowLong(Application.Handle,
                         GWL_WNDPROC));
  SetWindowLong(Application.Handle, GWL_WNDPROC, Longint(@NewWndProc));
  Application.Run;
end;

    イベントハンドラがどのオブジェクトから呼び出されたかを判別するには ?

該当するバージョン: Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

イベントハンドラがどのオブジェクトから呼び出されたかを判別するために、case文は使えないでしょうか。

A:

Tagプロパティが使えます。Tagプロパティは、どのオブジェクトにもあり、プログラマーの好きな目的のために割り当てられます。フォームに配置したコンポーネントの Tagプロパティを異なる値にしておけば、次のように case文を使ってプログラムできます。

   case (Sender as TButton).Tag of
     1: // Tag = 1 のボタンの処理
     2: // Tag = 2 のボタンの処理
   end;

    フォームの内容をスクロールさせるために、[PgUp]/[PgDn] キーを使用したい

該当するバージョン: Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

フォームの内容をスクロールさせるために、[PgUp]/[PgDn]([ROLLDOWN]/[ROLLUP])キーは使えないでしょうか ?

A:

フォームの OnKeyDown イベントハンドラを定義して、スクロールバー(VertScrollBarプロパティ)を制御できます。

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  with VertScrollbar do
    if Key = VK_NEXT then
      Position := Position + 10
    else if Key = VK_PRIOR then
      Position := Position - 10;
end;

    コンポーネントを作成する際にどのコンポーネントを上位クラスにするか

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

コンポーネントを作成したいのですが、どのコンポーネントを上位クラスにすればよいのかわかりません。

A:

Delphiのビジュアルコンポーネントライブラリ(VCL)には、さまざまなコンポーネントが含まれているので、自分の目的に近いものをさがしてください。

また、「リストボックスやグリッドのようなコントロールにいくつかの機能を付け加えたい」などのような場合には、VCLでは、それらのコントロールに対応した抽象クラスがあります。

抽象クラスは、新しくカスタマイズしたコンポーネントを派生させる元として使用できますので、抽象クラスを使用してください。

Delphiでは、抽象クラスの名前には、TCustomGridなどのように「Custom」という文字列を含みますので使用される場合には、該当するコンポーネントにTCustomがついているものを利用してください。

また、最初から、すべての動作を定義しようとする場合には、以下のコンポーネントを上位クラスとして使ってください。

  • TComponent - 非ビジュアルコンポーネント。
  • TWinControl - ウィンドウハンドルを必要とするコンポーネント。
  • TGraphicControl - ウィンドウハンドルを必要としない、ビジュアルコンポーネント。このクラスには、Paint メソッドがありオーバーライドできます。ただし、Canvasプロパティはありません。
  • TCustomControl - 一般的なビジュアルコンポーネント。ウィンドウハンドルを持ち、一般的なイベントハンドラやプロパティがあります。

また、Paintメソッドや Canvasプロパティもあります。

    コンポーネントの初期化中で値を参照するには、どうしますか ?

該当するバージョン: Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

TPanelから新しいコンポーネントを派生しています。コンストラクタで、BevelWidthプロパティを参照していると、ユーザーが設計時にどんな値を設定しているかに関わらず、常に 1になっているようです。そして、明かにコンストラクタが呼び出された後に、設計時の値が反映されているようです。これは、なぜでしょうか。コンポーネントの初期化中で値を参照する場合は、どこにプログラムすればよいでしょうか。

A:

ストリームからコンポーネントを読み込まれるとき、どのコンポーネントもまずコンストラクタを呼び出して構築されてから、プロパティの値をストリームから読み込みます。コンポーネントの Loadedメソッドは、すべてのストリームが読み込まれた後に呼び出されます。ストリームから読み込まれた状態のコンポーネントに対して何かの処理が必要な場合は、Loadedをオーバーライドしてください。

    コントロールのイベント中で発生した例外を簡単に受け取る方法

該当するバージョン: Delphi 1.0, Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

コントロールのイベント中で発生した例外を簡単に受け取る方法はありませんか ?

A:

例外をトラップするためのフォームメソッドを作成し、Application 変数のOnExceptionメソッドに代入します。このメソッドでは、EDatabaseErrorなどの調べたい例外を検査します。OnExceptionイベントについては、オンラインヘルプを参照してください。以下に、独自の例外処理メソッドを定義するプログラム例を示します。

以下のそれぞれのメソッドの宣言が、クラス定義の中にも必要です

procedure TForm1.MyExcept(Sender: TObject; E: Exception);
begin
  if E is EDatabaseError then
    MessageDlg('Trapped exception', mtInformation, [mbOk], 0)
  else
    {調べたい例外でなかった場合};
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.OnException := MyExcept;
end;

    OpenPictureDialog、SavePictureDialog の使用例

該当するバージョン: Delphi 3.0/3.1, Delphi 4

OpenPictureDialogと SavePictureDialogは、グラフィックファイルを選択した場合に右側のプレビュー領域にイメージが表示されるダイアログボックスです。

以下のコードは OpenPictureDialogと SavePictureDialogを使用した例です。

OpenPictureDialogを使用してグラフィックファイルを Imageコンポーネントに表示します。

Imageコンポーネントに表示されたグラフィックを SavePictureDialogを使用してファイルに保存します。

{ OpenPictureDialog を実行して、指定したファイルを表示 }

procedure TForm1.Button1Click(Sender: TObject);
begin

  try
    if OpenPictureDialog1.Execute then
    begin
      SavePictureDialog1.FileName := OpenPictureDialog1.FileName;
      Image1.Picture.LoadFromFile( OpenPictureDialog1.FileName );
    end;
  except
    on EFOpenError do
      ShowMessage('Open Error');
  end;
end;

{ SavePictureDialog1 を実行して、指定したファイル名でイメージを保存 }

procedure TForm1.Button2Click(Sender: TObject);
begin
   if SavePictureDialog1.Execute then
   begin
     Image1.Picture.SaveToFile(SavePictureDialog1.FileName);
   end;
end;

    CheckListBox で選択された行を取り出す

該当するバージョン: Delphi 3.0/3.1, Delphi 4

CheckListBox に表示される行にはチェックボックスがついています。各チェックボックスが選択されているかどうかは、Checked プロパティを利用して調べることができます。

以下のコードはチェックボックスで選択されている行だけを Memo コンポーネントに取り出します。

var
  i:Integer;
begin
  with CheckListBox1 do
    for i := 0 to Items.Count -1 do
      if Checked[i] = True then
        Memo1.Lines.Add(Items[i]);
end;

    フォームやコンポーネント上のヒントを長い時間表示

該当するバージョン: Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Q:

フォームやコンポーネント上のヒントがすぐ消えてしまいます。もっと長い時間表示させておくにはどうすればいいのですか ?

A:

Application.HintHidePause をデフォルト値の 2500ms よりも大きな値に設定してください。

    TAnimate で AVI を再生

該当するバージョン: Delphi 3.0/3.1, Delphi 4

Q:

どうすればリソースファイルに AVI を格納、すなわち外部ファイルなしで TAnimate で AVI を再生できるのですか ? リソースを TAnimation から参照するにはどうすればいいのですか ?

A:

あまり使われませんが 'AVI' と呼ばれるリソースタイプがあります。このリソースタイプを使用すると、リソース中のデータを AVI として扱うことができます。

手順:

  1. 新しい RES ファイルを作成する。(Resource Workshop が便利です。32 ビットのリソースファイルを作らなければいけません)
  2. 'AVI' という種類を RES プロジェクトに作ります。(Resource Workshop の場合は [ファイル|追加] を選びファイル形式を [ユーザー定義リソース]にして AVI ファイルを指定します。[カスタムリソースの指定] ダイアログで [新規] ボタンを押し 'AVI' を追加して登録します。

注意: 自動的に作成された名前は 'AVI_1' のようになります。プロジェクトを保存する前に後で意味が分かるような適切な名前に置き換えておくことを勧めます。(たとえば 'About' - 大文字小文字が区別されます。)

  1. RES プロジェクトに名前を付けて保存します (例: RORY.RES)
  2. Delphi 3 に戻り、リソースファイルを $R コンパイラ指令で読み込むよう指定します。 (例: {$R 'C:\AVI\RORY.RES'} )
  3. TAnimate の RESName プロパティをリソースの名前にします。(リソースの種類名の 'AVI' ではありません。)

例: Animate1.RESName := 'About';

Animate1.Active := True;

    ドラッグアンドドロップの機能の作成

該当するバージョン: Delphi 2.0, Delphi 3.0/3.1, Delphi 4

ドラッグアンドドロップの機能の作成

テキスト項目やグラフィックオブジェクトに適用できるドラッグアンドドロップの機能の作成について説明します。通常のテキストは、機能を達成するために行なう手順を記述してあります。{}括弧で囲まれたテキストは、説明や背景となる情報のためのものです。

  1. フォームに2つの Edit ボックスを配置します。(1つは、ドラッグアンドドロップを行なうためのソースとなるテキストのためのもので、もう1つは、テキストをドロップするディスティネーションのためのものです。)
  2. 最初の Edit ボックスを選択し、オブジェクトインスペクタの name プロパティにSourceEdit と入力します。
  3. 3.2番目の Edit ボックスを選択し、name プロパティに SenderEdit と入力します。{Source や Sender は名前に対して必ず必要ではありません。OnDragOver とOnDragDrop イベントプロシージャのコード内で使用する変数として定義します。
  4. SourceEdit は、ドラッグ操作が始まる場所であり、SenderEdit はドロップされるコントロールです。このことや関連するトピックについての詳細は、オンライントピックを参照してください。}
  5. SourceEdit コンポーネントを選択し、オブジェクトインスペクタの DragMode プロパティを dmAutomatic に設定します。
  6. SenderEdit コンポーネントを選択し、オブジェクトインスペクタのイベントを選択し、OnDragOver をダブルクリックします。
  7. begin と end; の間に、Accept := True; と入力します。{Accept 変数は、OnDragOver イベント手続きの引数によって提供されます。}
  8. オブジェクトインスペクタのイベントから OnDragDrop イベントをダブルクリックします。
  9. begin と end; の間に、SenderEdit.Text := SourceEdit.Text と入力します。
  10. アプリケーションを実行すると、SourceEdit コンポーネントをクリックして SenderEdit コンポーネントまでドラッグアンドドロップできます。SenderEdit へドロップすると、Text の内容は、SourceEdit の Text と同じになります。

    Windows メッセージのトラップ処理

該当するバージョン: Delphi 2.0, Delphi 3.0/3.1, Delphi 4

Delphi は、VCL コントロールに送られるメッセージをトラップする多くの方法を提供しますが、あるウィンドウのウィンドウプロシージャをトラップして置き換える素早くかつ効率的な方法を必要とする場合があります。ここで述べる方法は、ウィンドウやウィンドウハンドルプロパティのある VCL コントロールのウィンドウメッセージを効率的にトラップします。

背景:

作成されたすべてのウィンドウには、ウィンドウに関連する情報を保持する構造体がシステムによって作成されます。構造体に含まれる情報には、ウィンドウの親、インスタンス情報、ウィンドウのメインウィンドウプロシージャのアドレスなどがあります。

このプロシージャを通して、すべてのウィンドウメッセージは特定のウィンドウに送られます。

Windows API は、この構造体に含まれる値の検索や設定を行なう機能を提供するので、ウィンドウのメインウィンドウプロシージャのアドレスを検索しユーザーが作成した関数に指すように再設定することができます。

メインウィンドウプロシージャを置き換えるには、API関数の SetWindowsLong()を使用し処理するウィンドウのハンドル、GWL_WNDPROC(ウィンドウプロシージャを置き換えることを関数に知らせます)、取って代わる自作の関数のアドレスを渡します。

SetWindowsLong()関数をコールすると、関数は置き換える以前の値を戻します。トラップする必要のないすべてのメッセージのためには本来のプロシージャをコールするので、その以前の値を保存しておきます。処理が終わったら、本来のプロシージャを再設定することでメッセージ処理が通常に戻ります。

例のコードでは、TDBGridコンポーネントの WM_VSCROLLメッセージをトラップすることでユーザーが垂直スクロールバーをスクロールしたことがわかります。コントロールへのすべてのメッセージはまず自作のプロシージャへ渡されます。コンポーネントが受け取る前に、イベントを効率的にトラップおよび処理を行なうことができます。

トラップのための他のメッセージリストを取得したい場合には、オンラインヘルプからWM_で始まるメッセージを検索してください。

以下のコードは、16ビットと 32ビットの Delphiでコンパイルできます。

unit WinProc1;

interface

uses
{$IFDEF WIN32}
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, Grids, DBGrids, DB, DBTables;
{$ELSE}
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
  Controls, Forms, Dialogs, DB, DBTables, Grids, DBGrids;
{$ENDIF}

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    Table1: TTable;
    DataSource1: TDataSource;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

type
{$IFDEF WIN32}
  WParameter = LongInt;
{$ELSE}
  WParameter = Word;
{$ENDIF}
  LParameter = LongInt;

{ 置き換えるウィンドウプロシージャを保持する変数の宣言 }
var
  OldWindowProc : Pointer;

function NewWindowProc(WindowHandle : hWnd;
                       TheMessage   : WParameter;
                       ParamW       : WParameter;
                       ParamL       : LParameter) : LongInt
{$IFDEF WIN32} stdcall; {$ELSE} ; export; {$ENDIF}
begin

{ 任意のメッセージを処理 }
  if TheMessage = WM_VSCROLL then begin
    ShowMessage('The vertical scrollbar is scrolling!');
  end;

{ メッセージの処理をこれ以上行なわない場合は }
{ ここで Exit と ゼロを返す }

{ 古いウィンドウプロシージャを }
{ コールしてメッセージを処理する }
  NewWindowProc := CallWindowProc(OldWindowProc,
                                  WindowHandle,
                                  TheMessage,
                                  ParamW,
                                  ParamL);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
{ コントロールの新しいウィンドウプロシージャを設定し }
{ 古いウィンドウプロシージャを保持する }
  OldWindowProc := Pointer(SetWindowLong(DbGrid1.Handle,
                                         GWL_WNDPROC,
                                         LongInt(@NewWindowProc)));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
{ ウィンドウプロシージャを }
{ 古いウィンドウプロシージャに戻す }

  SetWindowLong(DbGrid1.Handle,
                GWL_WNDPROC,
                LongInt(OldWindowProc));

end;

end.

(*
{ プログラムのメインソースファイル }
program WinProc;

uses
  Forms,
  WinProc1 in 'WinProc1.pas' {Form1};

{$R *.RES}

begin
{$IFDEF WIN32}
  Application.Initialize;
{$ENDIF}
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
*)