旧C++Builder FAQ - プログラミング・VCL(3)

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

    タイマを使用してアイコン化している Formを表示したりアイコン化する方法

Q:

設定されている一定時間を経過したら(タイマを使用)、アイコン化しているアプリケーションの Formを表示したり、その逆にアイコン化する方法は

A:

次のようにすれば実現できます。TTimerコンポーネントを配置して、Intervalプロパティに経過時間を設定してください。

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
   if ( WindowState ==  wsMinimized )
      WindowState = wsNormal;
   else
      WindowState = wsMinimized;
}

    スクロールバーのあるフォームでの座標値

Q:

スクロールバー付きのフォームに描画する場合、単に座標値を数字で表すと、実行時にスクロールした場合も、スクロールした画面での座標値に描画されてしまいます。どのようにするとよいのでしょうか

A:

スクロールに関係なくフォームとしての座標値を取得する場合は、スクロールバーオブジェクトから、座標情報を取得します。フォーム上にある、水平または垂直スクロールバーの現在位置は ScrollPosプロパティにより取得できます。

HorzScrollBar->ScrollPos + X // 水平スクロールバーの場合
VertScrollBar->ScrollPos + Y // 垂直スクロールバーの場合

    自作のカーソルを表示したい

Q:

自作のカーソルを表示したいのですが、どのようにしたら良いのでしょうか

A:

簡単な手順を示します。

  1. Image Editorを使って独自のカーソルを持つリソースファイル(.RES)を生成します。この際、デフォルトで付けられるリソースの名前を大文字に変更してください。(例 Cursor1 -> CURSOR1)
  2. 保存した(.RES)をプロジェクトに加えます。
  3. リソースからロードして表示させるには、次のようなコードを書きます。
Screen->Cursors[1] = LoadCursor((void *)HInstance, "CURSOR1");
Screen->Cursor = TCursor(1);

その他、以下のカーソルを変更するサンプルも参考にしてください。

EXAMPLES\APPS\CURSORS 既存のカーソルに変更
EXAMPLES\APPS\SWAT 独自のカーソルに変更

    C++Builderで図形を描画する方法

Q:

フォームに、円とか楕円とか線をフォームに描画するには、どうすれば良いのでしょうか。また、描画する図形のプロパティの指定はどうしますか

A:

フォームには、Canvasという実行時プロパティがあり、このプロパティの描画用メソッド(LineToや Ellipseなど)を使って、フォーム上に描画できます。 もし、特定の描画領域(四角形)の中だけに描画したい場合は PaintBoxというコンポーネントを貼り付けて、PaintBox の Canvasプロパティを使ってください。

たとえば、ボタンを押したときに何か描画したいのであれば、フォーム上のどこかにボタンを配置し、ダブルクリックして、次のようなイベントハンドラを用意すればよいでしょう。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    Canvas->Pen->Width =3;      // ペンの幅
    Canvas->MoveTo(100,0);      // 座標を移動する
    Canvas->LineTo(300,200);    // 線を描画する
    Canvas->Ellipse(100, 0, 300, 200);  // 楕円を描画する
}

描画用のメソッドは、オンラインヘルプで TCanvasを参照してください。なお、サイズが変更されたり、隠れていたフォームが表示されたときにも、描画した内容を保持しておく(再描画する)ためには、描画した内容を変数等に保存してフォームの OnPaintイベントハンドラを定義して描画し直す必要があります。

    OnIdleイベントのオーバーライド

Q:

OnIdleイベントのメソッドをオーバーライドしたい場合の処理は(オブジェクトインスペクタにない場合)

A:

下記の様に TApplicationで定義されたイベントは、オブジェクトインスペクタで設定できない場合でもオーバーライドできます。

ほかに OnActivate、OnDeactivate、OnException、OnHelp、OnHint、OnIdle、OnMessage、OnMinimize、OnRestore、OnShowHintが同様の方法を適用できます。

  1. Formの FormCreateのメソッドにて、以下のコードを追加します。
       Application->OnIdle = IdleFunction;
  1. Formx.CPPに下記のような形で、メソッドを追加
    void __fastcall TForm1::IdleFunction( TObject* Sender, bool& done )
    {
    ....メソッドを記述
    }
  1. ヘッダー(Form?.h)にメンバーを追加
    void __fastcall IdleFunction( TObject* Sender, bool& count );

上記の IdleFunctionは任意に決めた名前ですので、自由に変更できます。

    QuickReportコンポーネントを使用してマスターリンクレポートを行いたい

該当するバージョン:C++Builder 1

Q:

QuickReportコンポーネントを使用してマスターリンクレポートを行なうには、どうしたら良いですか

A:

マスターリンクレポートとは、1対多の関係にある 2つのテーブルのリストです。

マスターテーブルの 1つのレコードに対し、リンクテーブルの複数のレコードが表示されます。

DBDEMOSの VENDORS.DBと PARTS.DBを用いて、マスターリンクレポートを作成します。

  1. [ファイル(F)|新規作成(N)]より表示されるオブジェクトリポジトリから[フォーム]をクリックし「QuickReportマスター/リンクフォーム」を選択します。このフォームの、Detailと DetailFooterという QRBandコンポーネントの BandTypeプロパティに、rbSubDetailと rbGroupFooterが設定されています。これらのプロパティを持つバンドは、QRDetailLinkによってマスターテーブルと接続され、1対多の関係が成立します。
  2. フォームに、TTableコンポーネントと TDataSourceコンポーネントを 2つずつ配置し、オブジェクトインスペクタで、以下のプロパティを設定します。
      Table1->DatabaseName : DBDEMOS
      Table1->TableName : VENDORS.DB
      DataSource1->DataSet :Table1

      Table2->DatabaseName : DBDEMOS
      Table2->TableName : PARTS.DB
      DataSource2->DataSet : Table2

      Table2->MasterSource :DataSource1

ここで、オブジェクトインスペクタの MasterFieldプロパティをダブルクリックして、「リンク項目の設計」ダイアログを表示します。選択可能なインデックス(V)から VendorNoを選択し、リンク項目とマスター項目の VendorNoをそれぞれ選択して「追加(A)」ボタンを押します。「OK」ボタンを押して、リンク項目を設定します。

      QuickReport->DataSource : DataSource1
      DetailLink->DataSource : DataSource2
  1. 「このバンドに置かれたコンポーネントがページヘッダーとして表示されます。」、「このバンドに置かれたコンポーネントがマスター情報となります。」などのようなコメントが記述されている、QRLabel1、QRLabel2、QRLabel3, QRLabel4、QRLabel4を削除します。
  2. Masterバンドの高さを大きくし、Masterバンド上に QRDBTextを 4つ配置します。オブジェクトインスペクタで、以下のプロパティを設定します。
      QRDBText1->DataSource : DataSource1
      QRDBText1->DataField : VendorNo
      QRDBText2->DataSource : DataSource2
      QRDBText2->DataField : VendorName
      QRDBText2->AutoSize : True
      QRDBText3->DataSource : DataSource1
      QRDBText3->DataField : Phone
      QRDBText4->DataSource : DataSource1
      QRDBText4->DataField : FAX
  1. それぞれの QRDBTextの左側に QRLabelを配置して、QRDBText項目にラベルをつけます。オブジェクトインスペクタで、以下のプロパティを設定します。
      QRLabel1->Caption : VendorNo
      QRLabel2->Caption : VendorName
      QRLabel3->Caption : Phone
      QRLabel4->Caption : FAX
  1. Detailバンドには、PARTS.DBの項目を表形式で表示します。Detailバンドの高さを大きくし、Detailバンド上に QRDBTextを 6つ配置します。オブジェクトインスペクタで、以下のプロパティを設定します。
      QRDBText5->DataSource : DataSource2
      QRDBText5->DataField : PartNo
      QRDBText6->DataSource : DataSource2
      QRDBText6->DataField : Description
      QRDBText7->DataSource : DataSource2
      QRDBText7->DataField : OnHand
      QRDBText8->DataSource : DataSource2
      QRDBText8->DataField : OnOrder
      QRDBText9->DataSource : DataSource2
      QRDBText9->DataField : Cost
      QRDBText10->DataSource : DataSource2
      QRDBText10->DataField : ListPrice
  1. Detailバンドの上に新しいバンドを配置します。オブジェクトインスペクタで、以下のプロパティを設定します。
      新しいバンドの name : DetailHeader
      DetailHeader->BandType : rbGroupHeader

      DetailLink->HeaderBand :DetailHeader

これで、このバンドが Detailのヘッダバンドとして機能するようになります。

上記 5と同じ方法で、DetailHeaderに QRLabelを 6つ配置してヘッダ行を作成します。

オブジェクトインスペクタで、以下のプロパティを設定します。

      QRLabel5->Caption : PartNo
      QRLabel6->Caption : Description
      QRLabel7->Caption : OnHand
      QRLabel8->Caption : OnOrder
      QRLabel9->Caption : Cost
      QRLabel10->Caption : ListPrice
  1. ふたつの TTableの Avtiveプロパティを Trueにして、QuickReportをダブルクリックするとレポートプレビューが表示されます。

アプリケーションから、上記のレポートフォームを使用して、印刷したりプレビューするには、このフォームクラスに印刷やプレビューを行なう手続きを追加します。

  1. QRListFormクラスの public宣言に、以下の記述を追加します。
      class  TQRListForm : public TForm
      {
      __published:
          TQuickReport QuickReport;
          TQRBand Title;
            :
            :
      private
      public
          void PreviewList( void );  //  追加
          void PrintList( void );    //  追加
      }
  1. 以下の手続きを記述します。
      void  TQRMDForm::PreviewList( void )
      {
        QuickReport->Preview();
      }

      void  TQRMDForm::PrintList( void )
      {
        QuickReport->Print();
      };
  1. この時点で、このフォームの印刷やプレビューを実行する手段が整いました。ここで、この手続きを実行するメインフォームを作成します。デスクトップには、レポートフォームの他に空のフォーム(Form1)があるはずです。このフォームをメインフォームとして使用します。
  2. メインフォームに、TButtonを 2つ配置します。オブジェクトインスペクタで、以下のプロパティを設定します。
      Button1->Caption : 印刷
      Button2->Caption : プレビュー
  1. Button1の OnClickイベントに以下のように記述します。
      void __fastcall TForm1::Button1Click( TObject Sender );
      {
        QRMDForm->PrintList();
      }

Button2 の OnClick イベントに以下のように記述します。

      void __fastcall TForm1::Button2Click( TObject Sender );
      {
        QRMDForm->PreviewList();
      }
  1. このメインフォームは、QRMDFormを使用しますので、[ファイル(F)|ユニットを使う(U)] から、QRMDFormのユニット(デフォルトであえば Unit2)を選択します。
  2. 以上の手順を行なって、コンパイル、実行してください。

また、\CBuilder\Quickrptに Qrmanual.docという名の DOC形式のマニュアルがあります。参照して下さい。

    フォントのスタイルを制御したい

Q:

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

A:

TFontオブジェクトの Styleプロパティは Setテンプレートを使って表現されています。

例えば、RichEditコンポーネントのフォントを変更するには次のようになります。

   RichEdit1->Font->Style = TFontStyles()<< fsBold << fsItalic;

    RichEditで指定された範囲のフォントスタイルや色を制御したい

Q:

RichEditで指定された範囲のフォントスタイルを制御するには、どうしたら良いのですか

A:

TFontオブジェクトの Styleプロパティは Setテンプレートを使って表現します。

指定された範囲は RichEditコンポーネントの SelStart, SelLengthプロパティから取得できます。指定された範囲のテキスト属性を変更するには SelAttributesプロパティを設定することで可能です。現在選択しているテキストの書式を 1つ変更するには、SelAttributesを読み取って目的の属性を設定します。現在選択しているテキストの書式をすべて変更するには、SelAttributesプロパティを、目的の属性が設定されている TTextAttributesオブジェクトと同じにします。

各プロパティの詳細についてはオンラインヘルプを参照してください。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    int start = RichEdit1->SelStart;
    int length = RichEdit1->SelLength;

    if ( length )
    // 色を変更
        RichEdit1->SelAttributes->Color = clGreen; 
        // 現在の設定の一部を変更
        RichEdit1->SelAttributes->Style 
            = RichEdit1->SelAttributes->Style << fsBold;
        // 新しい設定に変更 
        RichEdit1->SelAttributes->Style 
            = TFontStyles() << fsBold;
}

    リソースから、ビットマップをロードしたい

Q:

リソースファイルから、ビットマップをロードするにはどうしますか

A:

EXEファイルにバインドされているリソース(MyBitmap.res)から、ビットマップリソースとパレット情報をロードします。 まずは、リソースファイルをプロジェクトマネージャを使って、プロジェクトに追加します。

TBitmap::LoadFromResourceNameメソッドが利用できます。

    // HInstance は、アプリケーションのインスタンスです

    // コンポーネントに直接ロードする場合
    Image1->Picture->Bitmap->LoadFromResourceName((int)HInstance, "BITMAP1");
    BitBtn1->Glyph->LoadFromResourceName((int)HInstance, "BITMAP2");

    // TBitmap オブジェクトを動的に作成して利用する場合
    Graphics::TBitmap *NewBitmap = new Graphics::TBitmap;
    NewBitmap->LoadFromResourceName((int)HInstance, "BITMAP1");
       : 
    delete NewBitmap;

    AnsiString の Delete() が思ったように動作しません

Q:

次のようなコードを書いたのですが、期待したとおりに削除されません。何故でしょうか

    if (!Edit1->Text.IsEmpty())
    {
        (Edit1->Text).Delete((Edit1->Text).Length(), 1);
        Label1->Caption = Edit1->Text;
    }

A:

Text等のプロパティは参照なので Deleteを直接呼び出してもプロパティの内容は更新されません。

一旦、AnsiString 型の変数で受け取り処理を行うようにしてください。

    if (!Edit1->Text.IsEmpty())
    {
        AnsiString S = Edit1->Text;
        S.Delete(S.Length(), 1);
        Label1->Caption = S;
    }

    キャプションのないフォームを作成する方法

Q:

キャプションのないフォームを作成したいのですが、どうすれば良いのでしょうか

A:

簡単な方法では、BorderStyleプロパティを bsNoneにすることで実現できます。

しかし、サイズ変更不可、可視境界線なしとなるため、見ためも平坦なものになってしまいます。サイズ変更を可、境界線ありで、キャプションをなくすには、以下のように CreateParamsメソッドをオーバーライドして、フォームのスタイルを設定する必要があります。

class TForm1 : public TForm
{
  __published:
  private:
    void __fastcall CreateParams(TCreateParams &Params);
  public:         // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);
};

void __fastcall TForm1::CreateParams(TCreateParams &Params)
{
   TForm::CreateParams(Params);
   Params.Style = (Params.Style | WS_POPUP) & ~WS_DLGFRAME;
}

    DriveComboBoxのシステムエラーをハンドリングする

Q:

DriveComboBoxで、ディスクの入っていないドライブにアクセスしたときに Windowsのシステムエラーが表示されないようにして、任意の処理をしたい。

