またお世話になります。たいちうです。
FAQだと思っていたのですが調べられなかったのでお願いします。
MFCで入力用のダイアログを作っています。
複数あるCEditで、OnKillFocusでそれぞれデータをチェックして、
入力エラーの場合はフォーカスを移さないようにしているのですが、
キャンセルボタンを押したときはそのままダイアログを閉じたいのです。
現在はOnCancelが呼ばれる前に、エラーチェックに引っかかり、
キャンセルできません。対処法ご存知の方、よろしくお願いします。
【環境】Windows2000, VC6.0+SP5, MFC, SDIから呼ぶモードレスダイアログ
OnKillFocus()の引数では判定できないでしょうか。
dairygoodsさま
早速の回答ありがとうございます。
そしてごめんなさい。
ON_EN_KILLFOCUS でマッピングしたOnKillFocuxHogeのことで、
CWnd::OnKillFocusではありません。
変な書き方をして申し訳ありませんでした。
OnKillFocuxHogeには引数はありません。
とりあえず、次のような感じでどこにフォーカスが行ったか判別できました。
void CHemoDlg::OnKillFocusHoge()
{
if (GetFocus()->GetDlgCtrlID() == IDCANCEL) {
} else {
}
}
しかし、フォーカスがどこに行ったかで判断すると、
「キャンセル」ボタンを押しっぱなしにし、
ドラッグしてボタンのクリックをキャンセルする。
で、改めて「OK」を押す。
という抜け道ができてしまいます。
この問題は、KillFocusのタイミングで判定する限り、
回避不可能です。
dairygoodsさんも言われていますが、KillFocusを使うのはいい方法ではないです。
エディットコントロールならキーが入力されるたびとか、内容の変更が行われたとか
そういったタイミングで処理が出来たと思います。
ただ、私が思うに入力するたびにチェックするというのは実際にはダイアログの
インターフェースに馴染まないような気がします。入力するたびのチェックで出来るの
は、
せいぜい字種チェックぐらいでしょう。
最終的に入力が終わった後のチェックはOKボタン押下時に行い、
エラー項目にフォーカスを設定してエラーメッセージを出すというのが
インターフェイス的にはあっていると思います。
複数箇所ある場合は、直してOKを押すたびにフォーカスが別のところに移って
エラーのメッセージが出るといった流れになります。
インターフェイス的に馴染まない処理を行っているので
今回のような不整合がおこるのではないでしょうか。
ひとつの方法として・・・
まずCEditとCButtonのコントロールをサブクラス化します。
サブクラス化したクラスのWM_SETFOCUSをインプリメントします。
OnSetFocus(CWnd* pOldWnd) の関数ができるので、
以下のコードでダイアログにメッセージを送信
チェック対象IDはpOldWndがあるので
chkID=::GetDlgCtrlID(pOldWnd->m_hWnd);で取り、
新しいフォーカスIDは
newID=::GetDlgCtrlID(this->m_hWnd);で取ります。
fl=::SendMessage(this->GetParentOwner()->m_hWnd,OR_CHECKUP,chkID,newID);
これでダイアログにWM_CHECKUP(#define OR_CHECKUP WM_USER+2等して登録しておいて)
メッセージが送信されてくるので、
ダイアログCPPファイルのメッセージマップに
ON_MESSAGE(OR_CHECKUP, OnCheckUp) を追加
ダイアログヘッダーのメッセージマップに
afx_msg LRESULT OnCheckUp( WPARAM wParam, LPARAM lParam ); を追加
実装部で
LRESULT CNewCtrl2Dlg::OnCheckUp( WPARAM wParam, LPARAM lParam )
{
if(lPalam==IDC_STOP) return 0L; //フォーカス先が中止ボタンの時は何もチェックしな
い。
if(wParam==IDC_EDIT1){
if(チェック項目){
GetDlgItem(IDC_EDIT1)->SetFocus();
return true; //チェックに引っかかったらTRUEをかえす。
}
return 0L;
}
実装部間違えました。
実装部で
LRESULT CNewCtrl2Dlg::OnCheckUp( WPARAM wParam, LPARAM lParam )
{
if(lPalam==IDC_STOP) return 0L; //フォーカス先が中止ボタンの時は何もチェッ
クしな
い。
if(wParam==IDC_EDIT1){
if(チェック項目){
GetDlgItem(IDC_EDIT1)->SetFocus();
return true; //チェックに引っかかったらTRUEをかえす。
}
}
return 0L;
}
これの欠点:サブクラス化してないコントロールがあると、イベントがこない。
利点: チェック関数の中で、ループ掛けて項目をチェックすることも可能。
んー自分の文章見て分かりずらい・・・^^;
要約すると、フォーカスを受け取ったコントロールで、
前のフォーカスのチェックするための、メッセージをダイアログに送る
ということです。
尚、複数項目を一度にチェックするループですが、
コントロールのタブオーダー順にIDを整列させておく必要があります。
あぁ、また間違えた・・・
これでダイアログにWM_CHECKUP(#define OR_CHECKUP WM_USER+2等して登録しておいて)
メッセージが送信されてくるので、
これでダイアログにOR_CHECKUPです。
レス大変送れて申し訳ありません。
> dairygoodsさま、PATIOさま
GetFocus()->GetDlgCtrlID()が求めていたものでした。
OnOK()でも最終的なチェックをしているので、抜け道については心配ありません。
今回の設計はインターフェース的に馴染まないのですかね。
最終チェックはOnOKですが、キーとなるいくつかのエディットコントロール
については、フォーカスを失ったときにチェックして以降の無駄な入力をさせ
ないようにする、という意図なんですが。よく考えてみます。
> ATISさま
丁寧な説明有難うございます。
ただ、ちょっとイレギュラーな処理をしたかっただけのようですので、
そのために全てのコントロールに手を加えるのはどうかと思っています。
サンプルは後ほどゆっくりと勉強させてもらいます。
皆様ご回答有難うございました。