Delphi Programming / Object Pascal
ロゴマーク
[掲載 2012年07月24日] [更新 2014年06月27日] Delphi サンプルプログラム集
   
083_TStringGrid 等のセル編集とインプレイスエディタ
動作確認等 Windows XP(SP3) + Delphi XE Pro + Microsoft Office IME 2007
Windows 7 U64(SP1) + Delphi XE Pro + Microsoft Office IME 2010
083_InplaceEditor.zip [5,114 KB] 2014年06月27日版 (EXE 同梱)



  • 2014年04月26日
  • TStringGrid と TDBGrid の日本語 IME の無効化のコードを変更して,Delphi XE2 以降にも対応したサンプルとした
  • 日本語 IME の無効化関係の記事を追加




TStringGrid 等のセル編集とインプレイスエディタ


主に,TStringGrid や TDBGrid のセルを編集する際に,日本語入力用の IME を制御するサンプルプログラムです.

TStringGrid や TDBGrid のセルのテキストの編集を開始すると,インプレイスエディタというエディタが起動します.セルのテキストの編集は,このエディタで行われます.
インプレイスエディタのクラス名は,TInplaceEdit で,TCustomEdit,TCustomMaskEdit から継承されているクラスです.TEditには,TCustomEditから拡張された機能はありませんから,TInplaceEdit は TEditの持つ機能を全て持っているということになります.

           
TInpaceEdit は protected 部にあるため,そのままでは利用できません.そこで,このページのサンプルでは TStringGrid 等の継承クラスを作成して操作しています.
継承クラスを作成するということは,コンポーネントを作成するということですが,作成したコンポーネントを登録,インストールするのは面倒なので,[派生元のコンポーネントと交換する方法]で説明しているユニットを使用してテストしています.

TListView,TTreeView には TInplaceEdit に相当するクラスの実装はありません.インプレイスエディタのハンドルを取得して操作することになります.
マイクロソフトでは,このエディタを単に「エディトコントロール」としていますが,ここでは,インプレイスなエディタなので,そのままインプレイスエディタと呼ぶことにします.





01_TStringGrid  -  GetEditText イベントで処理


GetEditText イベントは,セルの編集を開始すると発生します.このイベント内でインプレイスエディタのハンドルを取得し,IME を設定します.
ACol の値が 1 の時だけ IME を imHira にし,それ以外では imDisable にします.また,編集中の背景色を変更しています.


図1
設計時画面
図2
imHira の列の場合
図3
imDisable の列の場合

リスト1
TStringGrid のセル編集時に日本語 IME を制御する
protected 部の GetEditText イベントで処理
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    Panel1: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure StringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer;
      var Value: string);
    procedure StringGrid1SetEditText(Sender: TObject; ACol, ARow: Integer;
      const Value: string);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//protected部にアクセスするためのクラス宣言
type
  TMyStringGrid = class(TStringGrid);

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  //StringGrid1のセルを編集可能にする
  StringGrid1.Options := StringGrid1.Options + [goEditing];
end;

//=============================================================================
//  StringGrid1のOnGetEditTextイベント処理
//
//  インプレイスエディタが起動し,セルの値を取得してセットする時に発生する
//  引数のValueに値をセットすると,インプレイスエディタ起動時にその値となる
//=============================================================================
procedure TForm1.StringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer;
  var Value: string);
var
  InpEdit  : TInplaceEdit;
  hInpEdit : HWND;
begin
  //インプレイスエディタのオブジェクトを取得
  InpEdit := TMyStringGrid(StringGrid1).InplaceEditor;

  if Assigned(InpEdit) then begin
    //インプレイスエディタのハンドルを取得して,IMEモードを設定
    hInpEdit := InpEdit.Handle;
    if ACol = 1 then begin
      SetImeMode(hInpEdit, imHira);
    end else begin
      Win32NLSEnableIME(hInpEdit, False);
    end;

    //InplaceEditの背景色の設定
    InpEdit.Brush.Color := clSkyBlue;
  end;
end;

//=============================================================================
//  StringGrid1のOnSetEditTexイベント処理
//  インプレスエディタの値が変化すると自動的に実行される
//=============================================================================
procedure TForm1.StringGrid1SetEditText(Sender: TObject; ACol, ARow: Integer;
  const Value: string);
begin
  //Panel1にインプレイスエディトの値を逐次表示
  Panel1.Caption := Value;
