DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

タイトルバーのないフォームを移動できるようにする

タイトルバーを持たないフォームをマウスで移動させる方法を紹介します。

マウスイベントによる方法

まずは、最も基本的なやり方です。次のコードでは、単純にフォーム(Form1)上でマウスの左ボタンが押され、移動させたことを感知することにより処理しています。

VB.NET
コードを隠すコードを選択
'マウスのクリック位置を記憶
Private mousePoint As Point

'Form1のMouseDownイベントハンドラ
'マウスのボタンが押されたとき
Private Sub Form1_MouseDown(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) _
        Handles MyBase.MouseDown
    If (e.Button And MouseButtons.Left) = MouseButtons.Left Then
        '位置を記憶する
        mousePoint = New Point(e.X, e.Y)
    End If
End Sub

'Form1のMouseMoveイベントハンドラ
'マウスが動いたとき
Private Sub Form1_MouseMove(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) _
        Handles MyBase.MouseMove
    If (e.Button And MouseButtons.Left) = MouseButtons.Left Then
        Me.Left += e.X - mousePoint.X
        Me.Top += e.Y - mousePoint.Y
        'または、つぎのようにする
        'Me.Location = New Point( _
        '    Me.Location.X + e.X - mousePoint.X, _
        '    Me.Location.Y + e.Y - mousePoint.Y)
    End If
End Sub
C#
コードを隠すコードを選択
//マウスのクリック位置を記憶
private Point mousePoint;

//Form1のMouseDownイベントハンドラ
//マウスのボタンが押されたとき
private void Form1_MouseDown(object sender,
    System.Windows.Forms.MouseEventArgs e)
{
    if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
    {
        //位置を記憶する
        mousePoint = new Point(e.X, e.Y);
    }
}

//Form1のMouseMoveイベントハンドラ
//マウスが動いたとき
private void Form1_MouseMove(object sender,
    System.Windows.Forms.MouseEventArgs e)
{
    if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
    {
        this.Left += e.X - mousePoint.X;
        this.Top += e.Y - mousePoint.Y;
        //または、つぎのようにする
        //this.Location = new Point(
        //    this.Location.X + e.X - mousePoint.X,
        //    this.Location.Y + e.Y - mousePoint.Y);
    }
}

上の例ではフォーム上にあるコントロールの上でマウスのボタンを押してドラッグしてもウィンドウは移動しません。コントロール上のドラッグでも移動できるようにするには、そのコントロールのMouseDownおよびMouseMoveイベントハンドラにForm1_MouseDownおよびForm1_MouseMoveを追加します。例えばForm1にPictureBox1オブジェクトがあり、PictureBox1上でマウスボタンを押してドラッグを開始してもForm1が移動できるようにするには次のようにします。

VB.NET
コードを隠すコードを選択
Private mousePoint As Point

'Form1のMouseDownイベントハンドラ
Private Sub Form1_MouseDown(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) _
        Handles MyBase.MouseDown, PictureBox1.MouseDown
    '(上の例と同じため省略)
End Sub

'Form1のMouseMoveイベントハンドラ
Private Sub Form1_MouseMove(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) _
        Handles MyBase.MouseMove, PictureBox1.MouseMove
    '(上の例と同じため省略)
End Sub
C#
コードを隠すコードを選択
//Form1のLoadイベントハンドラ
private void Form1_Load(object sender, System.EventArgs e)
{
    PictureBox1.MouseDown +=
        new MouseEventHandler(Form1_MouseDown);
    PictureBox1.MouseMove +=
        new MouseEventHandler(Form1_MouseMove);
}

private Point mousePoint;

//Form1のMouseDownイベントハンドラ
private void Form1_MouseDown(object sender,
    System.Windows.Forms.MouseEventArgs e)
{
    //(上の例と同じため省略)
}

//Form1のMouseMoveイベントハンドラ
private void Form1_MouseMove(object sender,
    System.Windows.Forms.MouseEventArgs e)
{
    //(上の例と同じため省略)
}

Win32 APIを使った方法

Win32 APIのReleaseCaptureとSendMessageを使って、フォーム上でマウスの左ボタンが押されたら、タイトルバー上でマウスの左ボタンが押されたことにするという方法があります。これは、「Move window/form without Titlebar in C#」などで紹介されている方法です。