A:

エラーが発生した場合の処理は、try - catchで行うことができます。

ただし、ドライブの準備ができていない場合のエラーに対応する例外ハンドラはありませんので、独自に定義する必要があります。

// UNIT1.H
class TForm1 : public TForm
{
    :  (略)
private:
    Byte OldDrive;  // 変更前のドライブを保存する
public:         // ユーザー宣言
    :  (略)
};

// 必要ならば、独自の例外クラスを作成する
class EDriveError : public Exception
{
private:
public:
    // ここに独自の処理を実装
    __fastcall EDriveError(const System::AnsiString Msg) : Exception(Msg) {};
    __fastcall ~EDriveError(void) { }
};

// UNIT1.CPP
// ディスク容量を取得します。エラーが発生すると例外を送出します
long int GetDiskSize(char Drive)
{
   UINT OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
   int Result = DiskSize(UpCase(Drive) - 0x40);
   SetErrorMode(OldMode);
   if ( Result == -1 )
       throw (*new Exception("Drive not ready"));
   return Result;
}

void __fastcall TForm1::DriveComboBox1Change(TObject *Sender)
{
    try {
        GetDiskSize(DriveComboBox1->Drive);
        OldDrive = DriveComboBox1->Drive;
        // 後に示す DirectoryListBox と連携させた場合必要になる処理
        // DirectoryListBox1->Drive = OldDrive;
    }
    catch (Exception &exception)
    {

        // ここで任意の処理を行う
        DriveComboBox1->Drive = OldDrive;
        ShowMessage(exception.Message);
    }
}

// DirectoryListBox と連携させている場合、以下の処理も必要になります

void __fastcall TForm1::DriveComboBox1Enter(TObject *Sender)
{
  DriveComboBox1->DirList = NULL;
  DirectoryListBox1->Drive = DriveComboBox1->Drive;
}

void __fastcall TForm1::DriveComboBox1Exit(TObject *Sender)
{
  DriveComboBox1->DirList = DirectoryListBox1;
}

    ダイアログ上のボタンに、ファンクションキーのショートカットを付けたい

Q:

ダイアログ上に置いたボタンにファンクションキーのショートカットを作成したいのですが、どのようにすれば良いのでしょうか

A:

一般には、Buttonの Captionプロパテに、アンド記号(&)等のアクセラレータ文字を付加することで、容易に実現できます。しかし、この方法では、ファンクションキーを割り当てることはできません。そこで、C++Builderの機能を以下に示すように利用することで、実現できます。

  1. Formの KeyPreviewプロパティをデフォルトの falseから trueにします。
  2. Form OnKeyDownイベントで、キーを判別を行い、該当するキーであれば、ボタンイベントを呼び出すようにします。
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key
                                   , TShiftState Shift)
{
    if (Key == VK_F3)
        Button1Click(Sender);
}

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    ShowMessage("Here is a Button Event!");
}

    TListBoxに関するFAQ

ListBoxコンポーネントについての質問

Q:

指定した行を先頭に表示したい

A:

TopIndexプロパティに先頭にしたい行番号を代入します。

Q:

特定の行に文字の色やサイズの変更を行いたい

A:

Styleプロパティを lbStandard以外にしたのち、OnDrawItemに再描画したい内容を記述します。

例:ListBoxのうち、「http」とかかれた行のみを青色(clNavy)にしたい場合

void __fastcall TForm1::ListBox1DrawItem(TWinControl *Control, int Index,
        TRect &Rect, TOwnerDrawState State)
{
  ListBox1->Canvas->Font->Color = clBlack;
  if (ListBox1->Items->Strings[Index].Pos("http")!=0 )
                ListBox1->Canvas->Font->Color = clNavy;
  ListBox1->Canvas->TextRect(Rect,Rect.Left,Rect.Top, 
  ListBox1->Items->Strings[Index]);
  }

Q:

Textファイルの内容を Listbox表示したい場合どういった方法があるでしょうか

A:

Itemsプロパティに文字列を挿入する場合は、Add関数を呼ぶのが一般的ですが、Textファイルの内容をそのまま入力したい場合はLoadFromFile()メソッドを使用するとよいでしょう。(下記に例を示します)

if (OpenDialog1->Execute()){
  //OpenDialog1にて指定されたファイルの内容をListBoxに表示します。
        ListBox1->Items->LoadFromFile(OpenDialog1->FileName);
}

    MediaPlayerコンポーネントについて

Q:

ファイルの指定及び OPENする方法は

A:

fileNameプロパティにファイル名をセットした後に、Open()メソッドを記述することで可能です。

  if(OpenDialog1->Execute()){
      MediaPlayer1->FileName=OpenDialog1->FileName;
      MediaPlayer1->Open();
  }

Q:

QuickTime形式(.MOV)の再生は可能ですか

A:

コンポーネントは対応そのものはしていますが、OSにドライバがインストールされていなければなりません。

もしドライバが入っていないお場合は、事前に WEBや雑誌の付録 CDなどで入手後、インストールしておく必要があります。

なお、現時点(97/6/26)での WEBでの入手先は下記の通りになっています。

        http://www.quicktime.apple.com

Q:

再生時の動画を指定したコントロール上に表示したい

A:

Displayプロパティに、オーバーレイ表示させたいコンポーネント(ボタン、パネル等)を指定(オブジェクトインスペクタでも指定可能)することでできます。

    フォームファイルをテキストで表示する

C++Builderで使用されているフォームファイル(.DFM)は、フォームに登録されているコンポーネント、およびそのプロパティが保存されています。これらは TStreamクラスから派生している TFileStream等を利用して、プログラムで読み込むことができます。

VCLには、フォームファイルに保存されたオブジェクトの内容をテキストに変換する関数 ObjectBinaryToText が用意されています。この関数は二つの TStream*型の変数をとり、初めの引数にオブジェクトが含まれるストリームを指定し、二番目には変換したテキストを受け取るためのストリームを指定します。

例えば、TEST.DFMに保存されているオブジェクトの情報をフォームに貼ったメモコンポーネント Memo1にテキストで表示してみます。まず、TEST.DFMはファイルですからTStreamから派生しているクラス TFileStreamを使用してストリームを生成します。

  TFileStream* formFile = new TFileStream( "TEST.DFM", fmOpenRead );

この際、ファイルを変更するわけではないので読み込み専用でオープンします(二番目の引数には fmOpenReadを使用します)。また、フォームファイルにはリソースのヘッダが書き込まれているので、ファイルポインタを移動するために TStreamのメソッド ReadResHeaderを呼び出します。

ObjectBinaryToTextに渡すテキストを受け取るためのストリームを用意しなければなりませんが、これはメモリ上のストリーム TMemoryStreamを使用します。この TMemoryStreamも TStreamから派生したクラスで、TStreamの機能を受け継いでいます。

  TMemoryStream* txtForm = new TMemoryStream;

受け取ったストリームからメモコンポーネント Memo1へ内容をコピーするためには TStringsのメソッド LoadFromStreamを使用します。

次の例は TEST.DFMファイルの内容を Memo1へロードします。

  TMemoryStream* txtForm = new TMemoryStream;
  TFileStream* formFile = new TFileStream( "TEST.DFM", fmOpenRead );
  try {
    formFile->ReadResHeader();
    ObjectBinaryToText( formFile, txtForm );
    txtForm->Seek( 0, 0 );
    Memo1->Lines->LoadFromStream( txtForm );
  } catch(...) {}
  delete formFile;
  delete txtForm;

なお、上記プログラムではストリームポインタを先頭にするために Seekメソッドを呼び出しています。Seekはストリームが読み書きを行なう位置を変更するためのメソッドで二番目の引数に基点となる位置(0は先頭、1は現在位置、2は最後)を指定し、基点となる位置からのオフセットを、初めの引数に指定します。したがって Seek( 0, 0 )は先頭に移動することを示します。

    PaintBoxの Colorプロパティを設定しても有効にならない

Q:

PaintBoxコンポーネントの Colorプロパティを設定しても有効にならない。描画するペンの色を設定しても、設定が有効に働きません。

A:

TPaintBoxコンポーネントの Colorプロパティは、TLabelや TButtonコンポーネントのようにオブジェクトインスペクタで設計時に、変更されることはありません。

Colorプロパティは、TPaintBoxの Canvasに描画処理が行われた際にその背景色となります。また、描画メソッドによって、Brushを使うものと Penを使うものがあります。メソッドにあった設定でない場合には、有効に働かないように 感じてしまうかも知れません。どちらを使うかといった判別材料としては、塗りつぶしを行うようなメソッドでは、Brushを使います。なお、TPaintBoxへの描画は、OnPaintイベントで実装しないと、再描画が行われることで、正しい描画にならない場合があるので注意が必要です。以下の、サンプルコードを新規のアプリケーション上に TPaintBoxコンポーネントを配置して、TPaintBoxの Colorプロパティを様々に変化させ、動作をお試しください。

   void __fastcall TForm1::PaintBox1Paint(TObject *Sender)
   {
       PaintBox1->Canvas->Pen->Width = 14;
       PaintBox1->Canvas->Pen->Color = clGreen;
       PaintBox1->Canvas->Ellipse(10, 10, 100, 80);
   }

    TTableコンポーネントに部分文字列を使ってフィルタをかけたい

Q:

TTableコンポーネントの Filter, Filtered, FilterOptionsを設定しましたが、期待したの動作になりません。部分文字でのフィルタをかけるには、どのような設定が必要なのでしょうか。

A:

フィルタをかけるためには、以下のようなコードを書きます。

   CustTable->Filter = "[State] = 'California '";
   /* あるいは */
   // CustTable->Filter = "[PatientAge] >= 18";
   CustTable->Filtered = true;
   CustTable->Refresh();

部分文字列でフィルタリングするためには、FilterOptionsプロパティの foNoPartialCompareがデフォルトの falseに設定されている必要があります。

また、Filterプロパティには、ワイルドカードとして、以下のようにアスタリスク(*)を使います。

[State] = 'C*'

なお、Filterプロパティの設定に不正な設定を行った場合、データセットが不正になってしまい、正しいフィルタリングが行えなくなる場合があります。 Filterプロパティを使ったフィルタ処理は簡易的なものになりますので、細かいフィルタ処理を必要とする場合には、OnFilterRecord イベントハンドラを使うか、Locateメソッドを使うといった、別の方法をご利用されることをお勧めいたします。

    dBASEテーブルに、MDXインデックスファイルを作成する方法

以下のようなコードを実装することで可能です。

    //インデックスを作成するためのボタンイベント
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
        Table1->Close();
        // 第1引数 : インデックス名(インデックスファイル名ではありません)
        // 第2引数 : 項目名
        // 第3引数 : インデックスオプション(TIndexOptions 型)

        Table1->AddIndex( "index1", "item1;item2",
                         TIndexOptions() << ixExpression );
    }

ただし、既に .MDXファイルが存在する場合、エラーとなってしまいます。その場合は、いったん、削除してから実行する必要があります。また、エクスプロラーなどで物理的に削除してしまうと、不整合を起こし、データベースをオープンすることができなくなってしまいますので、注意が必要です。インデクッスの削除は、次のようなコードで行えます。

    //インデックスを削除するためのボタンイベント
    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
        Table1->Exclusive = true;
        Table1->Open();
        Table1->DeleteIndex("index1");
    }

プログラムの実行中に、フォーム上のコンポーネントをマウスで移動させたい

    Q:

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

    A:

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

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

次の変数を Form1クラス内部に定義します。

   class TForm1 : public TForm
   {
          : (略)
   private:     // ユーザー宣言
      bool isDrag;
      TPoint CurrentPos;
      int orign;
          : (略)
   };

Image1 の MouseUp、MouseDown、MouseMove イベントに記述します

   void __fastcall TForm1::Image1MouseDown(TObject *Sender,
                       TMouseButton Button, TShiftState Shift, int X, int Y)
   {
      isDrag = true;
      CurrentPos = ClientToScreen(Point(X, Y));
   }

   void __fastcall TForm1::Image1MouseMove(TObject *Sender,
                       TShiftState Shift, int X, int Y)
   {
       TPoint p = ClientToScreen(Point(X, Y));
       if (isDrag)
       {
           Image1->SetBounds(Image1->Left + (p.x - CurrentPos.x ),
               Image1->Top + ( p.y - CurrentPos.y ),
               Image1->Width, Image1->Height);
       }
   }

   void __fastcall TForm1::Image1MouseUp(TObject *Sender, TMouseButton Button,
                       TShiftState Shift, int X, int Y)
   {
       isDrag = false;
   }

    オープン配列と TVarRec

Delphiでは、オープン配列パラメータとして array of const型の引数を持つ関数などがありますが、C++Builderでは TVarRec*、およびそのパラメータの数を代わりに使います。TVarRecクラスは、格納されているデータの型を判別するための VTypeおよびそのデータ型に対応したそれぞれの格納エリアが用意されています。

また、データを参照するためにそれぞれのデータ型に対応した =オペレータが用意されています。例えば TDataSet::AppendRecord関数は、Delphi では次のように定義されています。

    procedure AppendRecord(const Values: array of const);

C++Builderでは、次のように TVarRec*とそのパラメータの数を引数として持ちます。

    void __fastcall AppendRecord(const System::TVarRec * Values,
                                 const int Values_Size);

例えば、Delphi で次のようなコードは

    Table1.AppendRecord( [1000] );

C++Builder では、

    Table1->AppendRecord( &TVarRec( 1000 ), 0 );

と表現します。AppendRecordの Values_Sizeパラメータには、実際に渡す引数の数から 1引いた値を渡します。複数のパラメータが有る場合には TVarRecの配列を生成して渡します。例えば、三つのフィールド(Integer型、String型、Float型)を追加するコードは、Delphiでは、

    Table1.AppendRecord( [1, 'string1', 1.0] );

C++Builder では、

    TVarRec flds[] = {1, "string1", 1.0};
    Table1->AppendRecord( flds, 2 );

となります。また、上記は OPENARRAYを使用して、次のように一行で記述できます。

    Table1->AppendRecord( OPENARRAY(TVarRec, (1, "string1", 1.0)) );

OPENARRAYは OpenArrayと OpenArrayCountの二つのクラス・テンプレートを使用して、初めに指定した型の配列を生成し、配列の要素を指定した値で初期化します。

このようなオープン配列引数を取る関数は他にもあります。代表的なものでは、Format関数が挙げられるでしょう。Format関数は、指定した書式指定子にしたがって、引数から文字列を書式化します。Format関数は、次のように定義されています。

    System::AnsiString __fastcall Format(const System::AnsiString Format,
                                         const System::TVarRec * Args,
                                         const int Args_Size);

例えば次の例は、"Borland C++Builder 1.0J"を返します。

    Format( "%s %s%s %3.1fJ", OPENARRAY(TVarRec, ("Borland", "C++", "Builder"
           , 1.0)) );

    dBASEテーブルを物理レコードでカレントレコードにしたい