end;

end.




02_TStringGrid  -  継承クラスの CanEditShow メソッドで処理


TStringGrid の継承クラスを作成して,protected 部の CanEditShow メソッド内で処理する例です.
CanEditShow メソッドは,セルの編集開始時に,自動的に実行されるメソッドです.動作仕様は,上のサンプルと同じです.


図4
設計時画面

リスト2
TStringGrid のセル編集時に日本語 IME を制御する
継承クラスの CanEditShow メソッドで処理
unit Unit1;

interface

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

type
  TMyStringGrid = class(TStringGrid)
  protected
    function CanEditShow: Boolean; override;
    function GetEditText(ACol: Integer; ARow: Integer): string; override;
    procedure SetEditText(ACol: Integer; ARow: Integer; const Value: string);
      override;
  end;

  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    Panel1: TPanel;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    MyStringGrid1 : TMyStringGrid;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses plChgCompo;

{$R *.dfm}

// インプレイスエディタのprotected部にアクセスするための宣言
type
  TMyInpEdit = class(TInplaceEdit);


//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  //フォーム作成時にコンポーネントを入れ換えて,セルを編集可能にする
  MyStringGrid1 := TMyStringGrid(ChangeComponent(StringGrid1, TMyStringGrid));
  MyStringGrid1.Options := MyStringGrid1.Options + [goEditing];
end;

{ TMyStringGrid }

//=============================================================================
//  TStringGridのCanEditShowメソッド
//  インプレースエディタが起動した時に実行される
//=============================================================================
function TMyStringGrid.CanEditShow: Boolean;
begin
  Result := inherited CanEditShow;

  //フォントのサイズと背景色を設定
  if InplaceEditor <> nil then begin
    TMyInpEdit(InplaceEditor).Font.Size := 12;
    InplaceEditor.Brush.Color := clSkyBlue;

    if Col = 1 then begin
      SetImeMode(InplaceEditor.Handle, imHira);
    end else begin
      Win32NLSEnableIME(InplaceEditor.Handle, False);
    end;
  end;
end;

//=============================================================================
//  TStringGridのGetEditTextメソッド
//  インプレイスエディタがセルの値を受け取る時に実行される
//  戻り値の値がインプレイスエディタ起動時にセットされる
//=============================================================================
function TMyStringGrid.GetEditText(ACol, ARow: Integer): string;
begin
  Result := inherited GetEditText(ACol, ARow);
end;

//=============================================================================
//  TStringGridのSetEditTexメソッド
//  インプレスエディタの値が変化すると自動的に実行される
//=============================================================================
procedure TMyStringGrid.SetEditText(ACol, ARow: Integer; const Value: string);
begin
  inherited;

  //Panel1にインプレイスエディトの値を逐次表示
  Form1.Panel1.Caption := Value;
end;

end.




03_TDBGrid - 継承クラスの CanEditShow メソッドで処理


TDBGrid にも CanEditShow メソッドがあります.TDBGrid の継承クラスを作成して,このメソッド内で処理する例です.
このサンプルは,Delphi 付属のデータベースエンジンの BDE 接続で,Paradox のデータベースを使用しています.


図5
設計時画面
図6
実行開始画面
図7
日本語 IME は imHira
  • セルの編集中はセルの背景色が変わる
図8
日本語 IME は imHira

リスト3
TDBgGrid のセル編集時に日本語 IME を制御する
継承クラスの CanEditShow メソッドで処理
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Grids, ExtCtrls, DB, DBTables, DBGrids;

type
  TMyDBGrid = class(TDBGrid)
  protected
    function CanEditShow: Boolean; override;
    function GetEditText(ACol: Integer; ARow: Integer): string; override;
    procedure SetEditText(ACol: Integer; ARow: Integer; const Value: string);
      override;
  end;

  TForm1 = class(TForm)
    Panel1: TPanel;
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
    Table1: TTable;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    MyDBGrid1 : TMyDBGrid;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses plChgCompo;

{$R *.dfm}

// インプレイスエディタのprotected部にアクセスするための宣言
type
  TMyInpEdit = class(TInplaceEdit);


