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

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

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

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

C++

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

Q&A

解決済

3回答

597閲覧

MFC ダイアログ上のリストボックスの項目の選択を、マウスホイールで変えたい。

Kosho9513

総合スコア6

MFC

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

C++

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

0グッド

0クリップ

投稿2024/01/24 07:30

編集2024/01/24 07:35

実現したいこと

ダイアログ上でマウスホイールしたら、モーダルダイアログに配置したリストボックスの選択を変更したいです。

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

ダイアログのPreTranslateMessageでリストボックスの選択を変更しようと考えましたが、ダイアログのPreTranslateMessageだけではなく、CWinApp継承クラスのPreTranslateMessageでWM_MOUSEWHEELを処理してダイアログにSendMessageしているのが関係しているのか、ダイアログのPreTranslateMessageにWM_MOUSEWHEELが入ってきません。
また、OnMouseWheelでは、リストボックス上でホイール操作したものが入ってこないため、実現できません。
PreTranslateMessageでも、OnMouseWheelでもいいので、マウスホイールでリストボックスの選択を変える方法があれば教えてください。

該当のソースコード

C++

1// PreTranslateMessageでやろうとしたときのソースコード 2// CWinApp継承クラス 3BOOL MyApp::PreTranslateMessage(MSG* pMsg) 4{ 5 // 色んな処理 6 if (pMsg->message == WM_MOUSEWHEEL) { 7 CWnd* pTarget = CWnd::WindowFromPoint(CPoint(pMsg->lParam)); 8 pTarget->SendMessage(WM_MOUSEWHEEL, pMsg->wParam, pMsg->lParam); 9 return TRUE; 10 } 11 return CWinApp::PreTranslateMessage(pMsg); 12} 13 14// CDialogEx継承クラス 15BOOL MyDialog::PreTranslateMessage(MSG* pMsg) 16{ 17 switch (pMsg->message) { 18 case WM_MOUSEWHEEL: 19 // リストボックスの選択を変える処理 20 return TRUE; 21 default: 22 break; 23 } 24 25 return CDialogEx::PreTranslateMessage(pMsg); 26}

試したこと・調べたこと

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

インターネットで検索しても有力な情報は得られませんでした・・・。
構造が似た簡易的なアプリケーションで試したら、CWinApp継承クラスのPreTranslateMessageでWM_MOUSEWHEELを処理しなかったら、ダイアログのPreTranslateMessageに入ってきました。ただ、システムが巨大で影響範囲が広いCWinApp継承クラスは変更しない方向で考えたいです。

補足

特になし

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

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

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

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

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

guest

回答3

0

ベストアンサー

OnMouseWheelでは、リストボックス上でホイール操作したものが入ってこない

CListBox をサブクラス化してメッセージを処理すれば良いのではないかと思うのですが,
リストボックスでない場所でもホイール操作でリストボックスの選択を変えたいという話だと,それだけでは不足なので,プラスアルファが必要そうです.

既にAPPクラスがこのメッセージを PreTranslateMessage で捕獲してしまい SendMessage で投げているので,ダイアログ側ではこのメッセージは PreTranslateMessage に来ないハズです.
とすると,ダイアログでは直接的に WM_MOUSEWHEEL をハンドルすれば良いように思うのですが…


…っていうのをちょっと(ダイアログベースプロジェクトを作って)やってみたところ,
リストボックス上でマウスホイールを動かすと,リストボックスとダイアログの両方の処理が走るという謎の挙動になってしまいました.ワケワカンネー.
→ そしたら今度は何かしら多重処理を抑制するための工夫が必要になるのかもしれません……

あと,ダイアログ上の他のコントロール上でホイール操作した場合はどうするのか? とか考えると,そこはうまい方法は思いつかない感じです.

投稿2024/01/24 09:41

編集2024/01/24 09:56
fana

総合スコア11658

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

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

fana

2024/01/24 09:49

