2013-07-19 75 views
12

我正在嘗試使無邊框窗體彈出工具欄。我希望用戶能夠在右下角抓取(「調整大小手柄」),並且可以調整表單的大小,但無法以任何其他方式調整大小或重新定位表單。自定義調整無邊框窗體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); 
} 

難道我做錯了什麼,還是有更好的方法做我想要做什麼?

非常感謝。

+3

我認爲這將是有益的http://stackoverflow.com/questions/2575216/resize-winform-沒有邊界和http://www.codeproject.com/Articles/24005/Resizable-Moveable-Customizable-Borderless-Form –

+1

我在想這個,或者你不能只是改變FormBorderStyle爲Sizeable每當你的用戶鼠標懸停在您的自定義hitbox上,以及FixedSingle何時離開它?處理Rectangle類中的MouseEnter和MouseLeave事件。 –

+0

@AntonSemenov:第二種解決方案繞過操作系統處理程序。第一個是我想要的,但不起作用。除了窗口的邊緣,我沒有收到WM_NCHITTEST消息。 –

回答

11

我一直在尋找類似的東西和安東的代碼是一個偉大的基地。這是我最終調整各方面工作的結果。我不確定Dictionary是存儲hitbox的最佳方式,但我想這並不重要。

而且由於我的表單中充滿了使用Fill作爲Dock參數的控件,所以我只需將一個5px的填充添加到Form就可以很好地工作。

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); 
} 
+0

我無法驗證此作品,因爲我放棄了這種方法,但我確實記得讓我的表格充滿面板。添加表單填充可能會有所不同。 –

2

只是對您的代碼稍作修改。我已經添加了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

安東·謝苗諾夫,我不明白你的代碼。

無論如何,我曾與查爾斯的對
第一代碼中的問題時,我將窗口最大化,然後嘗試改變它的大小 - 它被調整。
之後,我無法再次修復它的正常大小,也沒有最大化與正常的最大按鈕再次。

我做了什麼來解決這個問題是在底部加入了「的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); 
    } 
0

基於查爾斯P.解決方案做了一些修改它,希望它可以幫助別人太:) 小檢查和改進,每次調用Windows消息時不聲明額外的變量。 還檢查窗口狀態最大時不繪製握把錨。 我想創建一個自定義控件,但不幸的是我最終填寫了這個代碼的表單。在設計文件

構造或:

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

重寫窗口功能:

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); 
    }