none
WinForms コントロール ListView マウスダウンによる項目直後にスクロールすると選択項目までスクロールが戻る現象 RRS feed

  • 質問

  • 1. ListViewコントロールに300個など、スクロール表示される部分までItemを追加する

    2. Selected、マウスダウン等による方法でItemを選択する

    3. その直後にその選択された項目が見えなくなるまでスクロールする

    4. 3で選択した項目までスクロールが戻る

    という現象がVB6、.NETのListViewで発生するのですが、

    何か有益な情報をご存じの方がいましたら教えていただけると幸いです。

    2016年2月3日 1:21

回答

  • スクロールが戻らないようにするだけなら

    class ListViewEx : ListView
    {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool KillTimer(IntPtr hWnd, IntPtr uIDEvent);
    
        protected override void WndProc(ref Message m)
        {
            const int WM_HSCROLL = 0x114;
            const int WM_VSCROLL = 0x115;
            const int WM_TIMER = 0x0113;
            const int WM_MOUSEWHEEL = 0x020A;
    
            switch (m.Msg)
            {
            case WM_TIMER:
                KillTimer(this.Handle, m.WParam);
                m.Result = IntPtr.Zero;
                return;
            }
    
            base.WndProc(ref m);
        }
    }
    のようにWM_TIMERを捨てるといいみたい。

    内部でタイマーを描画更新のトリガーに使って選択された項目にスクロールしてるのかな?


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)


    • 編集済み gekkaMVP 2016年2月3日 10:20 WM_WHEEL ->WM_MOUSEWHEEL
    • 回答としてマーク ぱちもん 2016年2月3日 10:41
    2016年2月3日 9:38