//=============================================================================
//  フォーム生成時の処理
//
//  配置コンポーネント類の設定は以下の通り
//
//  Table1.DatabaseName := DBDEMOS;
//  Table1.TableName    := customer.db;
//  DataSource1.DataSet := Table1;
//  DBGrid1.DataSource  := DataSource1;
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  //フォーム作成時にコンポーネントを入れ換える
  MyDBGrid1 := TMyDBGrid(ChangeComponent(DBGrid1, TMyDBGrid));

  //データベースの表を開く
  Table1.Active := True;
  //行の高さを設定
  MyDBGrid1.DefaultRowHeight := 23;
end;

{ TMyDBGrid }

//=============================================================================
//  TDBGridのCanEditShowメソッド
//  インプレースエディタが起動した時に自動的に実行される
//=============================================================================
function TMyDBGrid.CanEditShow: Boolean;
begin
  Result := inherited CanEditShow;

  if InplaceEditor <> nil then  begin
    //セルの背景色
    InplaceEditor.Brush.Color := clMoneyGreen;
    //フォントの設定
    TMyInpEdit(InplaceEditor).Font.Color := clBlue;
    //セルの枠線
    TMyInpEdit(InplaceEditor).BorderStyle := bsSingle;
    //キャレットの位置
    InplaceEditor.SelStart := 0;

    if Col = 2 then begin
      SetImeMode(InplaceEditor.Handle, imHira);
    end else begin
      Win32NLSEnableIME(InplaceEditor.Handle, False);
    end;
  end;
end;

//=============================================================================
//  TDBGridのGetEditTextメソッド
//  インプレイスエディタがセルから値を受け取る時に自動的に実行される
//  戻り値の値がインプレイスエディタにセットされる
//  本サンプルでは使用していないので実装しなくともよい.参考用
//=============================================================================
function TMyDBGrid.GetEditText(ACol, ARow: Integer): string;
begin
  Result := inherited GetEditText(ACol, ARow);
end;

//=============================================================================
//  TDBGridのSetEditTexメソッド
//  インプレスエディタの値が変化すると自動的に実行される
//=============================================================================
procedure TMyDBGrid.SetEditText(ACol, ARow: Integer; const Value: string);
begin
  inherited;

  //Panel1にインプレイスエディトの値を逐次表示
  Form1.Panel1.Caption := Value;
end;

end.




04_TListView - Editing イベントで処理


ListView の Editing イベントは,Item の Caption の編集を開始されると実行されます.このイベント内で処理する例です.


図9
設計時画面
図10
実行結果
  • 編集を開始したら,日本語 IME を imHira にする

リスト4
TListView の Caption 編集時に日本語 IME を制御する
Editing イベントで処理
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    ListView1: TListView;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure ListView1Editing(Sender: TObject; Item: TListItem;
      var AllowEdit: Boolean);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses CommCtrl;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  AColumn : TListColumn;
  AItem   : TListItem;
begin
  ListView1.ViewStyle := vsReport;

  //カラムを作成
  AColumn := ListView1.Columns.Add;
  AColumn.Width := 130;
  AColumn.Caption := '項目';

  AColumn := ListView1.Columns.Add;
  AColumn.Width := 60;
  AColumn.Caption := '値';

  //テスト用のデータをセット
  AItem := ListView1.Items.Add;
  AItem.Caption := 'A';
  AItem.SubItems.Add('Sub A');

  AItem := ListView1.Items.Add;
  AItem.Caption := 'B';
  AItem.SubItems.Add('Sub B');

  AItem := ListView1.Items.Add;
  AItem.Caption := 'C';
  AItem.SubItems.Add('Sub C');
end;

//=============================================================================
//  ListViewのItemが0のCaptionを編集状態にする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  ListView1.ItemIndex := 0;
  ListView1.Selected.EditCaption;
end;

//=============================================================================
//  ListView1のOnEditingイベント
//  Captionの編集開始時に呼ばれるイベント
//
//  ListView_GetEditControlの使用には,usesにCommCtrlが必要
//=============================================================================
procedure TForm1.ListView1Editing(Sender: TObject; Item: TListItem;
  var AllowEdit: Boolean);
var
  EditHandle : HWND;
begin
  //インプレースエディタのハンドルを取得
  EditHandle := ListView_GetEditControl(ListView1.Handle);
  if EditHandle > 0 then begin
    SetImeMode(EditHandle, imHira);
  end;
end;

end.




05_TListView - 継承クラスの CNNotify メソッドで処理


TListView のようなコモンコントロールでは,通知メッセージが発生すると,CNNotify メソッドが自動的に実行されます.このメソッド内で処理する例です.
インプレイスエディタの起動時の通知メッセージ LVN_BEGINLABELEDI で IME を imHira にし,インプレイスエディタが閉じるときの通知メッセージ LVN_ENDLABELEDIT で imClose にします.


図11
設計時画面
図12
編集開始
  • 日本語 IME を imHira にする
図13
編集終了
  • 日本語 IME を閉じる

リスト5
TListView の Caption 編集時に日本語 IME を制御する
継承クラスの CNNotify メソッドを使用
unit Unit1;

interface

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

type
  TMyListView = class(TListView)
  protected
    procedure CNNotify(var Message: TWMNotifyLV); message CN_NOTIFY;
    function CanEdit(Item: TListItem): Boolean; override;
    procedure Edit(const Item: tagLVITEMW); override;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    ListView1: TListView;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyListView1 : TMyListView;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses plChgCompo;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  AColumn : TListColumn;
  AItem   : TListItem;
begin
  //フォーム作成時にコンポーネントを入れ換える
  MyListView1 := TMyListView(ChangeComponent(ListView1, TMyListView));

  MyListView1.ViewStyle := vsReport;

  //カラムを作成
  AColumn := MyListView1.Columns.Add;
  AColumn.Width := 130;
  AColumn.Caption := '項目';

  AColumn := MyListView1.Columns.Add;
  AColumn.Width := 60;
  AColumn.Caption := '値';

  //テスト用のデータをセット
  AItem := MyListView1.Items.Add;
  AItem.Caption := 'A';
  AItem.SubItems.Add('Sub A');

  AItem := MyListView1.Items.Add;
  AItem.Caption := 'B';
  AItem.SubItems.Add('Sub B');

  AItem := MyListView1.Items.Add;
  AItem.Caption := 'C';
  AItem.SubItems.Add('Sub C');
end;

//=============================================================================
//  ListViewのCaptionを編集状態にする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  MyListView1.ItemIndex := 0;
  MyListView1.Selected.EditCaption;
end;

{ TMyListView }

//=============================================================================
//  CanEditメソッド
//  編集状態に入ると自動実行される
//  本サンプルでは使用していないので実装しなくともよい.参考用
//=============================================================================
function TMyListView.CanEdit(Item: TListItem): Boolean;
begin
  Result := inherited CanEdit(Item);
end;

//=============================================================================
//  Editメソッド
//  編集が終了すると自動実行される
//  編集状態を[キャンセル]した場合は実行されない
//  本サンプルでは使用していないので実装しなくともよい.参考用
//=============================================================================
procedure TMyListView.Edit(const Item: tagLVITEMW);
begin
  inherited;
end;

//=============================================================================
//  CNNotifyメソッド(WM_NOTIFYメッセージ処理)
//  TWMNotifyLVはDelphi 2007以降の実装.それより前ではTWMNotifyを使用する
//=============================================================================
procedure TMyListView.CNNotify(var Message: TWMNotifyLV);
var
  hWindow : HWND;
begin
  //編集状態に入ればこの値はインプレイスエディタのハンドル
  hWindow := Message.NMHdr.hwndFrom;

  //通知メッセージを判定して操作
  if Message.NMHdr.code = LVN_BEGINLABELEDIT then begin
    SetImeMode(hWindow, imHira);
  end else
  if Message.NMHdr.code = LVN_ENDLABELEDIT then begin
    SetImeMode(hWindow, imClose);
  end;

  inherited;
end;

end.




06_TTreeView -  継承クラスの CNNotify メソッドで処理


コモンコントロールである TTreeView も通知メッセージが発生すると,CNNotify メソッドが自動的に実行されます.このメソッド内で処理する例です.
インプレイスエディタの起動時の通知メッセージは,TVN_BEGINLABELEDIT で,インプレイスエディタが閉じるときの通知メッセージは,TVN_ENDLABELEDIT です.


図14
設計時画面
図15
編集開始
  • 日本語 IME を imHira にする
図16
編集終了
  • 日本語 IME を閉じる

リスト6
TTreeView の Cation 編集時に日本語 IME を制御する
継承クラスの CNNotify メソッドを使用
unit Unit1;

interface

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

type
  TMyTreeView = class(TTreeView)
  protected
    function CanEdit(Node: TTreeNode): Boolean; override;
    procedure Edit(const Item: tagTVITEMW); override;
    procedure CNNotify(var Message: TWMNotifyTV); message CN_NOTIFY;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    TreeView1: TTreeView;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyTreeView1 : TMyTreeView;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses plChgCompo;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  Node : TTreeNode;
begin
  //フォーム作成時にコンポーネントを入れ換える
  MyTreeView1 := TMyTreeView(ChangeComponent(TreeView1, TMyTreeView));

  //テスト用のノード(Item)を作成
  Node  := TreeView1.Items.Add(nil, 'NODE_0');
  TreeView1.Items.AddChild(Node, 'NODE_0_ SUB1');
  TreeView1.Items.AddChild(Node, 'NODE_0_ SUB2');
  TreeView1.Items.AddChild(Node, 'NODE_0_ SUB3');

  Node  := TreeView1.Items.Add(nil, 'NODE_1');
  TreeView1.Items.AddChild(Node, 'NODE_1_ SUB1');
  TreeView1.Items.AddChild(Node, 'NODE_1_ SUB2');

  //ノードを全て展開
  TreeView1.FullExpand;
end;

//=============================================================================
//  TreeView1のItemを選択して編集状態にする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  TreeView1.SetFocus;
  TreeView1.Items[2].Selected := True;
  //編集開始
  TreeView1.Selected.EditText;
end;

{ TMyTreeView }

//=============================================================================
//  CanEditメソッド
//  編集状態に入ると自動実行される
//  本サンプルでは使用していないので実装しなくともよい.参考用
//=============================================================================
function TMyTreeView.CanEdit(Node: TTreeNode): Boolean;
begin
  Result := inherited CanEdit(Node);
end;

//=============================================================================
//  Editメソッド
//  編集が終了すると自動実行される
//  編集状態を[キャンセル]した場合は実行されない
//  本サンプルでは使用していないので実装しなくともよい.参考用
//=============================================================================
procedure TMyTreeView.Edit(const Item: tagTVITEMW);
begin
  inherited;
end;

//=============================================================================
//  CNNotifyメソッド(WM_NOTIFYメッセージ処理)
//  TWMNotifyTVはDelphi 2007以降の実装.それより前ではTWMNotifyを使用する
//=============================================================================
procedure TMyTreeView.CNNotify(var Message: TWMNotifyTV);
var
  hWindow : HWND;
begin
  //編集状態に入ればこの値はインプレイスエディタのハンドル
  hWindow := Message.NMHdr.hwndFrom;

  //通知メッセージを判定して操作
  if Message.NMHdr.code = TVN_BEGINLABELEDIT then begin
    SetImeMode(hWindow, imHira);
  end else
  if Message.NMHdr.code = TVN_ENDLABELEDIT then begin
    SetImeMode(hWindow, imClose);
  end;

  inherited;
end;

end.




07_インプレイスエディタにおける日本語 IME の無効化


本ページの最初の方の TStringGrid と TDBGrid のインプレイスエディタで,日本語 IME を閉じて無効にするのに,Win32NLSEnableIME 関数を使用しています.
一般に,日本語 IME を閉じて無効にするには,次のようなコードを実行します.ここで,Edit.Handle は,入力コントロールのハンドルです.インプレイスエディタの場合は,そのハンドルです.

    SetImeMode(Edit.Handle, imDisable);

上のコードは,TStringGrid と TDBGrid のインプレイスエディタでは,Delphi XE2 UP1 以降で正常に機能しないことがあります.Delphi XE では正常に動作します.
上のコードで日本語 IME を無効にすると,その後,日本語 IME が制御できなくなる場合があります.つまり,かな漢字入力状態にするこができなくなります.
この現象をを回避するために,2014年04月26日の更新で,Win32NLSEnableIME を使用するコードに変更しました.Win32NLSEnableIME は,内部で WINNLSEnableIME を実行しているだけです.


下図は,Delphi XE5(UP2) Pro での実行結果です.
最初のサンプルで,SetImeMode(hInpEdit, imDisable); を使用した場合です.
「文字列」という文字列を入力した後,「ABC」を入力しています.その後,第 1 列目にフォーカスを移動していますが,日本語 IME が ON にならないため,キーボードでタイプした文字がそのまま入力されています.


[備考]
Win32NLSEnableIME 関数を使用して日本語 IME を無効にする方法は,全てのインプレイスエディタで正常に機能するわけではないようです.次項の ImmAssociateContext 関数で無効化した方が正常に機能する場合もあります.これは,コントロールの内部仕様によるものと思われます.


