web-dev-qa-db-ja.com

ボーダーレスフォームC#のカスタムサイズ変更ハンドル

ツールバーから飛び出すフチなしのフォームを作成しようとしています。ユーザーが右下隅(「サイズ変更ハンドル」)をつかんでフォームのサイズを変更できるようにしたいのですが、他の方法でフォームのサイズ変更や再配置を行うことはできません。

フォームに送信されたWM_NCHITTESTメッセージを傍受し、その結果をHTBOTTOMRIGHTに設定すると、オペレーティングシステムがフォームのサイズ変更を処理できるようになると聞きました。かなりのフレーム。私が持っていたアイデアは、マウスポインターが隅に定義したボックスに入ったかどうかを検出し、入った場合はHTBOTTOMRIGHTの結果を返すことでした。

Graphic illustrating the resize handle

これは私が期待したようにはうまくいきません。メッセージを傍受することはできますが、ユーザーがフォームの1pxの太い境界線にマウスカーソルを置いた場合にのみメッセージが送信されるようです。つまり、カーソルを右下の端に非常に正確に配置すれば、私が望むように機能します。

これが私のWndProcオーバーライドです:

protected override void WndProc(ref Message m)
{
    const UInt32 WM_NCHITTEST = 0x0084;
    const UInt32 HTBOTTOMRIGHT = 17;
    const int RESIZE_HANDLE_SIZE = 40;
    bool handled = false;
    if (m.Msg == WM_NCHITTEST)
    {
        Size formSize = this.Size;
        Point screenPoint = new Point(m.LParam.ToInt32());
        Point clientPoint = this.PointToClient(screenPoint);
        Rectangle hitBox = new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE);
        if (hitBox.Contains(clientPoint))
        {
            m.Result = (IntPtr)HTBOTTOMRIGHT;
            handled = true;
        }
    }

    if (!handled)
        base.WndProc(ref m);
}

私は何か間違ったことをしていますか、それとも私がやろうとしていることをするためのより良い方法がありますか?

どうもありがとう。

16
Frank Weindel

私は似たようなものを探していましたが、アントンのコードは素晴らしいベースでした。これは私がすべての側面からサイズ変更作業をすることになったものです。ヒットボックスを保存するのにDictionaryが最適な方法かどうかはわかりませんが、それほど重要ではないと思います。

また、フォームはDockパラメーターとしてFillを使用してコントロールで埋められているため、正常に機能させるには、Formに5pxのパディングを追加する必要がありました。

