トップ回答者
CheckedListBoxの複数選択

質問
回答
-
チェックボックスのON/OFFと文字列反転を同期させたいということですかね
以下のようにしてみてはどうでしょう
(ダイアログ形式でプロジェクト作成してしまったのでOnInitDialogは適当な初期化メソッドに置き換えてください)// 初期化処理 BOOL CTestDlg::OnInitDialog() { CDialog::OnInitDialog(); // 省略 m_listBox.SetCheckStyle( BS_3STATE ); m_listBox.AddString("ONE"); m_listBox.AddString("TWO"); m_listBox.AddString("THREE"); m_listBox.AddString("FOUR"); m_listBox.AddString("FIVE"); return TRUE; } // 選択変更イベント BOOL CTestDlg::OnLbnSelchangeList() { int selectedIndex = m_listBox.GetCurSel(); int checkState = m_listBox.GetCheck(selectedIndex); // 反転 m_listBox.SetCheck(selectedIndex, !checkState); }
-
既に回答済みとなっておりますが、気になったことがあったので投稿します。
CCheckListBox でチェック状態と選択状態の同期を実装すると、リスト項目の文字の部分をクリックする分には問題がないのですが、左側のチェックボックスの四角部分をクリックすると、リストボックスで選択状態のすべての項目についてチェック状態が変更されてしまうので同期が合わなくなってしまうと思います。
この問題を解決するには、CCheckListBox の派生クラスを作って、メッセージハンドラで既定の動きから処理を変えてやれば実装できるとは思いますが、コントロールの派生クラスを作らないで済む CTreeCtrl を使う方法がありましたので紹介します。
以下 CTreeCtrl での実装手順になります。
1.ダイアログにTree Controlを配置
2.配置したTree Controlを右クリックして「変数の追加」から変数の型を"CTreeCtrl"、変数名を"m_TreeCtrl"として変数を追加
3.また同様に、Tree Controlを右クリックして「イベントハンドラーの追加」から、TVN_SELCHANGINGとTVN_CLICKのイベントハンドラーを追加する
4.OnInitDialogに下記のコードを追加
// ツリービューの設定を変更 SetClassLong(m_TreeCtrl.GetSafeHwnd(), GCL_STYLE, GetClassLong(m_TreeCtrl.GetSafeHwnd(), GCL_STYLE) & ~CS_DBLCLKS); // ダブルクリックイベントは不必要 m_TreeCtrl.ModifyStyle(0, TVS_CHECKBOXES); // チェックボックス // ツリービューにアイテムを追加 m_TreeCtrl.InsertItem(TEXT("aaa"), 0, 0); m_TreeCtrl.InsertItem(TEXT("bbb"), 0, 0); m_TreeCtrl.InsertItem(TEXT("ccc"), 0, 0); // いくつかの項目を選択状態に設定 SetTreeItemCheck(1, TRUE); SetTreeItemCheck(3, TRUE); SetTreeItemCheck(4, TRUE);
5.追加されたイベントハンドラ OnTvnSelchangingTree1 は下記のように記述
void CMFCApplication1Dlg::OnTvnSelchangingTree1(NMHDR *pNMHDR, LRESULT *pResult) { *pResult = 1; }
6.追加されたイベントハンドラ OnNMClickTree1 は下記のように記述
void CMFCApplication1Dlg::OnNMClickTree1(NMHDR *pNMHDR, LRESULT *pResult) { POINT pt = { 0 }; GetCursorPos(&pt); m_TreeCtrl.ScreenToClient(&pt); TVHITTESTINFO hti = { 0 }; hti.pt.x = pt.x; hti.pt.y = pt.y; if (m_TreeCtrl.HitTest(&hti) && (hti.flags & TVHT_ONITEMSTATEICON || hti.flags & TVHT_ONITEMLABEL || hti.flags & TVHT_ONITEMRIGHT)) { if (m_TreeCtrl.GetCheck(hti.hItem)) { m_TreeCtrl.SetCheck(hti.hItem, 0); m_TreeCtrl.SetItemState(hti.hItem, 0, TVIS_SELECTED); } else { m_TreeCtrl.SetCheck(hti.hItem, 1); m_TreeCtrl.SetItemState(hti.hItem, TVIS_SELECTED, TVIS_SELECTED); } *pResult = 1; } }
7.また、TreeCtrlの項目に対してチェック状態の取得・設定を行うために下記のような関数をクラスに追加します
BOOL CMFCApplication1Dlg::GetTreeItemCheck(int nIndex) const { HTREEITEM hItem = m_TreeCtrl.GetRootItem(); for (int i = 0; i< nIndex; ++i) hItem = m_TreeCtrl.GetNextSiblingItem(hItem); return m_TreeCtrl.GetCheck(hItem); } void CMFCApplication1Dlg::SetTreeItemCheck(int nIndex, BOOL nCheck) { HTREEITEM hItem = m_TreeCtrl.GetRootItem(); for (int i = 0; i< nIndex; ++i) hItem = m_TreeCtrl.GetNextSiblingItem(hItem); m_TreeCtrl.SetCheck(hItem, nCheck); m_TreeCtrl.SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED); }
以上です。参考になりましたら幸いです。
- 回答としてマーク 村尾DOS 2016年4月5日 1:06
すべての返信
-
CheckedListBoxには3つの種類の「選んでいるかどうか」を表す状態があり、それぞれ独立しています。以下の3つです。
- フォーカス(アイテムの周囲に点線の枠を表示するなどして表現するもの。1つのコントロール内でfocusedなのは最大でも1つ)
- 選択(アイテムの背景を青にするなどして表現するもの。設定によって単一または複数選択可能)
- チェック(チェックボックスのチェック状態で表現するもの)
で、このうち「選択」を複数にしたい、ということでいいですか?
であれば、該当のコントロール作成時に、スタイルにLBS_MULTIPLESELを追加すればいいです。
-
.NET Framework の CheckedListBox でしょうか?
もしそうでしたら、
https://msdn.microsoft.com/ja-jp/library/system.windows.forms.checkedlistbox.selectionmode(v=vs.110).aspx
上記のページに「CheckedListBoxのオブジェクトは、複数選択がサポートされていません。」と記述されておりました。
ご確認ください。 -
-
チェックボックスのON/OFFと文字列反転を同期させたいということですかね
以下のようにしてみてはどうでしょう
(ダイアログ形式でプロジェクト作成してしまったのでOnInitDialogは適当な初期化メソッドに置き換えてください)// 初期化処理 BOOL CTestDlg::OnInitDialog() { CDialog::OnInitDialog(); // 省略 m_listBox.SetCheckStyle( BS_3STATE ); m_listBox.AddString("ONE"); m_listBox.AddString("TWO"); m_listBox.AddString("THREE"); m_listBox.AddString("FOUR"); m_listBox.AddString("FIVE"); return TRUE; } // 選択変更イベント BOOL CTestDlg::OnLbnSelchangeList() { int selectedIndex = m_listBox.GetCurSel(); int checkState = m_listBox.GetCheck(selectedIndex); // 反転 m_listBox.SetCheck(selectedIndex, !checkState); }
-
既に回答済みとなっておりますが、気になったことがあったので投稿します。
CCheckListBox でチェック状態と選択状態の同期を実装すると、リスト項目の文字の部分をクリックする分には問題がないのですが、左側のチェックボックスの四角部分をクリックすると、リストボックスで選択状態のすべての項目についてチェック状態が変更されてしまうので同期が合わなくなってしまうと思います。
この問題を解決するには、CCheckListBox の派生クラスを作って、メッセージハンドラで既定の動きから処理を変えてやれば実装できるとは思いますが、コントロールの派生クラスを作らないで済む CTreeCtrl を使う方法がありましたので紹介します。
以下 CTreeCtrl での実装手順になります。
1.ダイアログにTree Controlを配置
2.配置したTree Controlを右クリックして「変数の追加」から変数の型を"CTreeCtrl"、変数名を"m_TreeCtrl"として変数を追加
3.また同様に、Tree Controlを右クリックして「イベントハンドラーの追加」から、TVN_SELCHANGINGとTVN_CLICKのイベントハンドラーを追加する
4.OnInitDialogに下記のコードを追加
// ツリービューの設定を変更 SetClassLong(m_TreeCtrl.GetSafeHwnd(), GCL_STYLE, GetClassLong(m_TreeCtrl.GetSafeHwnd(), GCL_STYLE) & ~CS_DBLCLKS); // ダブルクリックイベントは不必要 m_TreeCtrl.ModifyStyle(0, TVS_CHECKBOXES); // チェックボックス // ツリービューにアイテムを追加 m_TreeCtrl.InsertItem(TEXT("aaa"), 0, 0); m_TreeCtrl.InsertItem(TEXT("bbb"), 0, 0); m_TreeCtrl.InsertItem(TEXT("ccc"), 0, 0); // いくつかの項目を選択状態に設定 SetTreeItemCheck(1, TRUE); SetTreeItemCheck(3, TRUE); SetTreeItemCheck(4, TRUE);
5.追加されたイベントハンドラ OnTvnSelchangingTree1 は下記のように記述
void CMFCApplication1Dlg::OnTvnSelchangingTree1(NMHDR *pNMHDR, LRESULT *pResult) { *pResult = 1; }
6.追加されたイベントハンドラ OnNMClickTree1 は下記のように記述
void CMFCApplication1Dlg::OnNMClickTree1(NMHDR *pNMHDR, LRESULT *pResult) { POINT pt = { 0 }; GetCursorPos(&pt); m_TreeCtrl.ScreenToClient(&pt); TVHITTESTINFO hti = { 0 }; hti.pt.x = pt.x; hti.pt.y = pt.y; if (m_TreeCtrl.HitTest(&hti) && (hti.flags & TVHT_ONITEMSTATEICON || hti.flags & TVHT_ONITEMLABEL || hti.flags & TVHT_ONITEMRIGHT)) { if (m_TreeCtrl.GetCheck(hti.hItem)) { m_TreeCtrl.SetCheck(hti.hItem, 0); m_TreeCtrl.SetItemState(hti.hItem, 0, TVIS_SELECTED); } else { m_TreeCtrl.SetCheck(hti.hItem, 1); m_TreeCtrl.SetItemState(hti.hItem, TVIS_SELECTED, TVIS_SELECTED); } *pResult = 1; } }
7.また、TreeCtrlの項目に対してチェック状態の取得・設定を行うために下記のような関数をクラスに追加します
BOOL CMFCApplication1Dlg::GetTreeItemCheck(int nIndex) const { HTREEITEM hItem = m_TreeCtrl.GetRootItem(); for (int i = 0; i< nIndex; ++i) hItem = m_TreeCtrl.GetNextSiblingItem(hItem); return m_TreeCtrl.GetCheck(hItem); } void CMFCApplication1Dlg::SetTreeItemCheck(int nIndex, BOOL nCheck) { HTREEITEM hItem = m_TreeCtrl.GetRootItem(); for (int i = 0; i< nIndex; ++i) hItem = m_TreeCtrl.GetNextSiblingItem(hItem); m_TreeCtrl.SetCheck(hItem, nCheck); m_TreeCtrl.SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED); }
以上です。参考になりましたら幸いです。
- 回答としてマーク 村尾DOS 2016年4月5日 1:06
-
-
HWND CDataExchange::PrepareCtrl(int nIDC) { ASSERT(nIDC != 0); ASSERT(nIDC != -1); // not allowed HWND hWndCtrl; COleControlSite* pSite = NULL; m_pDlgWnd->GetDlgItem(nIDC, &hWndCtrl); if (hWndCtrl == NULL) { // Could be a windowless OCX pSite = m_pDlgWnd->GetOleControlSite(nIDC); if (pSite == NULL) { TRACE(traceAppMsg, 0, "Error: no data exchange control with ID 0x%04X.\n", nIDC); ASSERT(FALSE); ← ここで止まっています。 AfxThrowNotSupportedException(); } } m_idLastControl = nIDC; m_bEditLastControl = FALSE; // not an edit item by default return hWndCtrl; }
すみません。dlgdata.cppで止まっていました。 -
うーん。すみませんが、ちょっと原因が分からないです・・・(CDialog::OnInitDialog()を呼ぶ前にm_TreeCtrlにアクセスしている?)
こちらで作成した VS2012 のプロジェクトをアップロードしてみますので、
お手数をおかけしますが、プログラムを比較していただけますでしょうか?http://work.vc/CheckListBoxByTreeCtrl.zip
よろしくお願いいたします。