質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.39%
MFC

MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

433閲覧

MFC リストボックスの選択を変更したら、親でキャッチしたい

Kosho9513

総合スコア6

MFC

MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2024/01/25 00:34

実現したいこと

マウスホイール操作で選択を変更できるリストボックスをCListBoxから派生させて作成しました。さらに、マウスホイール操作によって選択が変更されたということを、親のダイアログでキャッチしたいです。

発生している問題・分からないこと

マウスホイール操作があったら、リストボックスでSetCurSelを行って親ダイアログでON_WM_LBN_SELCHANGEでキャッチしようとしましたが、WM_LBN_SELCHANGEにが飛んでこないようです。なお、ウィンドウスタイルにLBS_NOTIFYは指定しています。

該当のソースコード

C++

1BOOL MyListBox::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) 2{ 3 int select{ 0 }; //仮 ここで選択位置を計算 4// 試したことその① 5 SetCurSel(select); 6// 試したことその② 7 GetParent()->SendMessage(LBN_SELCHANGE, GetDlgCtrlID(), reinterpret_cast<LPARAM>(m_hWnd)); 8// 試したことその③ 9 SendMessage(LB_SETCURSEL, select, 0); 10 return TRUE; 11}

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

Microsoftのドキュメントには、「CListBox::SetCurSelメンバー関数によって選択が変更された場合、この通知(ON_LBN_SELCHANGE)は送信されません。」と書いてありました。他のサイトではLBN_SELCHANGEを親にSendMessageしてもダメで、この通知はユーザーが選択を変更したときに通知されるものだと書いてありました。
ソースコードに記載しましたが、SetCurSelの代わりにLB_SETCURSELをSendMessageしてもやはりだめでした。

補足

特になし

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2024/01/25 00:53

コントロールの通知の仕組みを少しは自分で調べましょう。
Kosho9513

2024/01/25 00:58

コメントありがとうございます。 調べましたが分からなかったので質問させていただいています。 「少しは」ということは、調べる方向が間違っていなければ、この質問に対しての答えは簡単に見つかるということでしょうか?
退会済みユーザー

退会済みユーザー

2024/01/25 01:01

前の質問の解決が50分前で、今の質問してるので。少しは=数日くらいは本気で調べてください。
Kosho9513

2024/01/25 01:05

分かりました。 私も継続して調べます。 ありがとうございます。
fana

2024/01/25 01:30 編集

> マウスホイール操作によって選択が変更されたということ を伝達する手段というのは,「メッセージ」な形じゃないとダメなのでしょうか? 特に問題がないならば適当にコールバックを指定しておくみたいな方法等でも事足りるのではないか,という気がしますが,どうなのでしょう? また,メッセージを用いるにしても,既存と被らないプライベートなメッセージ(WM_APP + x みたいなやつ)を用いるのが良いのではないかと思ったりします.
Kosho9513

2024/01/25 01:48

コメントありがとうございます。 もちろん、メッセージでなくても大丈夫です。なるべく本来の構造に近づけたい気持ちもあってメッセージと書きました。 いただいたアドバイスを参考に、いろんな手を考えてみます。
fana

2024/01/25 02:26 編集

前の質問でも述べましたが,SendMessage を用いるなら(大抵の場合)それはその場で相手側のウィンドウプロシージャを直接呼び出すということになるハズです.そのような動作(処理タイミングとか?)で良いのあれば,ごく普通のコールバックな方法でも良く,むしろ,通知相手をウィンドウに限定しなくても良いとか,コールバックの引数や戻り値を都合よく決められる等の点で利便性が増すのではないかと思います. 通知先の処理を MyListBox::OnMouseWheel() 内から呼び出して実行するのではなくて,「とりあえず通知はメッセージキューに突っ込み,MyListBox::OnMouseWheel()からは抜けたい」ような場合には,SendMessage ではなく PostMessage を用いることになるでしょう.
Kosho9513

2024/01/25 02:42

ありがとうございます。 実装する上で工数が少ないと思われるコールバックの方で進めました。SendMessageで相手のウィンドウプロシージャを呼ぶのとコールバックの方法を取るのでは基本的な構造は変わらないということで納得しました。
fana

2024/01/25 02:56

まぁ,「実際にどの方法を採用するか」と,「今起きている事柄について突き詰めること」は別の話なので, 前者に別の話を選択したとしても,後者の方をないがしろにはしない方が良さそうかな,とは思います. この辺の話はある程度把握しておかないとMFCを使い続けてるとすぐにまた支障が出るような気がしますし.
Kosho9513

2024/01/25 03:03

今は仕事が立て込んでいてできないのですが、プログラムで選択を変更したときになぜLBN_SELCHANGEが飛ばないのか、などの疑問はいつか解決します。 ありがとうございました。
退会済みユーザー

退会済みユーザー

2024/01/25 03:29 編集

単純に仕事のやり方に問題があるだけですよ。 特に仕事なら他人に聞く前に十分調査し、分からない部分を整理した上で現象を明確に再現可能な最小限のコードを提示した上で、ピンポイントで聞きましょう。 工数不足を解消するために他人を使うのであれば、報酬が必要だと思いますよ。 現状MFCを使うようなアプリはほぼ全て不良資産だと思うので、若い人が無理矢理それにあてがわれてるなら助けるのもやぶさかではありませんが、ロクに調べもしないで工数削減のために無料で他人を利用するような形が混ざっていると、もうあまり手を貸したくないですね。
退会済みユーザー

退会済みユーザー

2024/01/25 04:30

一応このまま終わりかねないので、質問者さん以外の疑問を持つ人のために書いておくと、親子間の通知は特に親でも子でもハンドラを持てる構造にしてしまうと、1対多のときにひどい輻輳が起きたり、無限ループしたりする可能性があるので、恐らくSetCurSelで勝手に通知しないのかと思います。 通知の対応手段はいくつかあると思いますが、そもそもリストボックスを何のためにそうするのか?とか、ダイアログ自体の設計についても、合わせて考えるべき課題です。本来ホイールの上下でリストボックスの選択が変わるなどというコントロールの基本操作を根底から覆すような要件を、石化した上に苔が生えて風化してボロボロなMFCでやろうとするのが無謀だと思うので(不良度合いが著しく上がる)。元来MFCではコントロール自身のサブクラス化とか特殊な要件でもないとしないし、製品に使うとか、今後も使うとか、汎用な用途を考えているなら相当入念なテストが必要です(MFCは少しずつ進化したとはいえ、16bit Windows時代から存在する遺物なので、難しい使い方があまり想定されていません)。 が、一般的な親ダイアログ上の昔ながらのハンドラを使って操作するなら、最も原始的な手段だとListBoxコントロール側からダイアログにWM_COMMAND(LBN_SELCHANGE)をSendMessageすれば事足りるはずです。それ以外の方法は自分で調べてください。本来はそれらを全て吟味した上で、MFC以外のフレームや、(今あるのか知りませんが)MFCと親和性の高い市販のコントロールと比較するような話です。私ならホイールで選択の時点でちゃんと意識合わせしておきますし、間違っても他人に聞こうとは思いませんが…。 MFCは基本的に密な連携に勝手になるので、直にコールバックでも大きな問題はないと思いますが、気づいた限りの懸念点は2つです。 ・入り口が複数になるので不具合の元になるかもしれない ・C++標準ライブラリと併用する場合、MFCと役割の被るクラスも多いため注意
fana

2024/01/25 09:33 編集

> ……ような場合には,SendMessage ではなく PostMessage を用いることになるでしょう. と書きましたが,これは一般論(?)というかそんな話です. …というのを念のため述べておきます. というのは…… 本件は前の質問の延長線上にあるものと推測されるわけですが,前の質問で示されていたプログラムではメッセージ処理に嫌らしく介入している部分(特定のメッセージを独自処理してしまうAPPクラスの PreTranslateMessage )が既に存在している様子なので,ウィンドウメッセージを用いた処理はまともな(というか,一般的に期待するような)動作ができない可能性もあるんじゃないかな,と. > なるべく本来の構造に近づけたい気持ちもあってメッセージと書きました。 と述べておられましたが,前の質問のプログラムに対しての作業を実施している状況なのであれば,メッセージ処理を普通に用いること自体が既に 無理/困難/etc かもしれないという点も頭に入れて対応方法を考えていく必要があるのかもしれません.
退会済みユーザー

退会済みユーザー

2024/01/25 12:08 編集

PreTranslateMessageは主にWM_CLOSEなどで、ウィンドウを閉じる前の「保存しますか?」などの処理をするための機能だったと記憶しています。そういう意味ではメインウィンドウが非表示だったり存在しないタイミングがある場合にアプリで拾いたいことはあるんじゃないでしょうか? (編集: 後で調べたところ全く違ってました。WM_CLOSEのハンドラで処理されてました。) PostMessageを使用するのは、WM_PAINTのような後回しにしてもいいような(割と時間のかかる)イベント、その場で処理しなくても順序が狂わないイベントのとき、スレッドが違うときなどに使えばいいだけです。むしろ元のコードは本来の構造で、質問者さんの加えている変更が異質なのだと思いますよ。MFC自体は異質なコードに対して堅牢ではない気がする(むしろ継承するだけで予想外の問題が起きる)ので、本来の構造に近づける方が安全です。良く分かっていて十分以上にテストする工数があるなら、異質なやり方もありという程度かと…。個人的にはそうであってもその価値があるかどうかが疑わしいと思いますが。よほどMFCから離れられない理由があるのなら、逆に覚悟を決め、がっぷりよつに構えてより使いやすくアレンジしていくのもありかというくらいかと。
fana

2024/01/25 12:14

前の質問では, 「マウスホイールのメッセージを捕まえてそこで SendMessage し,return TRUE」ということをしている PreTranslateMessage がAPPクラスに既に存在していて,そこの部分の実装はもういじれない みたいな話(それ故にダイアログ側の PreTranslateMessageでは処理したいメッセージが捕まえられない,という問題が生じていて,うまく対処できない)と見えたので,もしもマウスホイールのメッセージ以外にも他のメッセージに関しても何か同じようなことがなされているとしたら,またいろいろと困ることも起こるかもしれないかな,と. まぁ現物を知らない他人が過度にどうこう言うことでもないかもしれません.
退会済みユーザー

退会済みユーザー

2024/01/25 19:02

あの部分の処理自体は質問者さんが消えたメッセージを探して消える前に処理しようと無茶した可能性が高いですね。ようは仕事の進め方が変なのです。もし無責任にあのコードがメンテナンスが必要な製品に入れられたら保守担当が大変なことになりますよ。 私が推測したのは質問者さんがあそこをいじったのはそもそも前にオーバーライドしたコードが存在してたからだろうと思っていて、元のコードには真っ当な理由があったのではないか?と言っていただけです。
guest

回答2

0

ベストアンサー出ていますが、こんな感じで
.h側

C++

1class CListBoxEx :public CListBox // CListBoxの派生 2{ 3public: 4 CListBoxEx() {}; 5 ~CListBoxEx() {}; 6 DECLARE_MESSAGE_MAP() 7 afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); 8}; 9 10class ****Dlg : public CDialogEx // 親 11{ 12// 略 13 CListBoxEx m_lstBox; 14}

.cpp側

BEGIN_MESSAGE_MAP(CListBoxEx, CListBox) ON_WM_MOUSEWHEEL() END_MESSAGE_MAP() BOOL CListBoxEx::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { int index = GetCurSel(); if (index != LB_ERR) { int temp(index); if (0 < zDelta && 0 < index) { index--; } else if (zDelta < 0 && index < GetCount() - 1) { ++index; } if (temp != index) { SetCurSel(index); auto wnd = GetParent(); // 親ウィンドウ auto id = GetWindowLong(this->m_hWnd, GWL_ID); // コントロールID auto wp = (LBN_SELCHANGE << 16) | (id & 0xFFFF); // wParam値を作成 wnd->SendMessage(WM_COMMAND, wp, (LPARAM)this->m_hWnd); } return TRUE; } else return CListBox::OnMouseWheel(nFlags, zDelta, pt); } // 親のダイアログは普通にOnLbnSelchangeを受け取る void ****Dlg::OnLbnSelchange() { TRACE("%d \n", m_lstBox.GetCurSel()); }

投稿2024/01/25 13:31

yominet

総合スコア187

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

自己解決

今回はコールバックを使用して解決しました。本当はメッセージによる解決策も考えたいところでしたが、業務がひっ迫している関係で今回は見送ります。
具体的には、以下のように実装しました。これが適切かどうかは今の私では判断できかねますが、期待する動作をさせることができました。

C++

1// MyListBox 2class MyListBox : public CListBox 3{ 4 std::function<void()> m_callBack; 5 6public: 7 MyListBox(const std::function<void()> callBack) 8 : m_callBack(callBack) 9 {} 10 11public: 12 BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) 13 { 14 int select{ 0 }; //仮 ここで選択位置を計算 15 SetCurSel(select); 16 if (m_callBack) 17 m_callBack(); 18 return TRUE; 19 } 20} 21 22class MyDialog : public CDialogEx 23{ 24 MyListBox m_list; 25 26public: 27 MyDialog() 28 : m_list(std::bind(&MyDialog::OnLbnSelChange, this)) 29 {} 30 31protected: 32 // WM_LBN_SELCHANGEのハンドラ、コールバックにも流用 33 afx_msg void OnLbnSelChange() 34 { 35 // 選択変更したらやること 36 } 37 38 afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) 39 { 40 // ダイアログ上のマウスホイール操作はすべてリストボックスへ 41 return m_list.OnMouseWheel(nFlags, zDelta, pt); 42 } 43}

アドバイスくださったdameoさん、fanaさん、ありがとうございました。

投稿2024/01/25 03:01

Kosho9513

総合スコア6

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.39%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問