該当するバージョン:C++Builder 1

Q:

dBASEテーブルを物理レコードで指定して、VCLでのカレントレコードにしたいのですが、どのようにすればよいのでしょうか

A:

VCLコンポーネントには、そういった機能はサポートされておりません。しかし、Borland Database Engine(BDE)の APIを利用することで実現可能です。

以下のサンプルを参考にしてください。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    int rec;
    try {
        rec = StrToInt(Edit1->Text);
    } catch (...) {
        rec = 1;
    }
    DbiSetToRecordNo(Table1->Handle, rec);
    Table1->Resync(TResyncMode()<< rmExact);
}

    実行時、TImageコンポーネントを、別のオブジェクトにコピーしたい

Q:

TImageコンポーネントを実行時に作成し、既存の別の Image1オブジェクトをプロパティをはじめ、イベントもコピーしたいのですが、可能でしょうか

A:

C++Builderには、オブジェクトを割り当てる Assignメソッドが用意されているので、

  CopyImg->Assign(Image1);

といった書き方をすることは可能ですが、実行時に EConvertErrorのクラス例外が発生してしまいます。TPictureプロパティのみを Assignして、その他のプロパティやイベントを別のコードで、個々に割り付ける方が一般的でしょう。

例:動的に生成する CopyImgは、UNIT1.Hの TForm1クラスの privateメンバーとして加えておきます。

   void __fastcall TForm1::FormCreate(TObject *Sender)
   {
       CopyImg = new TImage(Form1);
   }

   void __fastcall TForm1::FormDestroy(TObject *Sender)
   {
       delete CopyImg;
   }

   void __fastcall TForm1::Button1Click(TObject *Sender)
   {
        CopyImg->Picture->Assign(Image1->Picture);
        CopyImg->Parent = Form1;
        // 必要なプロパティはここにコピーを行う
        CopyImg->Stretch = Image1->Stretch;
        CopyImg->Left = 200;
        img->Visible = true;
   }

また、一旦、コンポーネントをクリップボードにコピーして、クリップボードから新しい親へ取り出すといった手法が考えられます。そのためには、TClipboard::GetComponent, TClipboard::SetComponentメソッドを使います。また、この方法でもイベントハンドラは、別途設定する必要があります。

例えば、

   #include <vcl\clipbrd.hpp>

   // ボタンイベントで、既存のコンポーネントのコピーを生成します
   void __fastcall TForm1::Button2Click(TObject *Sender)
   {
      // Image をクリップボードへコピーします
      Clipboard()->SetComponent(Image1);
      // 同じ名前のコンポーネントが 2 つ存在しないようにします
      Image1->Name = "OrgImg";
      // クリップボードから Image を取り出して Form 中に配置します
      Clipboard()->GetComponent(this, Form1);
      // 動作確認のため位置を移動して表示させます
      Image1->Left = 200;
      //  イベント処理を割り付けます
      Image1->OnClick = Image1Click;
   }

   // コンポーネントに割り付けたテスト用イベント
   void __fastcall TForm1::Image1Click(TObject *Sender)
   {
       ShowMessage("Hello");
   }

この場合、オンラインヘルプにもあるように、クラスをクリップボードから読み出せるようにするため、RegisterClassesを呼び出して Classes ユニットを登録しなければなりません。そうしないと、登録されていないクラスを読み出そうとするため EClassNotFound例外を発生してしまいます。

void __fastcall TForm1::FormCreate(TObject *Sender)
{
    TComponentClass classes[1] = { __classid(TImage)};
    RegisterClasses(classes, 0);
}

    RegisterClassesメソッドのヘルプ

該当するバージョン:C++Builder 1

CLASSES.HPP

宣言

extern void __fastcall RegisterClasses(System::TMetaClass* const * AClasses , const int AClasses_Size);

説明

RegisterClassesメソッドは AClasses配列パラメータで指定する各オブジェクト型をその配列内の要素ごとに RegisterClass手続きを呼び出して、ストリーミングシステムを使って登録します。

AClasses_Sizeは、登録したいコンポーネントクラスのサイズ -1を指定します。

RegisterClasses を使うと複数のオブジェクト型を簡単に登録できます。

オブジェクトをストリームに格納しそれを読み出せるようにするには、オブジェクトの型を登録しなくてはなりません。

例:以下の処理は、2つのオブジェクト型を 1回の呼び出しで登録します。

  void __fastcall TForm1::FormCreate(TObject *Sender)
  {
     TComponentClass classes[2] = { __classid(TImage), __classid(TButton)};
     RegisterClasses(classes, 1);
  }

[備考] Register関数のオンラインヘルプも参照にしてください。

    DBGridで MultiSelectを使って複数選択されたデータの取り出し方

Q:

DBGridコンポーネントの Optionsプロパティの MultiSelectを trueにして、選択された複数行のデータはどのように取り出すことができますか

A:

Bookmarkを利用することで実装できます。付属の BIOLIFE.DBを使って、以下のコードをお試しください。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    ListBox1->Clear();
    for (int i=0; i < DBGrid1->SelectedRows->Count; i++)
    {
        Table1->Bookmark = DBGrid1->SelectedRows->Items[i];
        ListBox1->Items->Add(Table1->FieldByName("Category")->AsString);
        ListBox2->Items->Add(Table1->FieldByName("Common_Name")->AsString);
    }
}

    データベースのログインプロンプトを表示させないようにしたい

該当するバージョン:C++Builder 3,C++Builder 4

Q:

ODBC接続の Accessデータベースへの接続時に、ログインプロンプトを表示させたくない。

A:

以下に動作確認をしていただくための、手順を示します。

  • TDatabaseコンポーネントの設定
    1. TTable、TDatabase コンポーネントを適当に配置します。
    2. フォーム上の TDatabaseコンポーネントを左マウスで、ダブルクリックします。
    3. 「エリアス名(A)」または「ドライバ名(D)」のいずれかを使い、Accessデータベースを指定します。
    4. 「名前(N)」に、適当な名前を命名してください(例: MyAccess)。
    5. オプションの「ログインプロンプトを出す(L)」と「アクティブでなくなった接続を維持(K)」のチェックを外します。
    6. [デフォルト(E)]ボタンを押して、「パラメータの変更(P)」に、パラメータを追加します。
    7. パスワードが設定されているデータベースであるならば、「パラメータの変更(P)」に表示された、"PASSWORD="にパスワードを明記します(例:PASSWORD=MAGIC)。
    8. [OK]ボタンを押して、TDatabaseコンポーネントの設定を終了します。
  • TTableコンポーネントの設定
    1. DatabaseNameプロパティに、上の4で作成した名前をリストの中から選択します。
    2. TableNameプロパティを設定します。

    今日の日付をフォーマットを指定して、TEditに表示したい

Q:

今日の日付をフォーマットを指定して、DBEditに表示したい。

A:

いくつかの方法が考えられます。一例としては、以下のようなコードが考えられます。

   void __fastcall TForm1::Button1Click(TObject *Sender)
   {
      Edit1->Text = FormatDateTime("dd/mm/yyyy" , Date());
   }

    SQLコンポーネントを使ってレコード数を取得したい

Q:

SQLコンポーネントを使ってレコード数を取得したいのですが、どのようにすれば良いのでしょうか

A:

CBUILDER\EXAMPLES\DBTASKS\QJOINに TEditと TButtonを追加して、以下のようなコードを書くことで、目的のデータベースのレコード数を取得することができます。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    Query1->SQL->Clear();
    Query1->SQL->Add("SELECT COUNT(DISTINCT Company) AS RECC FROM 'Customer.db'");
    Query1->Open();
    Edit1->Text = Query1->FieldByName("RECC")->AsString;
}

    フィルタリング処理について

