Delphi XE5でFireMonkeyのグリッドのセルの背景に画像を表示する

Delphi XE5でFireMonkeyのグリッドのセルの背景に画像を表示できたので作業の記録をまとめました。

006

グリッドのセルはTStyledControlクラス

グリッドのセルはTStyledControlクラスで構成されます。
TStyledControlクラスの派生クラスであるTEdit・TCheckBox・TProgressBar・TPopupBox・TImageControlなどのクラスは、グリッドのセルに使用できます。

今回は、TImageCellの上にTEditを配置したクラスをセルに使用することにします。

type
  /// <summary>
  /// 背景に画像を表示するセル
  /// </summary>
  TImageEditCell = class(TImageCell)
  private
    FEdit: TEdit;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

constructor TImageEditCell.Create(AOwner: TComponent);
begin
  inherited;
  Self.EnableOpenDialog := False;

  FEdit := TEdit.Create(Self);
  FEdit.Parent := Self;
  FEdit.Align := TAlignLayout.alClient;
end;

destructor TImageEditCell.Destroy;
begin
  FreeAndNil(FEdit);
  inherited;
end;

セルのDataプロパティを通してグリッドとセルは値をやりとりする

セルのDataプロパティを通してグリッドとセルは値をやりとりします。

セルの持つ情報を表現するクラスを作成します。

少し長いですが、単純なクラスなので全文を掲載します。

type
  /// <summary>
  /// TImageEditCellの値を表現するクラス
  /// </summary>
  TImageEditCellData = class(TPersistent)
  private
    /// <summary>
    /// セルに表示する文字列
    /// </summary>
    FText: string;

    /// <summary>
    /// セルの背景画像
    /// </summary>
    FBitmap: TBitmap;
  public
    constructor Create; overload;

    /// <param name="Text">
    /// セルに表示する文字列
    /// </param>
    /// <param name="Path">
    /// セルの背景画像のファイルのパス
    /// </param>
    constructor Create(Text, Path: string); overload;

    destructor Destroy; override;

    /// <summary>
    ///   値をコピーする
    /// </summary>
    procedure Assign(Source: TPersistent); override;
  published
    /// <summary>
    /// Editに表示する文字列
    /// </summary>
    property Text: string read FText write FText;
    /// <summary>
    /// TImageCellに表示する画像
    /// </summary>
    property Bitmap: TBitmap read FBitmap write FBitmap;
  end;

{ TImageEditCellData }

procedure TImageEditCellData.Assign(Source: TPersistent);
var
  Data: TImageEditCellData;
begin
  if Source is TImageEditCellData then
  begin
    Data := Source as TImageEditCellData;
    FText := Data.Text;
    FBitmap.Assign(Data.Bitmap);
  end;
end;

constructor TImageEditCellData.Create;
begin
  FBitmap := TBitmap.Create;
end;

constructor TImageEditCellData.Create(Text, Path: string);
begin
  FBitmap := TBitmap.Create;
  FText := Text;
  FBitmap.LoadFromFile(Path);
end;

destructor TImageEditCellData.Destroy;
begin
  FreeAndNil(FBitmap);
  inherited;
end;

Dataプロパティは次のように定義されていて、値の取得や設定の時にはGetDataメソッド・SetDataメソッドが呼び出されます。

property Data: TValue read GetData write SetData;

TImageEditCellのDataプロパティでTImageEditCellDataクラスをやりとりするようにします。

  /// <summary>
  /// 背景に画像を表示するセル
  /// </summary>
  TImageEditCell = class(TImageCell)
  private
    …
    FImageTextCellData: TImageEditCellData;
  protected
    function GetData: TValue; override;
    procedure SetData(const Value: TValue); override;
  public
    …
  end;

constructor TImageEditCell.Create(AOwner: TComponent);
begin
  …
  FImageTextCellData := TImageEditCellData.Create;
end;

destructor TImageEditCell.Destroy;
begin
  FreeAndNil(FImageTextCellData);
  …
end;

function TImageEditCell.GetData: TValue;
begin
  Result := FImageTextCellData;
end;

procedure TImageEditCell.SetData(const Value: TValue);
begin
  if Value.IsType<TImageEditCellData> then
  begin
    FImageTextCellData.Assign(Value.AsType<TImageEditCellData>);
    FEdit.Text := FImageTextCellData.Text;
    Self.Bitmap.Assign(FImageTextCellData.Bitmap);
  end;
end;

とりあえず、セルに値を設定する機能だけ実装しました。

セルのオブジェクトはTColumnクラスのCreateCellControlメソッドで生成する

セルのオブジェクトはTColumnクラスのCreateCellControlメソッドで生成します。

CreateCellControlメソッドでTImageEditCellクラスを返すTColumnクラスの派生クラスを作成します。