protected override void WndProc(ref Message m)
{
    const UInt32 WM_NCHITTEST = 0x0084;
    const UInt32 WM_MOUSEMOVE = 0x0200;

    const UInt32 HTLEFT = 10;
    const UInt32 HTRIGHT = 11;
    const UInt32 HTBOTTOMRIGHT = 17;
    const UInt32 HTBOTTOM = 15;
    const UInt32 HTBOTTOMLEFT = 16;
    const UInt32 HTTOP = 12;
    const UInt32 HTTOPLEFT = 13;
    const UInt32 HTTOPRIGHT = 14;

    const int RESIZE_HANDLE_SIZE = 10;
    bool handled = false;
    if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
    {
        Size formSize = this.Size;
        Point screenPoint = new Point(m.LParam.ToInt32());
        Point clientPoint = this.PointToClient(screenPoint);

        Dictionary<UInt32, Rectangle> boxes = new Dictionary<UInt32, Rectangle>() {
            {HTBOTTOMLEFT, new Rectangle(0, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
            {HTBOTTOM, new Rectangle(RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, formSize.Width - 2*RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
            {HTBOTTOMRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
            {HTRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, formSize.Height - 2*RESIZE_HANDLE_SIZE)},
            {HTTOPRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
            {HTTOP, new Rectangle(RESIZE_HANDLE_SIZE, 0, formSize.Width - 2*RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
            {HTTOPLEFT, new Rectangle(0, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
            {HTLEFT, new Rectangle(0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, formSize.Height - 2*RESIZE_HANDLE_SIZE) }
        };

        foreach (KeyValuePair<UInt32, Rectangle> hitBox in boxes)
        {
            if (hitBox.Value.Contains(clientPoint))
            {
                m.Result = (IntPtr) hitBox.Key;
                handled = true;
                break;
            }
        }
    }

    if (!handled)
        base.WndProc(ref m);
}
17
Charles P.

Anton Semenov、あなたのコードがわかりませんでした。

とにかく、私はCharles Pの最初のコードに問題がありました、
ウィンドウを最大化してからサイズを変更しようとすると、サイズが変更されます。
その後、通常のサイズに再度修正することも、通常の最大ボタンで再度最大化することもできませんでした。

この問題を修正するために私がしたことは、下部の「foreach」ループ内に条件を追加することでした。

    protected override void WndProc(ref Message m)
    {
        const UInt32 WM_NCHITTEST = 0x0084;
        const UInt32 WM_MOUSEMOVE = 0x0200;

        const UInt32 HTLEFT = 10;
        const UInt32 HTRIGHT = 11;
        const UInt32 HTBOTTOMRIGHT = 17;
        const UInt32 HTBOTTOM = 15;
        const UInt32 HTBOTTOMLEFT = 16;
        const UInt32 HTTOP = 12;
        const UInt32 HTTOPLEFT = 13;
        const UInt32 HTTOPRIGHT = 14;

        const int RESIZE_HANDLE_SIZE = 10;
        bool handled = false;
        if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
        {
            Size formSize = this.Size;
            Point screenPoint = new Point(m.LParam.ToInt32());
            Point clientPoint = this.PointToClient(screenPoint);

            Dictionary<UInt32, Rectangle> boxes = new Dictionary<UInt32, Rectangle>() {
        {HTBOTTOMLEFT, new Rectangle(0, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
        {HTBOTTOM, new Rectangle(RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, formSize.Width - 2*RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
        {HTBOTTOMRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
        {HTRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, formSize.Height - 2*RESIZE_HANDLE_SIZE)},
        {HTTOPRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
        {HTTOP, new Rectangle(RESIZE_HANDLE_SIZE, 0, formSize.Width - 2*RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
        {HTTOPLEFT, new Rectangle(0, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
        {HTLEFT, new Rectangle(0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, formSize.Height - 2*RESIZE_HANDLE_SIZE) }
            };

            foreach (KeyValuePair<UInt32, Rectangle> hitBox in boxes)
            {
                if (this.WindowState != FormWindowState.Maximized 
                    && hitBox.Value.Contains(clientPoint))
                    {
                        m.Result = (IntPtr)hitBox.Key;
                        handled = true;
                        break;
                    }
            }
        }

        if (!handled)
            base.WndProc(ref m);
    }
2
snir

コードを少し変更するだけです。 WM_MOUSEMOVEメッセージ処理を追加しました:

    protected override void WndProc(ref Message m)
    {
        const UInt32 WM_NCHITTEST = 0x0084;
        const UInt32 WM_MOUSEMOVE = 0x0200;
        const UInt32 HTBOTTOMRIGHT = 17;
        const int RESIZE_HANDLE_SIZE = 10;
        bool handled = false;
        if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE )
        {
            Size formSize = this.Size;
            Point screenPoint = new Point(m.LParam.ToInt32());
            Point clientPoint = this.PointToClient(screenPoint);
            Rectangle hitBox = new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE);
            if (hitBox.Contains(clientPoint))
            {
                m.Result = (IntPtr)HTBOTTOMRIGHT;
                handled = true;
            }
        }

        if (!handled)
            base.WndProc(ref m);
    }

ちなみに、ControlPaint.DrawSizeGrip Methodhttp://msdn.Microsoft.com/en-us/library/2e1yx2sa.aspx を使用して、システム固有のウィンドウサイズグリップを描画できます。

2
Anton Semenov

Charles P.ソリューションに基づいていくつかの変更を加えましたが、他の人にも役立つことを願っています:)Windowsメッセージが呼び出されるたびに余分な変数を宣言しないようにするための小さなチェックと改善。また、ウィンドウの状態が最大化されているときにグリップアンカーをペイントしないことも確認します。それからカスタムコントロールを作成したかったのですが、残念ながらこのコードをフォームに入力することになりました。

コンストラクターまたはデザイナーファイル:

this.DoubleBuffered = true;
this.ResizeRedraw   = true;

Windows関数のオーバーライド:

    const uint WM_NCHITTEST = 0x0084, WM_MOUSEMOVE = 0x0200,
                 HTLEFT = 10, HTRIGHT = 11, HTBOTTOMRIGHT = 17,
                 HTBOTTOM = 15, HTBOTTOMLEFT = 16, HTTOP = 12,
                 HTTOPLEFT = 13, HTTOPRIGHT = 14;
    Size formSize;
    Point screenPoint;
    Point clientPoint;
    Dictionary<uint, Rectangle> boxes;
    const int RHS = 10; // RESIZE_HANDLE_SIZE
    bool handled;

    protected override void WndProc(ref Message m)
    {
        if (this.WindowState == FormWindowState.Maximized)
        {
            base.WndProc(ref m);
            return;
        }

        handled  = false;
        if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
        {
            formSize    = this.Size;
            screenPoint = new Point(m.LParam.ToInt32());
            clientPoint = this.PointToClient(screenPoint);

            boxes = new Dictionary<uint, Rectangle>() {
                {HTBOTTOMLEFT, new Rectangle(0, formSize.Height - RHS, RHS, RHS)},
                {HTBOTTOM, new Rectangle(RHS, formSize.Height - RHS, formSize.Width - 2*RHS, RHS)},
                {HTBOTTOMRIGHT, new Rectangle(formSize.Width - RHS, formSize.Height - RHS, RHS, RHS)},
                {HTRIGHT, new Rectangle(formSize.Width - RHS, RHS, RHS, formSize.Height - 2*RHS)},
                {HTTOPRIGHT, new Rectangle(formSize.Width - RHS, 0, RHS, RHS) },
                {HTTOP, new Rectangle(RHS, 0, formSize.Width - 2*RHS, RHS) },
                {HTTOPLEFT, new Rectangle(0, 0, RHS, RHS) },
                {HTLEFT, new Rectangle(0, RHS, RHS, formSize.Height - 2*RHS) }
            };

            foreach (var hitBox in boxes)
            {
                if (hitBox.Value.Contains(clientPoint))
                {
                    m.Result = (IntPtr)hitBox.Key;
                    handled  = true;
                    break;
                }
            }
        }

        if (!handled)
            base.WndProc(ref m);
    }


    protected override void OnPaint(PaintEventArgs e)
    {
        if (this.WindowState != FormWindowState.Maximized)
        {
            ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor,
                this.ClientSize.Width - 16, this.ClientSize.Height - 16, 16, 16);
        }

        base.OnPaint(e);
    }
1
user7867434