すべての返信

  • 「ぱちもん」さん、

    テストプロジェクトを作って試してみましたが、再現しませんでした。
    試した環境は、Windows 10 Pro(64-bit), Visual Studio 2015 Update 1, VisualBasic, .NET Framework 4.5.2 です。

    1.別環境でも同じプロジェクトを使って試してみますが、「ぱちもん」さんの環境をお教えください。
    2.また、できればプロジェクトをシェアしていただけると幸いです(OneDrive, Dropbox などのクラウドストレージで)

    以上です。

    2016年2月3日 4:25
  • こんにちは。サンプルプロジェクトを作成しました。

    環境:Windows 7 Pro 64bit, Visual Studio 2013 Update5, C# または VB, .NET Framework 4.5.2

    https://onedrive.live.com/redir?resid=3C3E7130A10633D2!3878&authkey=!ALm-JxWcnYMl38w&ithint=file%2czip

    コードによる選択でスクロールが戻る現象はこのプロジェクトでは確認できませんでしたが、マウスダウン→即時にスクロールで選択位置まで戻ってしまう現象は確認できました。

    2016年2月3日 6:52
  • 「ぱちもん」さん、

    Project(Solution) ファイルをシェアしていただき、ありがとうございます。
    さっそく解凍してデバッグ実行してみました。

    「マウスダウン→即時にスクロールで選択位置まで戻ってしまう」とお書きになっている部分の
    「マウスダウン」とはどの部分のマウスダウンですか?

    垂直スクロールバーをクリックしても、見えなくなった選択位置まで戻ることはないのですが・・・

    2016年2月3日 7:04
  • すみません、動作に対する詳細がいくつか抜けておりました。

    マウスダウンする場所はListViewの選択したい行です。または既に選択されている行でも構いません。

    この状態から「マウスホイールによるスクロール」を実行し、少し待つと選択された行までスクロールが戻ります。

    追記でWindows8でも試してみましたが、同じ現象が発生してしまいます。あと単純にマウスが壊れているのかと思ったのですが、それとも違うようです。(他アプリケーションでは正常に動作する)

    意図しないタイミングでWM_VSCROLLが発生しているのではないかと思ってみてみましたが、そもそもホイールによるスクロールではこのメッセージは発生していないようでした。

    2016年2月3日 7:33
  • 「ぱちもん」さん、

    Windows 8.1 Pro(64-bit)、Visual Studio 2013 でデバッグ実行し、
    書いていただいた操作をしてみました。

    もう少し細かく書くと・・・
    「通常のリストビュー」で、スクロールバーで真ん中あたりまで移動し、行を選択しました。
    その状態で、選択行が見えなくなるまで「マウスホイール」で上方または下方にスクロールしました。
    そのまま放置しても、見えなくなった選択行が見えるようになる(選択された行にスクロールが戻る)現象は発生しませんでした。

    「仮想リストビュー」でも同じ結果でした。
    (仮想リストビューというのがどのような機能・テストのためかは分かっていません・・・(^-^;)

    Windows 10 Pro(64-bit)とのデュアルブートPCなので、本ポストを投稿してから Win. 10 でも動作を確認してみます。また、Win. 7 マシンでの結果も報告します。

    以上。

    2016年2月3日 9:01
  • 結果報告です。
     ① Windows 8.1 Pro(64-bit)、Visual Studio 2013 
     ② Windows 10 Pro(64-bit)、Visual Studio 2015
     ③ Windows 7 Ultimate(64-bit)、Visual Studio 2013
    以上の環境では再現しませんでした。

    原因は、マウス関連(設定とかドライバーとか)にあるように思えます。
    よろしければ、PCとマウスの情報(メーカー、モデル名など)をお教えいただけませんか?

    ちなみに、私の環境は次のとおりです。
    ①、②・・・デュアルブート:(Lenovo)ThinkPad E540(ノートPC)
    ③・・・自作機(タワー型のメインPC)
    マウスはどちらも(Microsoft)43U-00037(ワイヤレス、USB トランシーバ使用のスカルプト モバイル マウスという廉価なもの)

    以上です。

    2016年2月3日 9:30
  • スクロールが戻らないようにするだけなら

    class ListViewEx : ListView
    {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool KillTimer(IntPtr hWnd, IntPtr uIDEvent);
    
        protected override void WndProc(ref Message m)
        {
            const int WM_HSCROLL = 0x114;
            const int WM_VSCROLL = 0x115;
            const int WM_TIMER = 0x0113;
            const int WM_MOUSEWHEEL = 0x020A;
    
            switch (m.Msg)
            {
            case WM_TIMER:
                KillTimer(this.Handle, m.WParam);
                m.Result = IntPtr.Zero;
                return;
            }
    
            base.WndProc(ref m);
        }
    }
    のようにWM_TIMERを捨てるといいみたい。

    内部でタイマーを描画更新のトリガーに使って選択された項目にスクロールしてるのかな?


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)


    • 編集済み gekkaMVP 2016年2月3日 10:20 WM_WHEEL ->WM_MOUSEWHEEL
    • 回答としてマーク ぱちもん 2016年2月3日 10:41
    2016年2月3日 9:38
  • 動画を録画しました。録画環境は前回の通りです。少し分かり辛いかもしれませんが、クリック直後に選択行が見えなくなるまでスクロールしています。少し待つとスクロールが戻ります。(このときWM_MOUSEWHEELは発生しないようです。)

    メーカー:VAIO

    型番:VPCEA4AGJ

    使用マウス:Logicool M570

    https://onedrive.live.com/redir?resid=3C3E7130A10633D2!3882&authkey=!AAlxQdQio-1N7Qk&ithint=file%2czip

    2016年2月3日 9:52
  • 「ぱちもん」さん、

    mp4 を拝見しました。
    マウスホイールが回されたことは映像では分からないので、操作を理解/再現するのは難しいですが、意図は分かったつもりです。

    もし最終的に実現したいことが(自動)スクロール防止なら、gekka さんのコードが参考になるのではないでしょうか?
    (gekka さんのお名前は随所で拝見しています・・・<m(__)m>)
    もしそうでないなら、最終的に何ができればよいのかをお教えいただければ幸いです。

    以上です。

    2016年2月3日 10:12
  • <del>

    gekkaさんのコードで試してみたところ、自動スクロールがなくりました。Selectもできなくなってしまいましたが・・・。

    確かにWM_TIMERがそれらしき現象に関与してそうですね。

    もしかしてSelectedすると同時にEnsureVisible()的なメソッドが呼ばれているのかもしれないですね。

    こちらでももう少し調べてみようと思います。みなさま情報提供感謝です。


    </del>

    ↑プロパティの設定漏れでした。上記コードで問題なく、動作します。ありがとうございました!

    • 編集済み ぱちもん 2016年2月3日 10:41 状況変化
    2016年2月3日 10:26