OnFilterRecordのイベントハンドラで、ワイルドカード'*'を有効に使用することはできません。

この場合の実装は、コード側での対応が必要となります。以下のサンプルはその一例を示すものです。

void __fastcall TForm1::Table1FilterRecord(TDataSet *DataSet, bool &Accept)
{
    // このサンプルは、大文字小文字を区別します
    String str1 = DataSet->FieldByName("Name")->AsString;
    String str2 = "Br";                 // フィルターする文字列

    // 先頭から str2 で指定した文字分が一致する場合
    //  if ( str1.AnsiPos(str2) == 1) 

    // 一致する文字列を含むか比較する場合
       if ( str1.AnsiPos(str2)) 
        Accept = true;
    else
        Accept = false;
}

また、ワイルドカードを使った簡単なフィルタリングは、TTableコンポーネントの Filterプロパティで利用できます。プログラム中で実装する場合は、以下のようなコードになります。

        Table1->Filter = "[Name] = 'Borland*'";

    TShiftStateの値をチェックしたい

Q:

MouseMoveイベントの TShiftStateの値をチェックしたいのですが、どのようにすれば良いのでしょうか

A:

例えば、左マウスが押されているかチェックするには、以下のようなコードになります。

void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
    if (Shift * (TShiftState()<<ssLeft) != TShiftState())
        ShowMessage("Left!");
}

    エリアス名からパス名を取得できますか

Q:

エリアス名からパス名を取得できますか

A:

TSessionの GetAlialParamsメソッドを用いて、エリアスの情報を取得することができます。

この情報からパス名を取得するには、以下のようにします。

  // ボタンを押すとエリアスのパス名を表示します
  void __fastcall TForm1::Button1Click(TObject *Sender)
  {
     TStringList *aList = new TStringList();

     Session->GetAliasParams("BCDEMOS", aList);
     ShowMessage(aList->Values["PATH"]);
     delete aList;
  }

    カレントレコードを保存して、必要なときに復帰させたい

  • Queryコンポーネントの Bookmarkプロパティを利用する方法
    1. TFormクラスの privateメンバーに Bookmarkプロパティの値を保持する変数を用意します。 例: AnsiString CurrentRecord;
    2. 現在のカレントレコードを保持します。 例: CurrentRecord = Query1->Bookmark;
    3. 更新するタイミングで、Bookmarkの値を復帰させます。 例: Query1->Bookmark = CurrentRecord;
  • Borland Database Engine(BDE)の APIを利用する方法(但し、サポートの範囲外です) 以下のような処理を行うことで、Paradoxテーブルのシーケンス番号にカーソルが移動します。
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  DbiSetToSeqNo(Query1->Handle , StrToInt(Edit1->Text));
  Query1->Resync(TResyncMode()<<rmExact);
}

    Blobフィールドへの入出力

Q:

Blobフィールドへの読み書きができません。コンパイルエラーが出ます。

A:

下記のように記述すれば可能です。TTableのFindField(),FieldByName(),Fields[] 等は TField型になっていますので,TBlobField型へキャストを行う必要があります。

  Table->FindField("BINFLD")->LoadFromFile("test.bin");  //error
  ((TBlobField*)Table1->FindField("BINFLD"))->LoadFromFile("test.bin"); //OK

    コンポーネントの動的な作成方法

Q:

コンポーネントを動的に作成するにはどうすればいいのでしょうか

A:

下記のように記述すれば可能です。

注意として必ず、Parentプロパティに親となるコンポーネント(Form、Panel等)を指定するようにしてください。

これにより、不特定多数のコンポーネントを使用したり、配列でコンポーネントをコントロールできるようになります。

 //ユニットのヘッダのprivateに以下の一行を記述 
 TLabel *labels[9]; 

 //以下の記述をボタンのクリック時のイベントに記述
 //また、Form1にPanel1というパネルを事前にはっておいてください

 void __fastcall TForm1::Button1Click(TObject *Sender)
 {
   for (int i=0;i<=9;i++)
   {
     labels[i]=new TLabel(Form1);
     labels[i]->Top=i*20;
     labels[i]->Caption="Label"+IntToStr(i);
     labels[i]->Parent=Panel1;
   }
 }

    MDIの子フォームの close方法

Q:

MDIの子フォームを Closeしても、閉じる事ができない

A:

C++Builderのフォームは、Closeしてもフォームの実体が破棄されないように、デフォルトの設定がされています。

これを破棄するようにするには、Closeするフォームの OnCloseイベントに下記のコーディングを行なってください。

ほかにも OnClosの Actionの指定には

caFree      :フォーム破棄、完全にクローズ
caNone      :フォームクローズ不可
caHide      :フォーム不可視化
caMinimize  :フォームは閉じないでアイコン化

があります

 void __fastcall TMDIChild::FormClose(TObject *Sender, TCloseAction &Action)
 {
   Action = caFree;   //フォーム破棄<-この一行を追加
 }

    Delphiの集合の INに相当する関数はありますか

Q:

Delphiの集合の INに相当する関数はありますか

A:

Delphiの集合はテンプレート Setで実現されています。

その Setのメンバー関数 Containsで要素が含まれているか調べることができます。

以下は TEditの OnKeyDownイベントでシフトキーが押されたかを調べます。

void __fastcall TForm1::Edit1KeyDown( TObject *Sender, WORD &Key, TShiftState Shift )
{
  if( Shift.Contains( ssShift ))
    ShowMessage("Shift key down");
}

    数値をカンマ形式に変換したい

Q:

数値をカンマ形式に変換したい

A:

FormatFloat関数を使用すれば簡単に実現できます。

  // 9,000 の値で表示される
  ShowMessage( FormatFloat( "#,###,###", 9000 ));

    OS がシャットダウンする時にイベントを実行させたい

Q:

OS がシャットダウンする時に特定のイベントを実行させたい

A:

以下のように WM_ENDSESSIONメッセージを取得するように記述していただければ実現可能です。

ヘッダー側

 //-----------------------------------------------------------------
 class TForm1 : public TForm
 {
 __published:   // IDE 管理のコンポーネント
        
 private:       // ユーザー宣言
 public:        // ユーザー宣言
        __fastcall TForm1(TComponent* Owner);
    void __fastcall WMEndSession( TWMEndSession &Message );

 BEGIN_MESSAGE_MAP
   MESSAGE_HANDLER( WM_ENDSESSION, TWMEndSession, WMEndSession )
 END_MESSAGE_MAP( TForm )
 }
 //-----------------------------------------------------------------

ソース側

 //-----------------------------------------------------------------
 __fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
 {
 }
 //-----------------------------------------------------------------
 void __fastcall TForm1::WMEndSession( TWMEndSession &Message )
 {
   // WM_ENDSESSION を取得すると呼び出される
   ShowMessage("EndSession");
 }
 //-----------------------------------------------------------------

    Canvasに図形を書く時マウスボタンがアップするまで図形を決定したくない

Q:

Canvas に図形を書く時、マウスボタンがアップするまで図形を決定したくありません。どのように記述すれば良いのでしょうか

A:

四角などを描く時 MouseButtonUpイベントが発生するまで Drawを決定しない方法は、以下のように記述していただければ実現可能です。

Unit1.h

 //-----------------------------------------------------------------
 class TForm1 : public TForm
 {
 __published:   // IDE 管理のコンポーネント
        void __fastcall FormMouseDown(TObject *Sender, TMouseButton Button,
        TShiftState Shift, int X, int Y);
        void __fastcall FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y);
        void __fastcall FormMouseUp(TObject *Sender, TMouseButton Button,
        TShiftState Shift, int X, int Y);
 private:       // ユーザー宣言
 public:        // ユーザー宣言
        __fastcall TForm1(TComponent* Owner);

    TPoint    Origin,   //基点
              MovePt;   //移動点
    bool      Drawing;  //描画フラグ
 };
 //-----------------------------------------------------------------

Unit1.cpp

 //-----------------------------------------------------------------
 __fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
 {
 }
 //-----------------------------------------------------------------
 void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
        TShiftState Shift, int X, int Y)
 {
   Drawing = true;

   Canvas->MoveTo( X, Y );   //描画開始位置
   Origin = Point( X, Y );   //描画開始位置の保存
   MovePt = Origin;          //移動点の保存
 }
 //-----------------------------------------------------------------
 void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
 {
   if( Drawing )
   {
     Canvas->Pen->Mode = pmNotXor;

     //四角を描画
     Canvas->Rectangle( Origin.x, Origin.y, MovePt.x, MovePt.y );
     MovePt = Point( X, Y );  //移動点の保存
    
     //四角を再描画 ( 再描画しないと残像がでる )
     Canvas->Rectangle( Origin.x, Origin.y, MovePt.x, MovePt.y );
   }
 }
 //-----------------------------------------------------------------
 void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,
        TShiftState Shift, int X, int Y)
 {
   if( Drawing )
   {
     Canvas->Pen->Mode = pmCopy;
     //四角を描画
     Canvas->Rectangle( Origin.x, Origin.y, X, Y );
     Drawing = false;
   }
 }
 //-----------------------------------------------------------------

    DBGrid のカラムの色を変更したい

Q:

DBGrid のカラムの色を変更したい

A:

DBGridのセルの設定は、Columnsプロパティを使用して行うことができます。

以下のコードは動的にセルの幅と色を変更します。

 for( int i = 0; i < DBGrid1->FieldCount; i++ )
 {
   DBGrid1->Columns->Items[i]->Color = clBlue;
   DBGrid1->Columns->Items[i]->Width = 100;
 }

    TDateTimeFlagを使用した TDateTimeのコンストラクタ

Q:

TDateTimeFlagを使用した TDateTimeのコンストラクタの方法は

A:

この場合、ただ「Date」や「Time」と書くとキーワードの重複のためコンパイルできないので、下記のように記述します。

TDateTime dt1("1965/2/20"         ,TDateTimeFlag::Date);     //日付
TDateTime dt2("12:30:35"          ,TDateTimeFlag::Time);     //時間
TDateTime dt3("1965/2/20 12:30:35",TDateTimeFlag::DateTime); //日付時間

    日付フィールドの表示書式の変更

Q:

日付型フィールドの表示書式を変更したい

A:

各日付型フィールドは、DisplayFormatというプロパティを持っていますので、これを下記のように変更してください。

また、下記に代入した ggや eeは DisplayFormat特有のキーワードですので、内容については HELP の「TDateTimeField::DisplayFormat」を参照してください。

// 下記の行を Form の oncreate に追加
((TDateTimeField*)Table1->Fields[12])->DisplayFormat="gg ee \"年\"";

    TrackBarの SelEndに 32767以上の値を入れると範囲指定ができません

Q:

TrackBarの SelEndに 32767以上の値を入れると範囲指定ができません

A:

VCLでは Tracbarの設定を TBM_SETSELで行っていますので 16 bitの制限を越えられません。以下の記述で回避して下さい。

フォームの OnShow イベントに記述

//-----------------------------------------------------------------
void __fastcall TForm1::FormShow(TObject *Sender)
{
  //プロパティ SelStart と同等の処理
  TrackBar1->Perform( TBM_SETSELSTART, false, 32768 );
  
  //プロパティ SelEnd と同等の処理
  TrackBar1->Perform( TBM_SETSELEND, false, 32778 );
}
//-----------------------------------------------------------------

    DBGridを使用して Table1->Post()を呼び出すと例外が生成されます

Q:

DBGridを使用して Table1->Post()を呼び出すと例外が生成されます

A:

DBGrid等で作業を行っている場合、データを変更した場合だけ dsEditになります。

そこで変更せずに Table1->Post()を実行すると dsEdit状態ではありませんので例外が生成されます。

以下のように Modifiedを使用して Button1のイベントを変更して下さい。

      //変更された場合のみ Postを行う
      if( Table1->Modified )
        Table1->Post();

    Local SQL文で日付を条件式で取得したい

Q:

Local SQL文で日付を条件式で取得したい

A:

SQL文で日付を条件式で記述する場合は CASTをする必要があります。以下は SQL 文の例です。DTableテーブルの DDateという DATE型の項目が '97/07/07'のものを取り出します。

   select * from DTable.db where DDate = cast( '97/07/07' as date )

    TFileListBoxの振る舞い

Q:

TFileListBoxを使用して FDをアクセスした後 FDを抜き、別のドライブを参照すると例外が生成されます

A:

TFileListBoxは FDの内容を参照した後、他のドライブを参照する時に前の状態をチェックしているために、この現象が発生しているようです。以下の様に TFileListBoxを 2つ生成して回避して下さい。

//FileListBox1 と FileListBox2 を用意して
//FileListBox2 を FD 用に使います。
void __fastcall TForm1::DriveComboBox1Change(TObject *Sender)
{
  if(DriveComboBox1->Drive == 'a' )
  {
    FileListBox2 = new TFileListBox(this);   //Unit1.h で宣言
    FileListBox2->Parent = this;
    FileListBox2->Top  = 32;           //FileListBox1 とサイズを
    FileListBox2->Left = 200;          //合わせる
    FileListBox2->Height = 217;
    FileListBox2->Width = 217;

    //FD 用に FileListBox2 を使用する
    DirectoryListBox1->FileList = FileListBox2;
    FileListBox2->Directory = DirectoryListBox1->Directory;
    FileListBox2->Drive = DirectoryListBox1->Drive;
    FileListBox2->Update();
    FileListBox2->Show();

    FileListBox1->Drive = NULL;
    FileListBox1->Update();
    FileListBox1->Hide();
  }
  else
  {
    //他のドライブ用に FileListBox1 を使用する
    DirectoryListBox1->FileList = FileListBox1;

    FileListBox1->Directory = DirectoryListBox1->Directory;
    FileListBox1->Drive = DirectoryListBox1->Drive;
    FileListBox1->Update();
    FileListBox1->Show();

    if( FileListBox2 )
    {
      delete FileListBox2;
      FileListBox2 = 0;
    }
  }
}

    ボタンのフォーカスの移動

Q:

ボタンのフォーカスの移動でタブキーで移動すると他のコンポーネントは無視しますが、カーソルキーで行うと無視されなくなってしまいます

A:

ボタンのフォーカスの移動でカーソルキーを使用してしまうと他のコンポーネントへフォーカスが行ってしまいます。

Applicationの OnMessageイベントを使用してボタンを制御する必要があります。

以下のサンプルは OnMessageイベントを取得して Buttonにメッセージを送りボタンのフォーカスを制御します。

このサンプルはフォーム上に 5つのボタンがあり、タブ順序の一番上に Button1がありタブ順序が Button1, Button2, Button3, Button4, Button5と並んでいる必要があります。

ヘッダー

//-----------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE 管理のコンポーネント
        TButton *Button1;
        TButton *Button2;
        TButton *Button3;
        TButton *Button4;
        TButton *Button5;
        TGroupBox *GroupBox1;
        TGroupBox *GroupBox2;
        void __fastcall Button1KeyDown(TObject *Sender, WORD &Key, TShiftState Shift);

        void __fastcall Button5KeyDown(TObject *Sender, WORD &Key, TShiftState Shift);
