cxGridにおけるマスターデータと詳細データの連携設定と操作方法

以下は、cxGridコンポーネントでマスター/詳細構造を実装した際の表示イメージです。左側の展開アイコン(+/-)により、各マスター行に対応する詳細データの表示・非表示を切り替えられます。

設計構造としては、2つのレベル(Level)と2つのDBテーブルを用意します。例えば、「TV廃棄リスト」をマスターテーブル、「TV在庫照会」を詳細テーブルとして関連付けます。この構成により、階層的なデータ表示が可能になります。

まず、それぞれのデータセットを準備し、業務要件に応じたSQLクエリを記述します。その後、マスターと詳細の関連付けを行います。この部分はネット上でも情報が少なく、特に画像付き解説が少ないので、ここでは具体的な設定手順を詳しく説明します。

マスターテーブルの設定

「TV廃棄リスト」を選択し、プロパティパネルで KeyFieldNames を設定します。このフィールドはマスターテーブル内で一意である必要があります。重複値があるとIDEが不安定になる可能性があります(例:Delphi 10.3.3)。

詳細テーブルの設定

「TV在庫照会」を選択し、マスターテーブルとの関連フィールド名を一致させます。詳細テーブル側には一意キーがなくても問題ありませんが、KeyFieldNames は空のままでも構いません。ただし、関連フィールド(例:「部品コード」)は両テーブルで同じ名前・型である必要があります。

データが正しく表示されない場合の対処法

設定後も詳細データが1件しか表示されない、または表示されない場合は、SQL文に ORDER BY 関連フィールド を追加してください。これは内部データバインディングの安定性を確保するために重要です。

詳細データの取得とレコード数のカウント

詳細テーブルのレコード数を取得するには、以下のコードを使用します:

DetailCount := MasterView.ViewData.Rows[i].AsMasterDataRow.ActiveDetailGridView.DataController.RecordCount;

詳細テーブル内の特定セルの値を取得するには:

V := TcxGridDBTableView(MasterView.ViewData.Rows[i].AsMasterDataRow.ActiveDetailGridView).ViewData.Rows[j].Values[k];

実装例:詳細データを抽出してメモ欄に出力

procedure TForm1.Button1Click(Sender: TObject);
var 
  i, j, k, DetailCount: Integer;
  s: string;
  v: Variant;
begin
  Memo1.Clear;
  for i := 0 to MasterView.DataController.RowCount - 1 do begin
    DetailCount := MasterView.ViewData.Rows[i].AsMasterDataRow.ActiveDetailGridView.DataController.RecordCount;
    Memo1.Lines.Append('');

    // マスター行の内容出力
    s := '';
    for k := 0 to 2 do begin
      v := MasterView.ViewData.Rows[i].Values[k];
      if VarIsNull(v) then
        s := s + '; '
      else
        s := s + string(v) + '; ';
    end;
    Memo1.Lines.Append(s);

    // 詳細行の内容出力
    for j := 0 to DetailCount - 1 do begin
      s := ' ';
      for k := 0 to 3 do begin
        if k = 1 then Continue; // 特定カラムをスキップ
        with TcxGridDBTableView(MasterView.ViewData.Rows[i].AsMasterDataRow.ActiveDetailGridView) do
          v := ViewData.Rows[j].Values[k];
        if VarIsNull(v) then
          s := s + '; '
        else
          s := s + string(v) + '; ';
      end;
      Memo1.Lines.Append(s);
    end;
    Memo1.Lines.Append(' 詳細レコード数: ' + IntToStr(DetailCount));
  end;
end;

条件付き展開:詳細データがある場合のみ展開を許可

DataController.OnDetailExpanding イベントを使用して、詳細データが存在しない行の展開を抑制できます:

procedure TForm1.MasterTableDataControllerDetailExpanding(
  ADataController: TcxCustomDataController; 
  ARecordIndex: Integer; 
  var AAllow: Boolean);
var
  DetailRowCount: Integer;
begin
  DetailRowCount := MasterView.ViewData.Rows[ARecordIndex]
                    .AsMasterDataRow.ActiveDetailGridView.DataController.RecordCount;
  AAllow := DetailRowCount > 0; // 詳細データが1件以上ある場合のみ展開を許可
end;

詳細セルのダブルクリックでマスター行を更新し、詳細を折りたたむ

procedure TForm1.DetailViewCellDblClick(
  Sender: TcxCustomGridTableView; 
  ACellViewInfo: TcxGridTableDataCellViewInfo; 
  AButton: TMouseButton; 
  AShift: TShiftState; 
  var AHandled: Boolean);
var
  CurrentRow, SupplierIDCol, PurposeCol: Integer;
  RecordID, SupplierID, Purpose: string;
begin
  CurrentRow := ACellViewInfo.RecordViewInfo.Index;
  SupplierIDCol := ColSupplier.Index;
  PurposeCol := ColPurpose.Index;

  RecordID := DataSource.FieldByName('recordid').AsString;
  Purpose := TcxGridDBTableView(MasterView.ViewData.Rows[MasterView.Controller.FocusedRowIndex]
              .AsMasterDataRow.ActiveDetailGridView).ViewData.Rows[CurrentRow].Values[PurposeCol];
  SupplierID := TcxGridDBTableView(MasterView.ViewData.Rows[MasterView.Controller.FocusedRowIndex]
                 .AsMasterDataRow.ActiveDetailGridView).ViewData.Rows[CurrentRow].Values[SupplierIDCol];

  with UpdateQuery do begin
    Close;
    SQL.Text := Format('UPDATE 廃棄テーブル SET 供給元=%s, 用途=%s WHERE recordid=%s',
                       [SupplierID, QuotedStr(Purpose), RecordID]);
    ExecSQL;
  end;

  DataSource.Refresh;           // データ再読み込み
  MasterView.ViewData.Collapse(True); // 全ての詳細行を折りたたむ
end;

展開・折りたたみ操作の共通コード

// 全行展開
MasterView.ViewData.Expand(True);

// 全行折りたたみ
MasterView.ViewData.Collapse(True);

// 現在選択中の行のみ展開
MasterView.ViewData.Rows[MasterView.DataController.FocusedRowIndex].Expand(True);

// 現在選択中の行のみ折りたたみ
MasterView.ViewData.Rows[MasterView.DataController.FocusedRowIndex].Collapse(True);

列のプロパティ動的設定と取得

// 列の編集タイプを動的に変更
DetailViewColumn1.PropertiesClass := TcxSpinEditProperties;

// 表示フォーマットを設定
TcxSpinEditProperties(DetailViewColumn1.Properties).DisplayFormat := '0.00';

// 設定済みのプロパティ値を取得
ShowMessage(TcxSpinEditProperties(DetailViewColumn1.Properties).DisplayFormat);

利用可能なエディットプロパティクラス

  • TcxTextEditProperties
  • TcxDateEditProperties
  • TcxCalcEditProperties
  • TcxCheckBoxProperties
  • TcxSpinEditProperties
  • ...その他多数(IDEの補完機能で確認可能)

グリッド列の全削除と再生成

// 全列削除
IcxCustomGridDataController(GridTableView.DataController).DeleteAllItems;

// 自動再生成(false = 表示位置を保持しない)
IcxCustomGridDataController(GridTableView.DataController).CreateAllItems(False);

タグ: Delphi cxGrid MasterDetail データバインディング TcxGridDBTableView

7月2日 20:30 投稿