64027


MFCでTo Doリストアプリを作っていますが、To
Doリストタブの子ダイアログ上にあるエディットコントロールで文字入力した後Enterキーを押下するとタブコントロール上の子ダイアログが再描画されない問題で困っています。

参考gif：  
[![画像の説明をここに入力](https://i.stack.imgur.com/NZxMT.gif)](https://i.stack.imgur.com/NZxMT.gif)

実現したい処理は、To
Doリストタブの子ダイアログ上にあるエディットコントロールで文字入力した後Enterキーを押下すると、チェックボックスコントロールの表示文字列がエディットコントロールで文字入力した文字列に変更されることです。  
※この処理は、上記の子ダイアログが再描画されない問題以外は作成できています。  
処理作成のために、私が作ったソースは以下です。  
ToDoDlg.h(子ダイアログのクラスヘッダー)

[code]

    private:
        /*privateメンバ変数*/
        // To Do項目チェックボックスコントロール変数
        CButton m_check_item;
        // To Do項目表示文字列入力変数
        CString m_add_edit;
    
        /*privateメンバ関数*/
        afx_msg void OnEnKillfocusCaptionEdit();
    };
    
    
[/code]

ToDoDlg.cpp(子ダイアログのクラス実装部分)

[code]

    // ToDoDlg メッセージ ハンドラー
    
    
    void ToDoDlg::OnEnKillfocusCaptionEdit()
    {
        // TODO: ここにコントロール通知ハンドラー コードを追加します。
        /*エディットコントロールから入力文字を取得し、
        チェックボックスコントロールの表示文字に設定する*/
    
        // エディットコントロールの値変数にエディットコントロールで
        // 入力された文字列を代入する
        UpdateData(TRUE);
        CString edit_CString = m_add_edit;
    
        // 取得した文字列をチェックボックスコントロールに設定する
        m_check_item.SetWindowTextW(edit_CString);
    
        /*チェックボックスコントロールを表示して
        追加ボタンがまた押されたら、複製できるようにする*/
        //CButton check_item1 = m_check_item;
        //check_item1.Create(IDC_CHECK_ITEM, this->classToDoDlg);
    }
    
    
[/code]

備考：完了タブに切り替えて、再びTo Doタブに切り替えた際は期待通り描画されています。

この問題を解決するために以下の仮説を立てて解決できるかどうか試してみました。

仮説：再描画を手動で行う関数を使えば解決できる。  
再描画の機能を持つと思われる関数：  
・RedrawWindow関数  
・InvalidateRect関数 //領域無効化  
UpdateWindow関数 //再描画命令

問題：RedrawWindow関数を使うにしても、InvalidateRect関数を使うにしても、引数のlpcrectに何を設定してよいかわからない。  
この問題を解決するために、試しにlpcrectにToDoDlgのウィンドウサイズをポインタで取得できる関数があるか探してみましたが、見つかりませんでした。

解決策を教えて頂けるとありがたいです。どうかよろしくお願い致します。


CC BY-SA 4.0
2020-03-22T09:42:25.733
64025
2020-03-23T04:30:21.610
21034
question
mfc
エディットコントロールで文字入力した後Enterキーを押下するとタブコントロール上の子ダイアログが再描画されない


大元の問題である **「Enterキーを押下するとタブコントロール上の子ダイアログが再描画されない」** 件に関しては以下が答えになります。

**直接的な答え：**

該当の`ToDoDlg`の`IDOK`の`COMMAND`メッセージハンドラーが定義されていないためです。  
クラスウィザードを使ってそのハンドラーを追加すれば、中の処理は何も実装しなくてもクリア状態は発生しなくなります。

ただし、 **Escキー押下でも同様のクリア状態になり、こちらは対応する`IDNO`のメッセージハンドラーを定義しても解消されません。**

参考：  
[ダイアログボックスのキーボードショートカット](http://www.seosoft.net/windows/hint/keyboard/dialog.html)

**Escキー押下でも使える答え：**

Enter/Esc各キー押下時のWindowメッセージの発生状況(時系列順)が以下になり、子ダイアログの`WM_WINDOWPOSCHANGING`あたりでクリア状態が発生しているようです。

Enterキー押下時

[code]

    子ダイアログ
      0x400  DDM_SETFMT
      0x111  WM_COMMAND = IDOK
      0x046  WM_WINDOWPOSCHANGING
      0x047  WM_WINDOWPOSCHANGED
      0x000  WM_NULL
      0x000  WM_NULL
    親ダイアログ
      0x014  WM_ERASEBKGND
      0x136  WM_CTLCOLORDLG
      0x019  WM_CTLCOLOR
      0x318  WM_PRINTCLIENT
      0x36A  WM_KICKIDLE
    
[/code]

Escキー押下時

[code]

    子ダイアログ
      0x111  WM_COMMAND = IDNO
      0x046  WM_WINDOWPOSCHANGING
      0x047  WM_WINDOWPOSCHANGED
      0x000  WM_NULL
      0x000  WM_NULL
    親ダイアログ
      0x014  WM_ERASEBKGND
      0x136  WM_CTLCOLORDLG
      0x318  WM_PRINTCLIENT
      0x36A  WM_KICKIDLE
    
[/code]

**最適な方法かどうかは不明ですが、**
親ダイアログの`WM_PRINTCLIENT`のあたりで`OnTcnSelchangeTab()`と同様の処理を行うと、Enter/Escどちらのキー押下でもクリア状態が発生しなくなります。  
子ダイアログの`WM_WINDOWPOSCHANGED`以後でも良いのですが、全部の子ダイアログに必要とか、現在アクティブな子ダイアログを判定しなければいけないとか、煩雑になってしまうので。

実装するには、親ダイアログを対象としたクラスウィザードのメッセージタブで、「カスタムメッセージの追加」をクリックし、「カスタムWindowsメッセージ」の欄に「WM_PRINTCLIENT」を入力して「OK」すればソースに追加されるので、その中に`OnTcnSelchangeTab()`の表示処理をコピーすれば出来ます。

例えばタブが2つしかないなら、以下のような簡単な処理で大丈夫です。

[code]

    if (m_ctrlTab.GetCurSel() == 0)
    {
        m_pDlg1->ShowWindow(SW_SHOW);
    }
    else
    {
        m_pDlg2->ShowWindow(SW_SHOW);
    }
    
[/code]

参考：  
[第３４８章 ウィンドウアニメーション](http://www.kumei.ne.jp/c_lang/sdk4/sdk_348.htm)
この中に`WM_PRINTCLIENT`に関連した説明有り  
[WM_PRINTCLIENT message](https://docs.microsoft.com/en-
us/windows/win32/gdi/wm-printclient)

* * *

上記で解決するのであまり意味はありませんが、
**「問題：RedrawWindow関数を使うにしても、InvalidateRect関数を使うにしても、引数のlpcrectに何を設定してよいかわからない。」**
に対する答えは以下になります。

特定の領域だけ描画更新したいというので無ければ、いずれも`NULL`を指定しておけば良いのでは？
そうすれば、そのウィンドウのクライアント領域全体が対象となるはずです。

(太字は引用者)  
[CWnd::RedrawWindow](https://docs.microsoft.com/ja-jp/cpp/mfc/reference/cwnd-
class?view=vs-2019#redrawwindow)

> 指定したウィンドウのクライアント領域で、指定した四角形または領域を更新します。
>
> パラメーター  
>  lpRectUpdate  
>  更新用の四角形の座標を格納している[RECT 構造体](https://docs.microsoft.com/ja-
> jp/windows/win32/api/windef/ns-windef-rect)を指します。
> Prgnupdateに有効な地域ハンドルが含まれている場合、このパラメーターは無視されます。  
>  prgnUpdate  
>  更新リージョンを識別します。 **PrgnupdateとlpRectUpdateの両方が NULL
> の場合、クライアント領域全体が更新領域に追加されます。**

[CWnd::InvalidateRect](https://docs.microsoft.com/ja-
jp/cpp/mfc/reference/cwnd-class?view=vs-2019#invalidaterect)

> `CWnd`更新領域に四角形を追加することによって、指定された四角形内のクライアント領域を無効にします。
>
> パラメーター  
>  lpRect  
>  更新領域に追加する四角形 (クライアント座標) を格納する`CRect`オブジェクトまたは[RECT
> 構造体](https://docs.microsoft.com/ja-jp/windows/win32/api/windef/ns-windef-
> rect)を指します。 **LpRectが NULL の場合、クライアント領域全体が領域に追加されます。**


CC BY-SA 4.0
2020-03-22T12:25:15.750
64027
2020-03-23T04:30:21.610
2020-06-17T08:14:45.997
-1
26370
64025
answer