2015-07-11 48 views
1

我有我需要調整一個無國界的winform,我想辦法做這種方式:如何使用窗體邊框上的控件修改無邊界窗體的大小?

protected override void WndProc(ref Message m) 
    { 
     const int wmNcHitTest = 0x84; 
     const int htLeft = 10; 
     const int htRight = 11; 
     const int htTop = 12; 
     const int htTopLeft = 13; 
     const int htTopRight = 14; 
     const int htBottom = 15; 
     const int htBottomLeft = 16; 
     const int htBottomRight = 17; 

     if (m.Msg == wmNcHitTest) 
     { 
      Console.Write(true + "\n"); 
      int x = (int)(m.LParam.ToInt64() & 0xFFFF); 
      int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16); 
      Point pt = PointToClient(new Point(x, y)); 
      Size clientSize = ClientSize; 
      ///allow resize on the lower right corner 
      if (pt.X >= clientSize.Width - 16 && pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16) 
      { 
       m.Result = (IntPtr)(IsMirrored ? htBottomLeft : htBottomRight); 
       return; 
      } 
      ///allow resize on the lower left corner 
      if (pt.X <= 16 && pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16) 
      { 
       m.Result = (IntPtr)(IsMirrored ? htBottomRight : htBottomLeft); 
       return; 
      } 
      ///allow resize on the upper right corner 
      if (pt.X <= 16 && pt.Y <= 16 && clientSize.Height >= 16) 
      { 
       m.Result = (IntPtr)(IsMirrored ? htTopRight : htTopLeft); 
       return; 
      } 
      ///allow resize on the upper left corner 
      if (pt.X >= clientSize.Width - 16 && pt.Y <= 16 && clientSize.Height >= 16) 
      { 
       m.Result = (IntPtr)(IsMirrored ? htTopLeft : htTopRight); 
       return; 
      } 
      ///allow resize on the top border 
      if (pt.Y <= 16 && clientSize.Height >= 16) 
      { 
       m.Result = (IntPtr)(htTop); 
       return; 
      } 
      ///allow resize on the bottom border 
      if (pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16) 
      { 
       m.Result = (IntPtr)(htBottom); 
       return; 
      } 
      ///allow resize on the left border 
      if (pt.X <= 16 && clientSize.Height >= 16) 
      { 
       m.Result = (IntPtr)(htLeft); 
       return; 
      } 
      ///allow resize on the right border 
      if (pt.X >= clientSize.Width - 16 && clientSize.Height >= 16) 
      { 
       m.Result = (IntPtr)(htRight); 
       return; 
      } 
     } 
     else 
     { 
      Console.Write(false + "\n"); 
     } 
     base.WndProc(ref m); 
    } 

的問題是,有關於我的表單的左,右邊框控件,所以調整大小控用於上面的代碼不適用於那些有任何類型控件的區域。

下面是一個例子:

Resize problem

在上圖中可以看到顯着的區域內的標籤上我的表格的左邊框和它不會讓我調整其大小。

有沒有辦法解決這個問題?

+0

一個想法是刪除按鈕並在代碼中重新創建它。雖然不是最乾淨的方法... – SteveFerg

+1

你錯過了@SteveFerg這個問題的觀點。在**運行時**時,標籤會捕捉鼠標消息,以便用戶無法在表單邊緣調整表單的大小(標籤)時調整表單的大小。由於鼠標位於標籤上,表單不會得到非客戶區域命中測試消息... –

回答

1

這裏的問題是它是獲取鼠標通知的Label控件,而不是無邊界形式。到目前爲止,解決這個問題的最好方法是使標籤對鼠標透明。你已經知道如何做,WM_NCHITTEST也允許返回HTTRANSPARENT。 Windows一直在尋找通知的下一個候選對象,它將成爲標籤的父對象。

特別容易讓標籤做,因爲你通常沒有在所有的鼠標事件的任何使用:

using System; 
using System.Windows.Forms; 

public class LabelEx : Label { 
    protected override void WndProc(ref Message m) { 
     const int wmNcHitTest = 0x84; 
     const int htTransparent = -1; 
     if (!DesignMode && m.Msg == wmNcHitTest) m.Result = new IntPtr(htTransparent); 
     else base.WndProc(ref m); 
    } 
} 

適用於任何控制類,你會想,如果它更具有選擇性是一個按鈕。可能是你需要的所有東西,但是如果你在靠近邊緣的地方有很多不同類型的控件,它仍然很尷尬。您可以使用的另一種技術在本地Windows編程中稱爲「子分類」。在Winforms中通用,爲本地Windows控件創建包裝.NET類。它運作良好,在這裏也一樣,你可以在任何控制和攔截WM_NCHITTEST這樣的消息有偷看:

const int edge = 16; 

    class MouseFilter : NativeWindow { 
     private Form form; 
     public MouseFilter(Form form, Control child) { 
      this.form = form; 
      this.AssignHandle(child.Handle); 
     } 
     protected override void WndProc(ref Message m) { 
      const int wmNcHitTest = 0x84; 
      const int htTransparent = -1; 

      if (m.Msg == wmNcHitTest) { 
       var pos = new Point(m.LParam.ToInt32()); 
       if (pos.X < this.form.Left + edge || 
        pos.Y < this.form.Top + edge|| 
        pos.X > this.form.Right - edge || 
        pos.Y > this.form.Bottom - edge) { 
        m.Result = new IntPtr(htTransparent); 
        return; 
       } 
      } 
      base.WndProc(ref m); 
     } 
    } 

而剛剛創造了每一個獲得靠近窗口邊緣控制MouseFilter實例:

protected override void OnLoad(EventArgs e) { 
     base.OnLoad(e); 
     subClassChildren(this.Controls); 
    } 

    private void subClassChildren(Control.ControlCollection ctls) { 
     foreach (Control ctl in ctls) { 
      var rc = this.RectangleToClient(this.RectangleToScreen(ctl.DisplayRectangle)); 
      if (rc.Left < edge || rc.Right > this.ClientSize.Width - edge || 
       rc.Top < edge || rc.Bottom > this.ClientSize.Height - edge) { 
       new MouseFilter(this, ctl); 
      } 
      subClassChildren(ctl.Controls); 
     } 
    } 
+0

快速問題Hans:在您的MouseFilter類中,我們是否真的需要捕獲'WM_NCDESTROY'並手動調用'ReleaseHandle ()'?從[備註](https://msdn.microsoft.com/en-us/library/system.windows.forms.nativewindow.releasehandle(v = vs.110).aspx)中可以看到,「一個窗口會自動調用這個方法,如果它接收到本機Win32 WM_NCDESTROY消息,則表示Windows已銷燬該句柄。「那麼,是否爲''base.WndProc(ref m);'被執行了嗎? –

+0

默認處理中有FUD,NativeWindow.Callback()調用ReleaseHandle(false),它不會取消窗口的子類。同意,可能在這裏更好。 –