コントロールでスクロールされたことを検出する

コントロールでスクロールされたことを検出するコードを紹介します。

概要

フォームなどのScrollイベントのあるコントロールでは、Scrollイベントハンドラを実装することでスクロールを検出できますが、Scrollイベントのないコントロールではこの方法は使えません。Scrollイベントのないコントロールの場合はコントロールを派生してウィンドウメッセージを受信することでスクロールの検出ができます。

実装例

プロジェクトの作成

新規のWindowsフォームアプリケーションを作成します。プロジェクト作成後[新しい項目の追加]で[コンポーネント クラス]を選択します。[名前]の欄に名称を設定します。今回は"ScrollDetectTextBox.cs"としました。


ソリューションエクスプローラにコンポーネントクラスが追加されます。

コード(コンポーネント)

作成した"ScrollDetectTextBox.cs"に以下のコードを実装します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ScrollDetectControl
{
  public partial class ScrollDetectTextBox : TextBox
  {
    public ScrollDetectTextBox()
    {
      InitializeComponent();
    }

    public ScrollDetectTextBox(IContainer container)
    {
      container.Add(this);

      InitializeComponent();
    }

    public delegate void ScrollDelegate(object sender, ScrollEventArgs e);
    private ScrollDelegate onScroll;
    public event ScrollDelegate Scroll
    {
      add
      {
        onScroll += value;
      }
      remove
      {
        onScroll -= value;
      }
    }


    protected override void WndProc(ref Message message)
    {
      base.WndProc(ref message);

      switch (message.Msg) {
        case WindowsConst.WM_HSCROLL:
          WM_HSCROLL_Proc(message);
          break;
        case WindowsConst.WM_VSCROLL:
          WM_VSCROLL_Proc(message);
          break;
      }

    }

    private void WM_HSCROLL_Proc(Message message)
    {
      short hi = HiWord((long)message.WParam);
      short lo = LoWord((long)message.WParam);
      ScrollEventType set;

      switch (lo) {
        case WindowsConst.SB_ENDSCROLL:
          set = ScrollEventType.EndScroll;
          break;
        case WindowsConst.SB_LEFT:
          set = ScrollEventType.First;
          break;
        case WindowsConst.SB_RIGHT:
          set = ScrollEventType.Last;
          break;
        case WindowsConst.SB_LINELEFT:
          set = ScrollEventType.SmallDecrement;
          break;
        case WindowsConst.SB_LINERIGHT:
          set = ScrollEventType.SmallIncrement;
          break;
        case WindowsConst.SB_PAGELEFT:
          set = ScrollEventType.LargeDecrement;
          break;
        case WindowsConst.SB_PAGERIGHT:
          set = ScrollEventType.LargeIncrement;
          break;
        case WindowsConst.SB_THUMBPOSITION:
          set = ScrollEventType.ThumbPosition;
          break;
        case WindowsConst.SB_THUMBTRACK:
          set = ScrollEventType.ThumbTrack;
          break;
        default:
          set = ScrollEventType.EndScroll;
          break;
      }

      if (onScroll != null) {
        ScrollEventArgs e = new ScrollEventArgs(set, hi, ScrollOrientation.VerticalScroll);
        onScroll(this, e);
      }

    }

    private void WM_VSCROLL_Proc(Message message)
    {
      short hi = HiWord((long)message.WParam);
      short lo = LoWord((long)message.WParam);
      ScrollEventType set;

      switch (lo) {
        case WindowsConst.SB_BOTTOM:
          set = ScrollEventType.Last;
          break;
        case WindowsConst.SB_ENDSCROLL:
          set = ScrollEventType.EndScroll;
          break;
        case WindowsConst.SB_LINEDOWN:
          set = ScrollEventType.SmallIncrement;
          break;
        case WindowsConst.SB_LINEUP:
          set = ScrollEventType.SmallDecrement;
          break;
        case WindowsConst.SB_PAGEDOWN:
          set = ScrollEventType.LargeIncrement;
          break;
        case WindowsConst.SB_PAGEUP:
          set = ScrollEventType.LargeDecrement;
          break;
        case WindowsConst.SB_THUMBPOSITION:
          set = ScrollEventType.ThumbPosition;
          break;
        case WindowsConst.SB_THUMBTRACK:
          set = ScrollEventType.ThumbTrack;
          break;
        case WindowsConst.SB_TOP:
          set = ScrollEventType.First;
          break;
        default:
          set = ScrollEventType.EndScroll;
          break;
      }

      if (onScroll != null) {
        ScrollEventArgs e = new ScrollEventArgs(set, hi, ScrollOrientation.VerticalScroll);
        onScroll(this, e);
      }
    }

    protected short LoWord(long input)
    {
      return (short)((int)input & 0xFFFF);
    }

    protected short HiWord(long input)
    {
      return (short)((int)input >> 16);
    }
  }
}

解説

コンポーネントクラス作成時のコードは"Component"クラスを派生していますが、今回はTextBoxの派生クラスに変更し、スクロールを検出できるTextBoxを作成します。
public partial class ScrollDetectTextBox : Component
の部分を
public partial class ScrollDetectTextBox : TextBox
に変更します。

コントロールに送られたWM_SCROLLウィンドウメッセージを取得してスクロールを検出します。動作の詳細についてはこちらの記事を参照してください。

また、コントロールにScrollイベントを追加します。コンポーネントクラスへのイベント追加はこちらこちらの記事を参照してください。

アプリケーションUI

プロジェクトをビルドすると[ツールボックス]に作成した"ScrollDetectTextBox”コントロールが追加されます。


フォームデザイナに"ScrollDetectTextBox"コントロールを配置します。


"ScrollDetectTextBox"コントロールの[Multiline]プロパティを"true"にします。


"ScrollDetectTextBox"コントロールの[WordWrap]プロパティを"false"にします。また情報表示用のTextBoxを配置します。

コード(アプリケーション)

アプリケーションに以下のコードを記述します。
実際は"ScrollDetectTextBox"コントロールの"Scroll"イベントを実装します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ScrollDetectControl
{
  public partial class FormMain : Form
  {
    public FormMain()
    {
      InitializeComponent();
    }

    private void scrollDetectTextBox1_Scroll(object sender, ScrollEventArgs e)
    {
      if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll) {
        textBox1.Text += "横方向スクロール:";
      }
      else if (e.ScrollOrientation == ScrollOrientation.VerticalScroll) {
        textBox1.Text += "縦方向スクロール:";
    
      }

      switch (e.Type) {
        case ScrollEventType.EndScroll:
          textBox1.Text += "EndScroll \r\n";
          break;
        case ScrollEventType.First:
          textBox1.Text += "First \r\n";
          break;
        case ScrollEventType.LargeDecrement:
          textBox1.Text += "LargeDecrement \r\n";
          break;
        case ScrollEventType.LargeIncrement:
          textBox1.Text += "LargeIncrement \r\n";
          break;
        case ScrollEventType.Last:
          textBox1.Text += "Last \r\n";
          break;
        case ScrollEventType.SmallDecrement:
          textBox1.Text += "SmallDecrement \r\n";
          break;
        case ScrollEventType.SmallIncrement:
          textBox1.Text += "SmallIncrement \r\n";
          break;
        case ScrollEventType.ThumbPosition:
          textBox1.Text += "ThumbPosition \r\n";
          break;
        case ScrollEventType.ThumbTrack:
          textBox1.Text += "ThumbTrack \r\n";
          break;
      }
    }
  }
}

解説

"ScrollDetectTextBox"コントロールでスクロールされるとScrollイベントが呼び出されます。ScrollEventArgsのTypeです黒るの種類を、ScrollOrientationでスクロールの方向を検出します。

実行結果

アプリケーションを実行します。下図のウィンドウが表示されます。


"ScrollDetectTextBox"コントロールに文字列を入力しスクロールバーを有効にします。スクロールバーをクリックし"ScrollDetectTextBox"コントロールをスクロールすると下のTextBoxにスクロールされた旨のメッセージが表示されます。
スクロールバーのつまみ部分以外の背景をクリックしページスクロールすると"LargeDecrement"が表示されページスクロールも検出できます。


横方向のスクロールも検出できます。


著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
作成日: 2012-06-12
Copyright © iPentec all rights reserverd.