type
  /// <summary>
  /// セルにTImageEditCellを使う列クラス
  /// </summary>
  TImageEditColumn = class(TColumn)
  protected
    function CreateCellControl: TStyledControl; override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

constructor TImageEditColumn.Create(AOwner: TComponent);
begin
  inherited;
end;

function TImageEditColumn.CreateCellControl: TStyledControl;
begin
  Result := TImageEditCell.Create(Self);
end;

TGridクラスのAddObjectメソッドで列クラスを登録する

作成した列クラスをグリッドに登録します。

フォームにTGridを配置し、フォームのOnCreateイベントでグリッドの設定を行います。

procedure TForm1.FormCreate(Sender: TObject);
begin
  Grid1.RowCount := 5;
  Grid1.RowHeight := 64;
  Grid1.AddObject(TImageEditColumn.Create(Grid1));
end;

TGridクラスのOnGetValueイベントで、セルの値を設定する

TGridクラスのOnGetValueイベントで、セルの値を設定します。

まずセルに表示する値を保持する変数を作成します。

type
  TForm1 = class(TForm)
    …
  private
    { private 宣言 }
    FCellDataList: TObjectList<TImageEditCellData>;
    …
  end;

フォームのOnCreateイベントでセルに表示する値を作成します。

procedure TForm1.FormCreate(Sender: TObject);
begin
  …

  FCellDataList := TObjectList<TImageEditCellData>.Create;
  FCellDataList.Add(TImageEditCellData.Create('arrow_left_64.bmp',
    'C:\data\arrow_left_64.bmp'));
  FCellDataList.Add(TImageEditCellData.Create('arrow_right_64.bmp',
    'C:\data\arrow_right_64.bmp'));
  FCellDataList.Add(TImageEditCellData.Create('clipboard_copy_lined_64.bmp',
    'C:\data\clipboard_copy_lined_64.bmp'));
  FCellDataList.Add(TImageEditCellData.Create('clipboard_cut_64.bmp',
    'C:\data\clipboard_cut_64.bmp'));
  FCellDataList.Add(TImageEditCellData.Create('clipboard_paste_lined_64.bmp',
    'C:\data\clipboard_paste_lined_64.bmp'));
end;

グリッドのOnGetValueイベントで、セルの値を設定します。

procedure TForm1.Grid1GetValue(Sender: TObject; const Col, Row: Integer;
  var Value: TValue);
begin
  if Col = 0 then
    Value := FCellDataList[Row];
end;

セルの背景を透過する

ここまでのプログラムを実行すると、セルの背景画像が表示されません。

001

TImageEditCellクラスのTEditの背景が白色になっているため、TEditの下にあるTImageCellが見えなくなっています。

背景が透明なTEditのサブクラスを作成します。

type
  /// <summary>
  /// 背景が透明なTEdit
  /// </summary>
  TTransparentEdit = class(TEdit)
  protected
    procedure ApplyStyle; override;
  end;

{ TTransparentEdit }

procedure TTransparentEdit.ApplyStyle;
var
  Obj: TFmxObject;
begin
  inherited;
  Obj := FindStyleResource('background');

  if Assigned(Obj) then
  begin
    TControl(Obj).Opacity := 0;
  end;
end;

TEditではなく、作成したTTransparentEditクラスを使うように修正します。

constructor TImageEditCell.Create(AOwner: TComponent);
var
  Obj: TFmxObject;
begin
  inherited;
  Self.EnableOpenDialog := False;

  FEdit := TTransparentEdit.Create(Self); //TEditをTTransparentEditに変更
  FEdit.Parent := Self;
  FEdit.Align := TAlignLayout.alClient;

  FImageTextCellData := TImageEditCellData.Create;
end;

これで入力欄の背景が透明になり、画像が表示されるようになりました。

最後に

今回は表示だけで、入力のことは考慮していません。
実際に使用するにはいろいろと修正が必要かもしれません。

最後にソースコード全文を掲載します。

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants,
  FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
  System.Rtti, FMX.Layouts, FMX.Grid, FMX.Edit, System.Generics.Collections;

type
  /// <summary>
  /// TImageEditCellの値を表現するクラス
  /// </summary>
  TImageEditCellData = class(TPersistent)
  private
    /// <summary>
    /// セルに表示する文字列
    /// </summary>
    FText: string;

    /// <summary>
    /// セルの背景画像
    /// </summary>
    FBitmap: TBitmap;
  public
    constructor Create; overload;

    /// <param name="Text">
    /// セルに表示する文字列
    /// </param>
    /// <param name="Path">
    /// セルの背景画像のファイルのパス
    /// </param>
    constructor Create(Text, Path: string); overload;

    destructor Destroy; override;

    /// <summary>
    /// 値をコピーする
    /// </summary>
    procedure Assign(Source: TPersistent); override;
  published
    /// <summary>
    /// Editに表示する文字列
    /// </summary>
    property Text: string read FText write FText;
    /// <summary>
    /// TImageControlに表示する画像
    /// </summary>
    property Bitmap: TBitmap read FBitmap write FBitmap;
  end;

  /// <summary>
  /// 背景が透明なTEdit
  /// </summary>
  TTransparentEdit = class(TEdit)
  protected
    procedure ApplyStyle; override;
  end;

  /// <summary>
  /// 背景に画像を表示するセル
  /// </summary>
  TImageEditCell = class(TImageCell)
  private
    FEdit: TEdit;
    FImageTextCellData: TImageEditCellData;
  protected
    function GetData: TValue; override;
    procedure SetData(const Value: TValue); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

  /// <summary>
  /// セルにTImageEditCellを使う列クラス
  /// </summary>
  TImageEditColumn = class(TColumn)
  protected
    function CreateCellControl: TStyledControl; override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