図17
1 列目の IME が ON にならない




08_TEdit 等の入力コントロールの日本語 IME の無効化


前項の説明は,Delphi XE2 UP1 以降の場合でした.今度は Delphi XE2 UP1 よりも前の Delphi で日本語 IME を閉じて無効にする際の参考です.インプレイスエディタではなく,TEdit 等,一般の入力コントロールの場合です.

前項で示した SetImeMode の ImeMode を imDisable にして,日本語 IME を無効にした後,他の入力コントロールにフォーカスを移動しても,日本語 IME が制御できなくなる現象が発生することがあります.また,かな漢字入力にした後,imDisable のコントロールでの入力が半角の英数字にならないことがあります.
この現象を回避するには,下のサンプルのように,ImmAssociateContext 関数を使用します.
この現象は,Delphi XE2 UP1 で修正されていますが,実際には内部で ImmAssociateContext 関数を実行しているだけです.したがって,Delphi XE2 UP1 以降でも,そのまま使用可能です.

一般の入力コントロールの場合も,本ページのこれまでのサンプルのように入力コントロールにフォーカスが移動したら設定します.オブジェクトインスペクタの設定や,フォーム生成時の設定は,コントロールにフォーカス移動しても設定内容が反映されない場合があります.
また,imDisable が正常に機能しない環境で,オブジェクトインスペクタやフォームの OnCreate 等で imDisable を設定しまっては,ImmAssociateContext 関数を使用する意味がありません.その場合,当然ですが,下のサンプルコードは正常に機能しません.



[備考]
ImmAssociateContext 関数を使用して日本語 IME を無効にする方法は,全ての入力コントロールで正常に機能するわけではないようです.前項の Win32NLSEnableIME 関数で無効化した方が正常に機能する場合もあります.これは,コントロールの内部仕様によるものと思われます.
事実,Delphi XE2 UP1 より前の Delphi では,日本語 IME の無効化に,Win32NLSEnableIME 関数が使用されています.

そのため,Delphi XE2 UP1 より前の Delphi では,TStringGrid 等のインプレイスエディタの日本語 IME の無効化は正常に機能し,逆に,TEdit 等の日本語 IME の無効化が正常に機能しない現象が発生するものと思われます.


図18
設計時画面
図19
実行時画面
  • 日本語 IME のモードの切り替えが正常に行われる

リスト7
TEdit 等の入力コントロールの日本語 IME の操作コード
Delphi XE2 UP1 よりも前の Delphi の場合
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    procedure Edit1Enter(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses imm;

{$R *.dfm}

//=============================================================================
//  Edit1,Edit2,Edit3共有のOnEnterイベント
//  日本語IMEのモードを切り替える
//
//  オブジェクトインスペクタやForm1のOnCreate等でImeModeをimDisableにしないこと
//=============================================================================
procedure TForm1.Edit1Enter(Sender: TObject);
begin
  if Sender = Edit1 then begin
    ImmAssociateContext(TEdit(Sender).Handle, 0);
  end else
  if Sender = Edit2 then begin
    SetImeMode(TEdit(Sender).Handle, imHira);
  end else
  if Sender = Edit3 then begin
    ImmAssociateContext(TEdit(Sender).Handle, 0);
  end;
end;

end.




09_インプレイスエディタのインスタンスをコントロール配列から取得


インプレイスエディタのインスタンスは,次のように,コントロールの配列からも取得できます.
インプレイスエディタが起動すると,コントロールが増えます.その中で検索します.



var
  i       : Integer;
  InpEdit : TInplaceEdit;
begin
  for i := 0 to StringGrid1.ControlCount -1  do begin
    if (StringGrid1.Controls[i] is TInPlaceEdit) then begin
      InpEdit := StringGrid1.Controls[i] as TInPlaceEdit;
      break;
    end;
  end;


TStringGrid の場合,コントロール配列の要素数は,通常は 0 です.インプレイスエディタが起動すると要素数は 1 になります.したがって,次のように,1 番目の要素がインプレイスエディタ・コントロールということになります.



var
  InpEdit : TInplaceEdit;
begin
  if StringGrid1.ControlCount > 0 then
    InpEdit := TInplaceEdit(StringGrid1.Controls[0]);