と,まぁ,完全に対応できる話ではないのですが,何かの参考になるかもしれないので書いてみました. > CWinApp継承クラスのPreTranslateMessageでWM_MOUSEWHEELを処理しなかったら、ダイアログのPreTranslateMessageに入ってきました。ただ、システムが巨大で影響範囲が広いCWinApp継承クラスは変更しない方向で考えたいです。 とのことですが,APPクラス側で「対象(pTarget)がこのダイアログであるときだけは処理しないぜ!」みたいな変更を施すこともNGなのでしょうか. 影響はこのダイアログだけになる(?)でしょうし,最も手っ取り早い気がしますが…
fana

2024/01/24 09:54

最初, > 既にAPPクラスがこのメッセージを PreTranslateMessage で捕獲してしまい SendMessage で投げている のところを PostMessage に変えればイインジャネーノ? とか思ったのですが,単純にそれやると,ポストしたメッセージをまたここが最初に捕獲するわけなので無限ループ状態ですね^^ 別のユーザ定義メッセージ(このAPPクラスで処理しないやつ)をポストするとかすれば回避できる気がしますが,いずれにせよAPPクラス側を変更できないという話なのでダメですよね.
Kosho9513

2024/01/24 23:14 編集

ご回答ありがとうございます。 >既にAPPクラスがこのメッセージを PreTranslateMessage で捕獲してしまい SendMessage で投げているので,ダイアログ側ではこのメッセージは PreTranslateMessage に来ないハズです. これは知らなかった情報なのでありがたいです。ちなみに、このことに関する記述があるドキュメント等ありますでしょうか? サブクラス化が頭から抜け落ちていて試していませんでした。 一度サブクラス化を試してみます。
Kosho9513

2024/01/25 00:46

サブクラス化で期待する動作にすることができました! 別の問題も出てきましたが、それは別の質問にさせていただきます。 ありがとうございました。
fana

2024/01/25 01:14

> このことに関する記述があるドキュメント等ありますでしょうか? https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessage の Remarks のところに以下の一文があります. > If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. 要は,SendMessageは(ここで言われている条件を満たす場合には)ウィンドウプロシージャをダイレクトに呼び出す,という話なので,そこのところに他の処理(今回は PreTranslateMessage)が入り込む余地がない,という話になるかと思います.
guest

0

MFCを使っていたのは大昔すぎてうろ覚えなのですが、PreTranslateMessageはメッセージを事前に処理し、本来のイベントハンドラに渡さないために使用してたはずです。戻り値でFALSEを返せば本来のイベントハンドラにそのイベントが渡ります(既定の動作(=基底クラス呼び出し)がFALSEでオーバーライドして止めたいときにTRUEにする)。


とりあえずコードがないと分からないのかもしれないということで、fanaさんが言っている

CListBox をサブクラス化してメッセージを処理

を作るべく、ダイアログベースのMFCアプリケーションを作成したところからのリソースを除く差分だけ載せます。

diff

