Windowsフォームアプリにおけるスワイプによるスクロールについて

投稿者: Anonymous

現在C#/.net4.5環境においてWindowsフォームを使った
Win8タブレット向けのアプリケーションを作成しています。

アプリでは、

■PanelA
├■Button1
├■Button2
├■Button3

といった形で、パネルの子コントロールとしてボタンを並べて配置している箇所があります。
このとき、PanelAにはスクロールバーが表示される状態になっており、
PanelA上の何も無い箇所をスワイプするとスクロールを行ってくれるのですが、
Button上を起点にスワイプするとスクロールされません。

調べて試してみたのですが、WPFではScrollViewer.PanningModeを設定することで、
ボタン上からのスワイプによってスクロールされることを確認しました。
ただ、WPFとの混合運用は極力避けたいと考えており、
まずはWindowsフォームでそもそもこの機構を実現するのは難しいのかどうかも合わせて、
ご教授いただけないでしょうか。

よろしくお願いいたします。

解決

Buttonの様なクリックのみで動作するコントロールであれば、MouseDownMouseMoveの中で親コントロールにCaptureを設定すれば親がドラッグされているかのように動作させられます。下のコードはクリックをキャンセルしてPanelを水平スクロールさせるサンプルになります。

public partial class Form1 : Form
{
    private int _PreviousX;

    public Form1()
    {
        // デザイナー上ではpanel1の中にButtonが水平に並べてあります。
        InitializeComponent();

        // パネル内のコントロールにイベントハンドラーを設定
        foreach (Control c in panel1.Controls)
        {
            c.Click += (s, args) => MessageBox.Show(s.ToString());
            c.MouseDown += Button_MouseDown;
            c.MouseMove += Button_MouseMove;
        }
    }

    private void panel1_MouseDown(object sender, MouseEventArgs e)
    {
        // パネルが普通にクリックされた場合
        panel1.Capture = true;
        _PreviousX = panel1.PointToScreen(e.Location).X;
    }

    private void panel1_MouseMove(object sender, MouseEventArgs e)
    {
        if (panel1.Capture)
        {
            // パネルのドラッグ処理
            var x = panel1.PointToScreen(e.Location).X;
            panel1.HorizontalScroll.Value = Math.Min(
                                                Math.Max(
                                                    panel1.HorizontalScroll.Minimum,
                                                    panel1.HorizontalScroll.Value + _PreviousX - x),
                                                    panel1.HorizontalScroll.Maximum);
            _PreviousX = x;
        }
    }

    private void panel1_MouseUp(object sender, MouseEventArgs e)
    {
        panel1.Capture = false;
    }

    private void Button_MouseDown(object sender, MouseEventArgs e)
    {
        // ボタンがクリックされた場合
        var c = (Control)sender;

        c.Capture = true;

        _PreviousX = c.PointToScreen(e.Location).X;
    }

    private void Button_MouseMove(object sender, MouseEventArgs e)
    {
        var c = (Control)sender;
        if (c.Capture)
        {
            // ボタンが押された状態である程度動いたらpanel1にイベントを付け替える
            var x = panel1.PointToScreen(e.Location).X;
            if (Math.Abs(x - _PreviousX) > 3)
            {
                c.Capture = false;
                panel1.Capture = true;
            }
        }
    }
}
回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *