63869


すみません。プログラミング初心者レベルの者です。

MFCでチェックボックスコントロールをダイアログ上に複製する処理を作りたいのですが、  
どうすればそれが作れるでしょうか？

[![画像の説明をここに入力](https://i.stack.imgur.com/FgviV.png)](https://i.stack.imgur.com/FgviV.png)

ボタンコントロールのイベントハンドラを作成後、  
チェックボックスコントロールのコントロール変数を作成して、  
チェックボックスコントロールを複製する関数があるかMSDNを見たりネットで検索しましたが、  
やり方が分かりませんでした。

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


CC BY-SA 4.0
2020-03-14T12:36:37.420
63835
2020-03-16T02:10:33.890
21034
question
mfc
チェックボックスコントロールを複製する方法を教えてください


**純粋に単純なダイアログウィンドウにチェックボックスを追加作成するだけの処理について回答します。**

タブコントロール上の子ウィンドウに表示するにはどうするかとか、それぞれのチェックボックスでチェック状態が変化した時の処理ルーチンとか、チェックボックスの表示/非表示や文言を変更したり、プログラム終了時に廃棄するには？
と言ったことは別途自分で検索するなり考えてみるなりしてください。

`XxxxDlg.h`に以下を追加します。

[code]

    // 作成するCheckBoxのIDの開始番号と開始左端位置
    #define IDBASE_CHECKBOX 2000
    #define LEFTBASE_CHECKBOX 10
    
        // 作成したCheckBoxを記憶しておく配列オブジェクト
        CObArray    m_CheckBoxArray;
        int m_CumulativeCount;  // 作成したCheckBoxの累積数
    
[/code]

`XxxxDlg.cpp`の`追加ボタン`のクリック処理に以下を実装します。  
10個を1列として下に表示して行き、10個毎に列を変えて表示するようになっています。

[code]

    CButton *pCheckBox = new CButton();  // CheckBox用CButtonオブジェクト作成
    int cbaCount = m_CheckBoxArray.GetCount();  // CheckBox用表示領域の計算
    int cbLeft = (10 + ((cbaCount / 10) * 200));// 以下同じ(位置やサイズは適当に調整する)
    int cbTop = (10 + ((cbaCount % 10) * 24));
    int cbRight = cbLeft + 160;
    int cbBottom = cbTop + 20;
    CString cbCaption;  // 以下と併せてCheckBox用表示文言作成
    cbCaption.Format(_T("Check Item No. %d"), (m_CumulativeCount + 1));
    // CheckBox作成
    pCheckBox->Create(cbCaption, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
        CRect(cbLeft, cbTop, cbRight, cbBottom), this, (IDBASE_CHECKBOX + m_CumulativeCount));
    // 作成したCheckBoxを配列に格納
    m_CheckBoxArray.Add(pCheckBox);
    m_CumulativeCount++;  // 作成したCheckBoxの累積数を更新
    
[/code]

* * *

ちなみにチェック状態が変化した時の処理ルーチンをどのように定義して呼び出されるようにするか、についてはこの記事を応用することで出来るでしょう。  
[動的に作成したStatic
Textのクリックイベント](https://rarara.org/community/programming/%E5%8B%95%E7%9A%84%E3%81%AB%E4%BD%9C%E6%88%90%E3%81%97%E3%81%9FStatic%20Text%E3%81%AE%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88/)

`XxxxDlg.h`に以下を追加します。

  * チェックボックスに対して割り付けるIDの最大値
  * `afx_msg void Yyyy(UINT id)`で処理ルーチンの定義

`XxxxDlg.cpp`に以下を追加します。

  * `BEGIN_MESSAGE_MAP`と`END_MESSAGE_MAP`の間に`ON_COMMAND_RANGE(ID最小値, ID最大値, 処理ルーチンアドレス(&クラス名::Yyyy))`を定義
  * `void クラス名::Yyyy(UINT id)`関数を定義し、`id`パラメータでどのチェックボックスが変更されたかを特定し、対応する処理を行う

* * *

他の人の回答だと **「複製」** という言葉がこだわりの心を呼び覚ましてしまったようですが、質問画面上の「To
Do」と「完了」と書いてあるのを考えると、予定や課題の増減/完了をリストにしたいのが目的でしょう。

その場合、単独のチェックボックスを複製する(したように見せかける)のではなく、より相応しいであろう[CCheckListBoxクラス](https://docs.microsoft.com/ja-
jp/cpp/mfc/reference/cchecklistbox-
class?view=vs-2019)に切り替えるのが簡単でリーズナブルだと思われます。変なテクニックを使う必要も無くなりますし。

(太字は引用者)  
[CCheckListBox の使い方](https://mish.myds.me/wordpress/dev/2012/03/14/how-to-use-
cchecklistbox/)

>   1. 通常の手順で，リストボックスをダイアログに貼り付ける．
>   2. コントロールのプロパティを「オーナー描画」－｢固定」，「文字列あり」－「チェック」に変更する．
>   3. クラスウィザードなどを利用して，コントロールの変数（CListBox）を登録する．
>   4. ヘッダファイルを開いて，CListBox を CCheckListBox に変更する．
>   5. **項目の追加は，CListBox などと同様に AddString など．**
>   6. チェックの指定は，SetCheck で．
>

上記記事にあったCListBoxでのAddStringの使用例  
[CListBox::AddString](https://bg1.hatenablog.com/entry/2016/04/09/210000)

直接的な関連は無いですがCCheckListBoxを扱った記事  
[セルフ描画コントロール](http://www2.wbs.ne.jp/~shingo/programming/vc/vc08.htm)  
[Checkboxes in MFC listbox are appearing differently in
VS2015](https://stackoverflow.com/q/43936200/9014308)  
[CCheckListBox items get overlapped on selection if app build using 'Visual
Studio 2017 (v141)' Platform
Toolset](https://stackoverflow.com/q/57951333/9014308)


CC BY-SA 4.0
2020-03-15T13:17:15.463
63869
2020-03-16T02:10:33.890
2020-03-16T02:10:33.890
26370
26370
63835
answer


まず、第一に、実用的なプログラムにおいては、HWNDを持つコントロールを「複製する必要」はほぼありえないと言い切れます。他の仕様を考えるべき段階にあるかもしれません。  
これは、調べてみると

(※)HWNDを持つコントロールの単純な複製方法は存在しない。

ことから、示唆的に知ることができます。

で、今回は、それをむりやり行う方法について、考えてみます。  
一般に、GUIプログラムにおいては

(あ)「UI上での振る舞い」と  
(い)「コード上での仕組み」は

分けて考えなくてはなりません。  
本件の要件は次のように考えられます。

(前提条件)下記はユーザーインターフェース上(ユーザーの見た目)の振る舞いです。  
(A)「追加」ボタンの押下をきっかけに、  
(B)画面上に最初に配置されたチェックボックスが複製され、  
(C)画面上の他の位置(複製を示唆する位置)に表示される。

というわけですね。  
従って、「コード上の仕組み」としては次のような手順が考えられます。

(1)「追加」ボタンのコマンド発生時に、  
(2)新規にチェックボックスをCreate(...)する(仮にNewCheckと命名する)。  
(3)上を「複製されたように見せかける」ため、最初のチェックボックスのウィンドウキャプションをコピーしてNewCheckに設定する。  
(3)同様に「複製されたように見せかける」ため、合理的な表示位置を算定する。(最初のチェックボックスの位置を参考に、縦又は横に配置する)

というような感じではないでしょうか。


CC BY-SA 4.0
2020-03-16T01:29:28.663
63873
2020-03-16T01:29:28.663
3793
63835
answer


何が何でも動的に生成せざるを得ないというなら別ですが、オイラなら

  * 事前に最大数を作っておいて
  * 起動時に `CWnd::ShowWindow()` で隠して
  * ボタンを押したら見えるようにする

だけで実装します。 ClassWizard が自動でやってくれることをわざわざ手書きするなんてありえない。


CC BY-SA 4.0
2020-03-16T01:30:26.497
63874
2020-03-16T01:30:26.497
8589
63835
answer