1diff --git a/CHogeListBox.cpp b/CHogeListBox.cpp 2new file mode 100644 3index 0000000..4260d57 4--- /dev/null 5+++ b/CHogeListBox.cpp 6@@ -0,0 +1,18 @@ 7+#include "pch.h" 8+#include "CHogeListBox.h" 9+ 10+BEGIN_MESSAGE_MAP(CHogeListBox, CListBox) 11+ ON_WM_MOUSEWHEEL() 12+END_MESSAGE_MAP() 13+ 14+BOOL CHogeListBox::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { 15+ int selected = this->GetCurSel(); 16+ if (selected != LB_ERR) { 17+ int count = this->GetCount(); 18+ selected = selected - ((zDelta >= 0) * 2 - 1); 19+ selected = selected < 0 ? 0 : selected; 20+ selected = selected >= count ? count - 1 : selected; 21+ this->SetCurSel(selected); 22+ } 23+ return TRUE; 24+} 25\ No newline at end of file 26diff --git a/CHogeListBox.h b/CHogeListBox.h 27new file mode 100644 28index 0000000..74c5d63 29--- /dev/null 30+++ b/CHogeListBox.h 31@@ -0,0 +1,10 @@ 32+#pragma once 33+#include <afxwin.h> 34+class CHogeListBox : 35+ public CListBox 36+{ 37+protected: 38+ afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); 39+ DECLARE_MESSAGE_MAP() 40+}; 41+ 42diff --git a/MFCApplication1Dlg.cpp b/MFCApplication1Dlg.cpp 43index 215c288..7c3bf84 100644 44--- a/MFCApplication1Dlg.cpp 45+++ b/MFCApplication1Dlg.cpp 46@@ -59,6 +59,7 @@ CMFCApplication1Dlg::CMFCApplication1Dlg(CWnd* pParent /*=nullptr*/) 47 void CMFCApplication1Dlg::DoDataExchange(CDataExchange* pDX) 48 { 49 CDialogEx::DoDataExchange(pDX); 50+ DDX_Control(pDX, IDC_LIST1, m_lbList1); 51 } 52 53 BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx) 54@@ -100,6 +101,11 @@ BOOL CMFCApplication1Dlg::OnInitDialog() 55 SetIcon(m_hIcon, FALSE); // 小さいアイコンの設定 56 57 // TODO: 初期化をここに追加します。 58+ CString str; 59+ for (int i = 0; i < 100; ++i) { 60+ str.Format(_T("項目%03d"), i + 1); 61+ m_lbList1.AddString(str); 62+ } 63 64 return TRUE; // フォーカスをコントロールに設定した場合を除き、TRUE を返します。 65 } 66@@ -152,4 +158,3 @@ HCURSOR CMFCApplication1Dlg::OnQueryDragIcon() 67 { 68 return static_cast<HCURSOR>(m_hIcon); 69 } 70- 71diff --git a/MFCApplication1Dlg.h b/MFCApplication1Dlg.h 72index f94a9a5..4af5d41 100644 73--- a/MFCApplication1Dlg.h 74+++ b/MFCApplication1Dlg.h 75@@ -3,6 +3,7 @@ 76 // 77 78 #pragma once 79+#include "CHogeListBox.h" 80 81 82 // CMFCApplication1Dlg ダイアログ 83@@ -31,4 +32,6 @@ protected: 84 afx_msg void OnPaint(); 85 afx_msg HCURSOR OnQueryDragIcon(); 86 DECLARE_MESSAGE_MAP() 87+public: 88+ CHogeListBox m_lbList1; 89 };

ホイールを上下させてるときの動作
イメージ説明

投稿2024/01/24 10:31

編集2024/01/24 18:08
dameo

総合スコア943

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

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

Kosho9513

2024/01/24 23:15

ご回答ありがとうございます。 コードまでつけていただきありがとうございます。 サブクラス化で試してみます。
Kosho9513

2024/01/25 00:47 編集

いただいたコードを参考にして、期待する動作にすることができました。 別の問題も出てきましたが、それは別の質問にさせていただきます。 ありがとうございました。
guest

0

OS側の設定になりますが、Windows10以降を利用しているとして
Windowsボタン(左下)をクリック、設定(歯車マーク)をクリック
Windowsの設定画面が出る。その中のデバイスをクリック、左メニューからマウスを選択。
変わった右画面の中で、ポイントしたときに非アクティブウィンドウをスクロールするがオフだった場合、オンにしてみてください。
過去にホイールスクロールで変わるのが嫌で、そこをオフにしたらできなくなったことがあるので。

投稿2024/01/24 08:25

編集2024/01/24 08:26
ardin

総合スコア546

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

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

ardin

2024/01/24 08:27

ちなみにOS側の設定なので、Windowsの操作全てに効いてきます。
Kosho9513

2024/01/24 08:32

ご回答ありがとうございます。 Windows11を使用しており、設定を確認しましたが「ホバーしたときに非アクティブウィンドウをスクロールする」はONになっていました。
ardin

2024/01/24 09:04 編集

そうですか。残念。 それでは別回答として、結局ホイール対象はダイアログではなくリストボックスなので、リストボックス側のホイールイベントを取らないとダメです。 オーナードローで自分で作りましょう。
Kosho9513

2024/01/24 23:14

ありがとうございます。 サブクラス化を試していなかったので、やってみます。
Kosho9513

2024/01/25 00:48

サブクラス化で期待する動作にすることができました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問