private:        // ユーザー宣言
public:         // ユーザー宣言
        __fastcall TForm1(TComponent* Owner);
    
    // OnMessage と同じイベントを作成
    void __fastcall KeyMsg(tagMSG &Msg, bool &Handled);
};
//-----------------------------------------------------------------

ソース

//-----------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
  //KeyMsg メソッドを OnMessage に割り当てる
  Application->OnMessage = KeyMsg;
}
//-----------------------------------------------------------------
void __fastcall TForm1::KeyMsg( tagMSG &Msg, bool &Handled )
{
  if( Msg.message == WM_KEYDOWN )
  {
    //フォーカスがあるコントロールにメッセージを送る
    SendMessage( GetFocus(), Msg.message, Msg.wParam, Msg.lParam );
  }
}
//-----------------------------------------------------------------
void __fastcall TForm1::Button1KeyDown(TObject *Sender, WORD &Key,
        TShiftState Shift)
{
  //左と上
  if( Key == VK_LEFT || Key == VK_UP )
  {
    //フォーカスが次のコントロールにフォーカスを送る
    ::SetFocus( Button2->Handle );
  }
}
//----------------------------------------------------------------
void __fastcall TForm1::Button5KeyDown(TObject *Sender, WORD &Key,
        TShiftState Shift)
{
  //右と下
  if( Key == VK_RIGHT || Key == VK_DOWN )
  {
    ::SetFocus( Button4->Handle );
  }
}

    パレットを動的に作成しても反映されていない

該当するバージョン:C++Builder 1,C++Builder 3

Q:

PaintBoxにパレットを動的に作成して割り当てても反映されていない

A:

以下のように二度呼び出して回避して下さい。どの Canvasのパレットを変更する場合も、二度呼び出しが必要です。

   SelectPalette( PaintBox1->Canvas->Handle, Palette, 0 );
   RealizePalette( PaintBox1->Canvas->Handle );
   SelectPalette( PaintBox1->Canvas->Handle, Palette, 0 );
   RealizePalette( PaintBox1->Canvas->Handle );

    ディスプレイの色数の設定が 256モードの時 256色以上のビットマップを表示させたい

該当するバージョン:C++Builder 1

Q:

ディスプレイの色数の設定が 256モードの時、色落ちせず 256色以上のビットマップを表示させたい

A:

全く色落ちせず表示させることは不可能ですが、元の色に似せて表示させることは可能です。以下のサンプルの様に HalftonePaletteを使用していただければ近似色を表示させて、あたかも色落ちしていない様に見せることができます。

__(途中省略)__
      HPALETTE  hpal;
      hpal = CreateHalftonePalette( Canvas->Handle );

      Graphics::TBitmap *bmp = new Graphics::TBitmap();
      bmp->LoadFromFile("same.bmp");

      //以下の関数は記述通り2回ずつ呼び出して下さい
      SelectPalette( Canvas->Handle, hpal, 0 );
      RealizePalette( Canvas->Handle, hpal );
      SelectPalette( Canvas->Handle, hpal, 0 );
      RealizePalette( Canvas->Handle, hpal );

      Canvas->Draw( 0, 0, bmp );
__(途中省略)__

    VCLが階層構造になった資料が欲しい(99/6/4)

該当するバージョン:C++Builder 4

Q:

VCLが階層構造になった資料が欲しい

A:

Visual Component Libraryを階層的に表示するためのツール(Java Applet)を用意しました。

2種類の圧縮ファイルがあります。同一の内容ですので、使用する環境に応じて必要な方をダウンロードしてください。

  • Java Plug-Inを使用して表示する場合は、plug_in.lzh (659KB)
  • Hot Javaブラウザーを使用して表示する場合は、hotj.lzh (717KB)

表示に必要な環境や、使用方法等の詳細は README.TXTを参照してください。

    JPEG画像を編集したい(99/5/12)

該当するバージョン:C++Builder 4

Q:

JPEG画像を編集したい。

A:

TJPEGImageでは、内部ビットマップイメージへのアクセスができませんので TBitmapに変換して、編集した後もう一度変換し直して下さい。次に例を示します。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  TJPEGImage *jpg = new TJPEGImage;
  Graphics::TBitmap *bmp = new Graphics::TBitmap;

  if( OpenDialog1->Execute() )
  {
    //JPEG File のロード
    jpg->LoadFromFile( OpenDialog1->FileName );
    
    //Bitmap への変換
    bmp->Assign( jpg );
    
  }
  for( int cnt=0; cnt < bmp->Width; cnt++ )
  {
    //Pixel の操作
    bmp->Canvas->Pixels[cnt][0] = clRed;
  }
  //JPEG に変換し直し
  jpg->Assign( bmp );
  //JPEG をセーブ
  jpg->SaveToFile("New.jpg");

  delete bmp;
  delete jpg;
}

    Active Xコンポーネントのメソッドの引数の BSTR(99/5/12)

該当するバージョン:C++Builder 4

Q:

Active Xコンポーネントのメソッドの引数に BSTRとありますが、C++Builderから引数を渡すには、どのようにすれば良いでしょうか

A:

BSTRは、Windows APIで定義された型です。次のように定義されています。

typedef OLECHAR *BSTR;

OLECHARは wchar_tと同一でワイド文字の型になります。

C++Builderでは、この BSTRをラップしたクラス WideStringを用意しており、この WideStringクラスは、AnsiStringのように扱いやすくなっています。

例えば次のように ActiveXのインスタンス atxがあるとします。このメソッド GetNameに BSTR *を渡さなければならない時は、WideStringの参照を渡して下さい。(WideStringクラスは operator &を実装していて BSTR *を返す)

   void __fastcall TForm1::Button1Click( TObject *Sender )
   {
     WideString name;
     atx->GetName( &name );
     ShowMessage( name );
   }

また、関数が終了しても有効な文字列を渡す場合には、WideStringのメソッド Detach()を使用して下さい。関数の中で WideStringのインスタンスを生成すると自動変数として生成されます。その関数のスコープが終了する時に自動変数は破棄されますので、WideStringも破棄され、内部に確保している文字列の領域もデストラクタで開放されます。

Detach()を使用すると WideStringとの関連がなくなり、WideStringが破棄されても、その文字列の領域は有効です。

   void __fastcall TForm1::Button1Click( TObject *Sender )
   {
     WideString name("C++Builder");

     atx->GetName( name.Detach() );
   }

ただし、Detach()を使用した場合は、何処かで必ず、文字列のメモリ領域を開放する必要があります。

    オートメーションオブジェクトを使用するとコンパイルエラーになります(99/5/12)

該当するバージョン:C++Builder 4

Q:

次のコードは C++Builder3.0Jで動作していたものですが、4.0Jにするとコンパイルエラーになります

      //OleObject の生成
      Variant excel = CreateOleObject("Excel.Application");

      //Visible プロパティを true にすることにより
      //Excel が表示される
      excel.OlePropertySet( "Visible", true );

      Variant book = excel.OlePropertyGet( "Workbooks" );

      //ワークブックの Open
      book.OleFunction( "Open", "e:\\book1.xls" );
      
      Variant application = excel.OlePropertyGet( "Application" );

      //マクロの実行
      application.OleFunction( "Run", "macro1");

      //保存
      application.OleFunction( "Save" );

      //終了
      application.OleFunction( "Quit" );

A:

オートメーションを使用しているソースコードに

#include <vcl/utilcls.h>

を追記してください。