type
  TForm1 = class(TForm)
    Grid1: TGrid;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Grid1GetValue(Sender: TObject; const Col, Row: Integer;
      var Value: TValue);
  private
    { private 宣言 }
  public
    { public 宣言 }
    FCellDataList: TObjectList<TImageEditCellData>;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}
{ TImageEditCell }

constructor TImageEditCell.Create(AOwner: TComponent);
var
  Obj: TFmxObject;
begin
  inherited;
  Self.EnableOpenDialog := False;

  FEdit := TTransparentEdit.Create(Self);
  FEdit.Parent := Self;
  FEdit.Align := TAlignLayout.alClient;

  FImageTextCellData := TImageEditCellData.Create;
end;

destructor TImageEditCell.Destroy;
begin
  FreeAndNil(FImageTextCellData);
  FreeAndNil(FEdit);
  inherited;
end;

function TImageEditCell.GetData: TValue;
begin
  Result := FImageTextCellData;
end;

procedure TImageEditCell.SetData(const Value: TValue);
begin
  if Value.IsType<TImageEditCellData> then
  begin
    FImageTextCellData.Assign(Value.AsType<TImageEditCellData>);
    FEdit.Text := FImageTextCellData.Text;
    Self.Bitmap.Assign(FImageTextCellData.Bitmap);
  end;
end;

{ TImageEditCellData }

procedure TImageEditCellData.Assign(Source: TPersistent);
var
  Data: TImageEditCellData;
begin
  if Source is TImageEditCellData then
  begin
    Data := Source as TImageEditCellData;
    FText := Data.Text;
    FBitmap.Assign(Data.Bitmap);
  end;
end;

constructor TImageEditCellData.Create;
begin
  FBitmap := TBitmap.Create;
end;

constructor TImageEditCellData.Create(Text, Path: string);
begin
  FBitmap := TBitmap.Create;
  FText := Text;
  FBitmap.LoadFromFile(Path);
end;

destructor TImageEditCellData.Destroy;
begin
  FreeAndNil(FBitmap);
  inherited;
end;

{ TImageEditColumn }

constructor TImageEditColumn.Create(AOwner: TComponent);
begin
  inherited;
end;

function TImageEditColumn.CreateCellControl: TStyledControl;
begin
  Result := TImageEditCell.Create(Self);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Grid1.RowCount := 5;
  Grid1.RowHeight := 64;
  Grid1.AddObject(TImageEditColumn.Create(Grid1));

  FCellDataList := TObjectList<TImageEditCellData>.Create;
  FCellDataList.Add(TImageEditCellData.Create('arrow_left_64.bmp',
    'C:\data\arrow_left_64.bmp'));
  FCellDataList.Add(TImageEditCellData.Create('arrow_right_64.bmp',
    'C:\data\arrow_right_64.bmp'));
  FCellDataList.Add(TImageEditCellData.Create('clipboard_copy_lined_64.bmp',
    'C:\data\clipboard_copy_lined_64.bmp'));
  FCellDataList.Add(TImageEditCellData.Create('clipboard_cut_64.bmp',
    'C:\data\clipboard_cut_64.bmp'));
  FCellDataList.Add(TImageEditCellData.Create('clipboard_paste_lined_64.bmp',
    'C:\data\clipboard_paste_lined_64.bmp'));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FCellDataList.Free;
end;

procedure TForm1.Grid1GetValue(Sender: TObject; const Col, Row: Integer;
  var Value: TValue);
begin
  if Col = 0 then
    Value := FCellDataList[Row];
end;

{ TTransparentEdit }

procedure TTransparentEdit.ApplyStyle;
var
  Obj: TFmxObject;
begin
  inherited;
  Obj := FindStyleResource('background');

  if Assigned(Obj) then
  begin
    TControl(Obj).Opacity := 0;
  end;
end;

end.

変更履歴

  • 2013年11月2日
    入力欄の背景を透明にする方法をカスタムスタイを使った方法から、背景が透明なTEditのサブクラスを使用する方法に変更しました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください