日本語入力モード時のOnChangeをするには?


かんとく  2010-12-14 00:31:37  No: 39706

お世話になっております。

Delphi2007,XPです。

Form1にMemo1とLabel1を置き、
Memo1の値が変更されたら、その度に値をLabel1に入れたいんですが、日本語入力モードがOFF(直接入力)の場合は問題ありませんが、On(ひらがな)の場合は、入力した文字の変換を確定しなければLabel1の値が変わらず、しかも、確定すると入力した文字数の一文字ずつにOnChangeイベントが起こるので、Label1の変更にも時間がかかります。(一秒前後ですが)

エクセルだと、日本語入力がOnの場合、上にある数式入力欄に文字を入れると、セルの値も同時に変わります。

このエクセルのような動きをしたいのですが、どうしたらよいでしょうか。

よろしくお願いします。

-------------------------------------------
※単純ですが、コードです。

Form1にMemo1とLabel1を置く。

procedure TForm1.Memo1Change(Sender: TObject);
begin

label1.caption:=memo1.text;

end;


Mr.XRAY  2010-12-15 03:40:58  No: 39707

こんにちは.Mr.XRAYです.

>procedure TForm1.Memo1Change(Sender: TObject);
>begin
>  label1.caption:=memo1.text;
>end;

このコードは,Memo1のテキストの代入です.
IMEで変換中の文字列は,まだMemo1のテキストにはなっていません.
確定すると,Memo1のテキストになるわけです.

>エクセルだと、日本語入力がOnの場合、上にある数式入力欄に文字を入れると、セルの値も同時に変わります。

IMEがOFFでもそうなりますよ.「連動入力」というやつですね.
そういう現象から想定すると,今回の場合は,Memo1が発生するメッセージを,
とりあえず,全てLabel1に送ってみてはどうですか.
やってみて,必要がないメッセージがあったら,それを除外するとかですね.


au  2010-12-15 05:18:19  No: 39708

WM_IME_COMPOSITION でIME入力の変更はわかるっぽいのでその時にIMEの変換文字列を取得したら良いかなと。

一応下のコードで変換中の文字列を取得出来ますけど、メモに入力した文字も含めて取得する場合はもう一工夫いりますかね。
最後に追加だけなら楽ですけど。

procedure TForm1.FormCreate(Sender: TObject);
begin
  FMemoWndproc := Memo1.WindowProc;
  Memo1.WindowProc := MemoWndProc;
end;

procedure TForm1.MemoWndProc(var Message: TMessage);
var
  hImc: THandle;

  Buf: PByte;
begin
  if (Message.Msg = WM_IME_COMPOSITION) And ((Message.LParam And GCS_COMPSTR) = GCS_COMPSTR) then
  begin
    hImc := ImmGetContext(Memo1.Handle);

    Buf := GetMemory(1024);
    ImmGetCompositionString(hImc, GCS_COMPSTR, Buf, 1024);

    Label1.Caption := String(Buf);
    FreeMem(Buf);
    ImmReleaseContext(Memo1.Handle, hImc);
  end;
  FMemoWndproc(Message);
end;


状況はいろいろ  2010-12-15 05:22:54  No: 39709

>日本語入力がOnの場合、上にある数式入力欄に文字を入れると、セルの値も同時に変わります。
>このエクセルのような動きをしたいのですが、どうしたらよいでしょうか

どんな入力状況であっても大丈夫なように対処するのは かなり面倒なことだと思う。

procedure TMemo.WndProc(var Msg: TMessage);
var
  IMC: HIMC;
  Len: Integer;
  sCS: string;
begin
  inherited;
  case Msg.Msg of
   WM_KILLFOCUS: Form1.Label1.Caption := Text;     // (未確定中に)フォーカスが他に移った時
   WM_LBUTTONDOWN,
   WM_IME_COMPOSITION:
    if (Msg.LParam and GCS_COMPSTR <> 0)           // 未確定文字列の変更時
     or(Msg.LParam and $ffff0000 <> 0) then begin  // 未確定中にクリックでCARET移動した時
     IMC := ImmGetContext(Handle);
     Len := ImmGetCompositionString(IMC, GCS_COMPSTR, nil, 0);
     SetLength(sCS, Len);
     ImmGetCompositionString(IMC, GCS_COMPSTR, PChar(sCS), Len);
     Len := CharToByteLen(Text, SelStart);
     // CARET位置に未確定文字列を挿入
     Form1.Label1.Caption := Copy(Text, 1, Len) + sCS + Copy(Text, Len+1, 1000);
     ImmReleaseContext(Handle, IMC);
    end else
    if Msg.LParam = 0 then begin   // 未確定中に未確定文字列をすべて消した時
     Form1.Label1.Caption := Text;
    end;
   CN_COMMAND:           // IMEがOFFの状態での入力時
    if TWMCommand(Msg).NotifyCode = EN_UPDATE{EN_CHANGE} then begin
     Form1.Label1.Caption := Text;
    end;
  end;
end;


