1. 程式人生 > >WPF無邊框捕獲訊息改變視窗大小

WPF無邊框捕獲訊息改變視窗大小

原文: WPF無邊框捕獲訊息改變視窗大小

文章大部分轉載自http://blog.csdn.net/fwj380891124,如有問題,請聯絡刪除


 最近一直在學習 WPF,看著別人做的WPF程式那麼漂亮,眼紅啊~ 很多漂亮的程式都是無邊框的。於是無邊框視窗操作就是最重要的了。無邊框視窗的操作一直以來相關的資料就很少。WPF 下的就更少了,有的大多是無邊框窗體的移動。在得到群裡高人的指點,再查了一些資料之後,終於把問題解決了。

      廢話不多說,直接來看看如何實現吧!其實現原理很簡單:攔截並處理 Windows 訊息:WM_NCHITTEST。

      WPF 處理 Windows 訊息的模式和 WinForm 不一樣了。Window 類裡沒有 WndProc 函數了,想要擷取 Windows 訊息必須藉助 HwndSource 新增 Hook。

  1. protected override void OnSourceInitialized(EventArgs e)  
  2. {  
  3.     base.OnSourceInitialized(e);  
  4.     HwndSource hwndSource = PresentationSource.FromVisual(thisas HwndSource;  
  5.     if (hwndSource != null)  
  6.     {  
  7.         hwndSource.AddHook(new
     HwndSourceHook(this.WndProc));  
  8.     }  
  9. }  
  10.   
  11. protected virtual IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)  
  12. {  
  13.     return IntPtr.Zero;  
  14. }  

    OK,WndProc 註冊完成之後就可以通過 WndProc 函式完成對Windows訊息的處理了。可以發現,這裡的 WndProc 和標準的 Win32 訊息迴圈很像,只是多了一個 ref bool handled 引數,對於該引數MSDN是這樣說明的: 指示該訊息是否已處理的值。如果該訊息已處理,請將值設定為 true;否則請將其設定為 false  在下面我們將會使用到這個引數數。
  1. private const int WM_NCHITTEST = 0x0084;  
  2. private readonly int agWidth = 12; //拐角寬度  
  3. private readonly int bThickness = 4; // 邊框寬度  
  4. private Point mousePoint = new Point(); //滑鼠座標  
  5.    
  6. protected virtual IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)  
  7. {  
  8.     switch (msg)  
  9.     {  
  10.         case WM_NCHITTEST:  
  11.         this.mousePoint.X = (lParam.ToInt32() &0xFFFF);  
  12.         this.mousePoint.Y = (lParam.ToInt32() >> 16);  
  13.   
  14.         測試滑鼠位置#region 測試滑鼠位置  
  15.   
  16.          // 視窗左上角  
  17.          if (this.mousePoint.Y - this.Top <= this.agWidth   
  18.             && this.mousePoint.X - this.Left <= this.agWidth)   
  19.         {  
  20.             handled = true;  
  21.             return new IntPtr((int)HitTest.HTTOPLEFT);  
  22.         }  
  23.         // 視窗左下角      
  24.          else if (this.ActualHeight + this.Top - this.mousePoint.Y <= this.agWidth   
  25.             && this.mousePoint.X - this.Left <= this.agWidth)  
  26.         {  
  27.             handled = true;  
  28.             return new IntPtr((int)HitTest.HTBOTTOMLEFT);  
  29.         }  
  30.         // 視窗右上角  
  31.          else if (this.mousePoint.Y - this.Top <= this.agWidth   
  32.             && this.ActualWidth + this.Left - this.mousePoint.X <= this.agWidth)  
  33.         {  
  34.             handled = true;  
  35.             return new IntPtr((int)HitTest.HTTOPRIGHT);  
  36.         }  
  37.         // 視窗右下角  
  38.          else if (this.ActualWidth + this.Left - this.mousePoint.X <= this.agWidth   
  39.             && this.ActualHeight + this.Top - this.mousePoint.Y <= this.agWidth)  
  40.         {  
  41.             handled = true;  
  42.             return new IntPtr((int)HitTest.HTBOTTOMRIGHT);  
  43.         }  
  44.         // 視窗左側  
  45.          else if (this.mousePoint.X - this.Left <= this.bThickness)  
  46.         {  
  47.             handled = true;  
  48.             return new IntPtr((int)HitTest.HTLEFT);  
  49.         }  
  50.         // 視窗右側  
  51.          else if (this.ActualWidth + this.Left - this.mousePoint.X <= this.bThickness)  
  52.         {  
  53.             handled = true;  
  54.             return new IntPtr((int)HitTest.HTRIGHT);  
  55.         }  
  56.         // 視窗上方  
  57.          else if (this.mousePoint.Y - this.Top <= this.bThickness)  
  58.         {  
  59.             handled = true;  
  60.             return new IntPtr((int)HitTest.HTTOP);  
  61.         }  
  62.         // 視窗下方  
  63.          else if (this.ActualHeight + this.Top - this.mousePoint.Y <= this.bThickness)  
  64.         {  
  65.             handled = true;  
  66.             return new IntPtr((int)HitTest.HTBOTTOM);  
  67.         }  
  68.         else // 視窗移動  
  69.         {  
  70.             handled = true;  
  71.             return new IntPtr((int)HitTest.HTCAPTION);  
  72.         }  
  73.         #endregion  
  74.     }  
  75.     return IntPtr.Zero;  
  76. }  

     從上面的程式碼可以看出,工作原理很簡單:擷取 WM_NCHITTEST 訊息,獲得滑鼠座標,再在你希望的地方返回不同的訊息以模擬滑鼠的狀態即可。需要注意的是,返回訊息之前必須將handled 設為 true。告訴系統你已經處理過該訊息,不然無效果。

    關於 HitTest 是自定義的列舉類,裡面包含了滑鼠的各種訊息。

  1. 1public enum HitTest:int  
  2. {  
  3.     HTERROR = -2,  
  4.     HTTRANSPARENT = -1,  
  5.     HTNOWHERE = 0,  
  6.     HTCLIENT = 1,  
  7.     HTCAPTION = 2,  
  8.     HTSYSMENU = 3,  
  9.     HTGROWBOX = 4,  
  10.     HTSIZE = HTGROWBOX,  
  11.     HTMENU = 5,  
  12.     HTHSCROLL = 6,  
  13.     HTVSCROLL = 7,  
  14.     HTMINBUTTON = 8,  
  15.     HTMAXBUTTON = 9,  
  16.     HTLEFT = 10,  
  17.     HTRIGHT = 11,  
  18.     HTTOP = 12,  
  19.     HTTOPLEFT = 13,  
  20.     HTTOPRIGHT = 14,  
  21.     HTBOTTOM = 15,  
  22.     HTBOTTOMLEFT = 16,  
  23.     HTBOTTOMRIGHT = 17,  
  24.     HTBORDER = 18,  
  25.     HTREDUCE = HTMINBUTTON,  
  26.     HTZOOM = HTMAXBUTTON,  
  27.     HTSIZEFIRST = HTLEFT,  
  28.     HTSIZELAST = HTBOTTOMRIGHT,  
  29.     HTOBJECT = 19,  
  30.     HTCLOSE = 20,  
  31.     HTHELP = 21,  
  32. }  
  33.  HTBORDER 在不具有可變大小邊框的視窗的邊框上。 
    · HTBOTTOM 在視窗的水平邊框的底部。 
    · HTBOTTOMLEFT 在視窗邊框的左下角。  
    · HTBOTTOMRIGHT 在視窗邊框的右下角。  
    · HTCAPTION 在標題條中。  
    · HTCLIENT 在客戶區中。  
    · HTERROR 在螢幕背景或視窗之間的分隔線上(與HTNOWHERE相同,除了Windows的DefWndProc函式產生一個系統響聲以指明錯誤)。  
    · HTGROWBOX 在尺寸框中。  
    · HTHSCROLL 在水平滾動條上。  
    · HTLEFT 在視窗的左邊框上。  
    · HTMAXBUTTON 在最大化按鈕上。  
    · HTMENU 在選單區域。  
    · HTMINBUTTON 在最小化按鈕上。  
    · HTNOWHERE 在螢幕背景或視窗之間的分隔線上。  
    · HTREDUCE 在最小化按鈕上。  
    · HTRIGHT 在視窗的右邊框上。  
    · HTSIZE 在尺寸框中。(與HTGROWBOX相同)  
    · HTSYSMENU 在控制選單或子視窗的關閉按鈕上。  
    · HTTOP 在視窗水平邊框的上方。  
    · HTTOPLEFT 在視窗邊框的左上角。  
    · HTTOPRIGHT 在視窗邊框的右上角。  
    · HTTRANSPARENT 在一個被其它視窗覆蓋的視窗中。  
    · HTVSCROLL 在垂直滾動條中。  
    · HTZOOM 在最大化按鈕上。