VB.NET
コードを隠すコードを選択
'Imports System.Runtime.InteropServices

Private Const WM_NCLBUTTONDOWN As Integer = &HA1
Private Const HT_CAPTION As Integer = &H2

<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SendMessage(hWnd As IntPtr, _
    Msg As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
End Function
<DllImportAttribute("user32.dll")> _
Private Shared Function ReleaseCapture() As Boolean
End Function

'Form1のMouseDownイベントハンドラ
Private Sub Form1_MouseDown(sender As Object, _
        e As System.Windows.Forms.MouseEventArgs) _
        Handles MyBase.MouseMove
    If e.Button = MouseButtons.Left Then
        'マウスのキャプチャを解除
        ReleaseCapture()
        'タイトルバーでマウスの左ボタンが押されたことにする
        SendMessage(Handle, WM_NCLBUTTONDOWN, _
                    New IntPtr(HT_CAPTION), IntPtr.Zero)
    End If
End Sub
C#
コードを隠すコードを選択
//using System.Runtime.InteropServices;

private const int WM_NCLBUTTONDOWN = 0xA1;
private const int HT_CAPTION = 0x2;

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(
    IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImportAttribute("user32.dll")]
private static extern bool ReleaseCapture();

//Form1のMouseDownイベントハンドラ
private void Form1_MouseDown(object sender,
    System.Windows.Forms.MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        //マウスのキャプチャを解除
        ReleaseCapture();
        //タイトルバーでマウスの左ボタンが押されたことにする
        SendMessage(Handle, WM_NCLBUTTONDOWN, (IntPtr)HT_CAPTION, IntPtr.Zero);
    }
}

この方法も前の方法と同じように、フォーム上にあるコントロールの上でマウスのボタンを押してドラッグしてもウィンドウは移動しません。

WndProcをオーバーライドする方法

次は、フォームのWndProcメソッドをオーバーライドする方法です。この方法では、あたかもタイトルバーをマウスで移動させているふりをして、フォームを移動させています。

VB.NET
コードを隠すコードを選択
'Imports System.Security.Permissions

<SecurityPermission(SecurityAction.Demand, _
    Flags:=SecurityPermissionFlag.UnmanagedCode)> _
Protected Overrides Sub WndProc(ByRef m As Message)
    MyBase.WndProc(m)

    Const WM_NCHITTEST As Integer = &H84
    Const HTCLIENT As Integer = 1
    Const HTCAPTION As Integer = 2

    'マウスポインタがクライアント領域内にあるか
    If (m.Msg = WM_NCHITTEST) AndAlso (m.Result.ToInt32() = HTCLIENT) Then
        'マウスがタイトルバーにあるふりをする
        m.Result = New IntPtr(HTCAPTION)
    End If
End Sub
C#
コードを隠すコードを選択
//using System.Security.Permissions;

[SecurityPermission(SecurityAction.Demand,
    Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m)
{
    base.WndProc (ref m);

    const int WM_NCHITTEST = 0x84;
    const int HTCLIENT = 1;
    const int HTCAPTION = 2;

    //マウスポインタがクライアント領域内にあるか
    if ((m.Msg == WM_NCHITTEST) &&
        (m.Result.ToInt32() == HTCLIENT))
    {
        //マウスがタイトルバーにあるふりをする
        m.Result = (IntPtr) HTCAPTION;
    }
}

この方法では、フォームをダブルクリックした時にフォームが最大化されます。フォームの最大化を防ぐ方法は、「フォームを最大化、最小化できないようにする」で説明しています。

この方法もはじめの方法と同様、このままでは、フォーム上にあるコントロールの上でマウスのボタンを押して移動させようとしてもウィンドウは移動しません。

  • 履歴:
  • 2006/9/20 コメントにお寄せいただいたご指摘により、一番始めのコードを書き直しました。
  • 2010/6/30 WndProcにSecurityPermissionAttributeを付けた。
  • 2013/7/6 「Win32 APIを使った方法」を追加。
  • 2013/12/9 SecurityAction.LinkDemandの代わりにSecurityAction.Demandを使うようにした。
  • 2015/6/15 「WndProcをオーバーライドする方法」で、フォームをダブルクリックすると最大化されることを追記(コメントでご指摘いただきました)。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。