助監督(仮名)  2010-12-15 10:21:16  No: 39710

丁寧なサンプルが示されたのでもう十分だとは思いますが、当掲示板内の過去ログに
ほぼ同様の相談を見つけました。
自分の勉強にもなったので一応書いておきます。

入力中の文字列の取得 - Delphi Q & A掲示板
https://www.petitmonte.com/bbs/answers?question_id=6149
※なつめぐさん提示のサンプルはメッセージの振り分けが省略されてるので、正しく動作してないようです。動作の確認にはこのようにMemoにLines.Addした方が分かりやすいと思います。

上記トピック内で紹介されてるココ↓に、WM_IME_COMPOSITIONメッセージが解説されてます。

猫でもわかるプログラミング - Windows SDK編 第3部 - 第281章 IMEの操作  その4
http://homepage2.nifty.com/c_lang/sdk3/sdk_281.htm


Mr.XRAY  2010-12-15 15:19:35  No: 39711

こんにちは,Mr.XRAYです.

>そういう現象から想定すると,今回の場合は,Memo1が発生するメッセージを,
>とりあえず,全てLabel1に送ってみてはどうですか.

考えてみたら,無理ですね.
異なるコントロールですから,処理可能なメッセージが違います.
大変失礼しました.


かんとく  2010-12-16 01:48:36  No: 39712

いろいろとご説明、ありがとうございます。

サンプルが難しくて、変数の宣言の部分でエラーが出ますが、じっくり調べて、やってみます。

助監督(仮名)さんのレスのリンクから、(Mr.XRAYさんの書き込みのようですが) 
未定義のエラーが現れた場合,Delphi標準の(他の方が提供しているのではなく)
のものであれば,1つの方法として[Source]フォルダ内を検索する方法があります.

とあったので、MemoWndProcのエラーがなくなるようにやってみましたが、よく分かりませんでした。
未定義のエラーが現れた場合の対処も、もうちょっと詳しく教えてもらえると、ありがたいです。

よろしくお願いします。


これだけ  2010-12-16 02:06:30  No: 39713

uses
  Windows, Messages, SysUtils, Classes, Controls, Forms,
  ...............................
  , Imm;        // ←これを追加

// TForm1のType宣言の前に↓これを追加
type
  TMemo = class(StdCtrls.TMemo)
  private
    procedure WndProc(var Msg: TMessage); override;  // WndProcの宣言
  end;

type
  TForm1 = class(TForm)
  .........


Mr.XRAY  2010-12-16 05:22:37  No: 39714

こんにちは,Mr.XRAYです.
「かんとく」さんは,今までどうやってクラス(TForm1等)の関数,メソッドを作成して
いましたか?

>未定義のエラーが現れた場合の対処も、もうちょっと詳しく教えてもらえると、ありがたいです。

そのスレッドの私の説明が不十分だったようですね.
「未定義」にもいろいろあります.今回は,

procedure TForm1.MemoWndProc(var Message: TMessage);
procedure TMemo.WndProc(var Msg: TMessage);

となっています.上の例で言うと,
TForm1とMemoWndProの間に「ドット」がありますよね.
これは,MemoWndProcがTForm1というクラスのメンバーであるということです
(メソッドですね).また,

label1.caption

これも,label1とcaptionの間にドットがあります.
このcaptionは,label1のメンバーということです(この場合,プロパティですね)
MemoWndProcがTForm1のメソッドであるということは,このメソッドの宣言部が
interface部にあると,なければならないということです.

もちろん,interface部にメソッドの宣言がなければ「未定義のエラー」が発生しますが,
これは,クラス,今の場合は,TForm1というクラスですが,クラスのメソッド,関数を作成
する際の基本です.
覚えておいて損はありません(かな?).

このドットは省略することもできます.例えば,

procedure TForm1.Button1Click(Sender: TObject);
begin
  caption := 'テスト';
end;

とすれば,このcaptionは,TForm1自身のcaptionとなります.

  self.caption;
  
としても同じです,このselfは自分自身,つまり,TForm1のことになります.
くどうようですが,知っていて損はありません.


Mr.XRAY  2010-12-16 05:30:41  No: 39715

解説ついでに,

procedure TMemo.WndProc(var Msg: TMessage);

のコード作成は,Delphi 2007でしたら,以下のようにも作成できます.
このWndProというメソッドは,TMemoに元々あるメンバーメソッド(関数)です.
多くのコントロールに,このWndProcがあります.

http://mrxray.on.coocan.jp/Delphi/Others/A_IDETechnique.htm#07


かんとく  2011-01-25 04:25:05  No: 39716

返事が遅くなってすみません。

教えていただいた内容が難しくて、何から質問したらいいかさえ分からない状態でした。

変数の宣言や、関数・メソッドの作成など、基本的な部分がまだきちんと分かっていないということが分かりました。

教えていただいた内容だけでも、理解できるようにがんばってみますので、もうちょっと時間を下さい。

すみません。


※返信する前に利用規約をご確認ください。








  このエントリーをはてなブックマークに追加