2008-09-20 68 views

回答

6

以下將在XP中工作,我沒有Vista機器方便測試它,但我認爲你的問題源於不正確的HWND。無論如何,與評論不好的代碼。

// The state of our little button 
ButtonState _buttState = ButtonState.Normal; 
Rectangle _buttPosition = new Rectangle(); 

[DllImport("user32.dll")] 
private static extern IntPtr GetWindowDC(IntPtr hWnd); 
[DllImport("user32.dll")] 
private static extern int GetWindowRect(IntPtr hWnd, 
             ref Rectangle lpRect); 
[DllImport("user32.dll")] 
private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); 
protected override void WndProc(ref Message m) 
{ 
    int x, y; 
    Rectangle windowRect = new Rectangle(); 
    GetWindowRect(m.HWnd, ref windowRect); 

    switch (m.Msg) 
    { 
     // WM_NCPAINT 
     case 0x85: 
     // WM_PAINT 
     case 0x0A: 
      base.WndProc(ref m); 

      DrawButton(m.HWnd); 

      m.Result = IntPtr.Zero; 

      break; 

     // WM_ACTIVATE 
     case 0x86: 
      base.WndProc(ref m); 
      DrawButton(m.HWnd); 

      break; 

     // WM_NCMOUSEMOVE 
     case 0xA0: 
      // Extract the least significant 16 bits 
      x = ((int)m.LParam << 16) >> 16; 
      // Extract the most significant 16 bits 
      y = (int)m.LParam >> 16; 

      x -= windowRect.Left; 
      y -= windowRect.Top; 

      base.WndProc(ref m); 

      if (!_buttPosition.Contains(new Point(x, y)) && 
       _buttState == ButtonState.Pushed) 
      { 
       _buttState = ButtonState.Normal; 
       DrawButton(m.HWnd); 
      } 

      break; 

     // WM_NCLBUTTONDOWN 
     case 0xA1: 
      // Extract the least significant 16 bits 
      x = ((int)m.LParam << 16) >> 16; 
      // Extract the most significant 16 bits 
      y = (int)m.LParam >> 16; 

      x -= windowRect.Left; 
      y -= windowRect.Top; 

      if (_buttPosition.Contains(new Point(x, y))) 
      { 
       _buttState = ButtonState.Pushed; 
       DrawButton(m.HWnd); 
      } 
      else 
       base.WndProc(ref m); 

      break; 

     // WM_NCLBUTTONUP 
     case 0xA2: 
      // Extract the least significant 16 bits 
      x = ((int)m.LParam << 16) >> 16; 
      // Extract the most significant 16 bits 
      y = (int)m.LParam >> 16; 

      x -= windowRect.Left; 
      y -= windowRect.Top; 

      if (_buttPosition.Contains(new Point(x, y)) && 
       _buttState == ButtonState.Pushed) 
      { 
       _buttState = ButtonState.Normal; 
       // [[TODO]]: Fire a click event for your button 
       //   however you want to do it. 
       DrawButton(m.HWnd); 
      } 
      else 
       base.WndProc(ref m); 

      break; 

     // WM_NCHITTEST 
     case 0x84: 
      // Extract the least significant 16 bits 
      x = ((int)m.LParam << 16) >> 16; 
      // Extract the most significant 16 bits 
      y = (int)m.LParam >> 16; 

      x -= windowRect.Left; 
      y -= windowRect.Top; 

      if (_buttPosition.Contains(new Point(x, y))) 
       m.Result = (IntPtr)18; // HTBORDER 
      else 
       base.WndProc(ref m); 

      break; 

     default: 
      base.WndProc(ref m); 
      break; 
    } 
} 

private void DrawButton(IntPtr hwnd) 
{ 
    IntPtr hDC = GetWindowDC(hwnd); 
    int x, y; 

    using (Graphics g = Graphics.FromHdc(hDC)) 
    { 
     // Work out size and positioning 
     int CaptionHeight = Bounds.Height - ClientRectangle.Height; 
     Size ButtonSize = SystemInformation.CaptionButtonSize; 
     x = Bounds.Width - 4 * ButtonSize.Width; 
     y = (CaptionHeight - ButtonSize.Height)/2; 
     _buttPosition.Location = new Point(x, y); 

     // Work out color 
     Brush color; 
     if (_buttState == ButtonState.Pushed) 
      color = Brushes.LightGreen; 
     else 
      color = Brushes.Red; 

     // Draw our "button" 
     g.FillRectangle(color, x, y, ButtonSize.Width, ButtonSize.Height); 
    } 

    ReleaseDC(hwnd, hDC); 
} 

private void Form1_Load(object sender, EventArgs e) 
{ 
    _buttPosition.Size = SystemInformation.CaptionButtonSize; 
} 
1

繪畫似乎是比較容易的部分,下面將做到這一點:

[編輯:代碼刪除,請參閱我的其他答案]

真正的問題是不斷變化的狀態,按鈕上檢測點擊...因爲你需要鉤入程序的全局消息處理程序,.NET似乎隱藏了表單的鼠標事件,而不是在實際的容器區域(即鼠標移動並點擊標題欄) 。我正在尋找有關這方面的信息,現在發現它,我正在研究它,不應該太難......如果我們能夠弄清楚這些信息實際上傳遞了什麼。

+0

此代碼在Vista中似乎不起作用。 – 2008-09-20 05:21:56

+0

你能定義「不起作用」嗎?它不識別這些信息,它不是在繪製?它甚至不會編譯? – 2008-09-20 05:47:23

+0

對不起,它運行但繪製的矩形不顯示。 – 2008-09-20 05:54:43

2

我知道它已經很長時間以來的最後一個答案,但這真的幫了我最近,我喜歡更新克里斯提供的代碼與我的意見和修改。 該版本在Win XP和Win 2003上完美運行。在Win 2008上,有一個小錯誤,在調整窗口大小時我無法識別。也適用於Vista(無Aero),但請注意,標題欄按鈕不是正方形,並且按鈕尺寸應考慮到這一點。

switch (m.Msg) 
      { 
       // WM_NCPAINT/WM_PAINT   
       case 0x85: 
       case 0x0A: 
        //Call base method 
        base.WndProc(ref m); 
        //we have 3 buttons in the corner of the window. So first's new button left coord is offseted by 4 widths 
        int crt = 4; 
        //navigate trough all titlebar buttons on the form 
        foreach (TitleBarImageButton crtBtn in titleBarButtons.Values) 
        { 
         //Calculate button coordinates 
         p.X = (Bounds.Width - crt * crtBtn.Size.Width); 
         p.Y = (Bounds.Height - ClientRectangle.Height - crtBtn.Size.Height)/2; 
         //Initialize button and draw 
         crtBtn.Location = p; 
         crtBtn.ButtonState = ImageButtonState.NORMAL; 
         crtBtn.DrawButton(m.HWnd); 
         //increment button left coord location offset 
         crt++; 
        } 
        m.Result = IntPtr.Zero; 
        break; 
       // WM_ACTIVATE  
       case 0x86: 
        //Call base method 
        base.WndProc(ref m); 
        //Draw each button 
        foreach (TitleBarImageButton crtBtn in titleBarButtons.Values) 
        { 
         crtBtn.ButtonState = ImageButtonState.NORMAL; 
         crtBtn.DrawButton(m.HWnd); 
        } 
        break; 
       // WM_NCMOUSEMOVE   
       case 0xA0: 
        //Get current mouse position 
        p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits    
        p.Y = (int)m.LParam >> 16;  // Extract the most significant 16 bits   
        p.X -= windowRect.Left; 
        p.Y -= windowRect.Top; 

        //Call base method 
        base.WndProc(ref m); 

        ImageButtonState newButtonState; 
        foreach (TitleBarImageButton crtBtn in titleBarButtons.Values) 
        { 
         if (crtBtn.HitTest(p)) 
         {//mouse is over the current button 
          if (crtBtn.MouseButtonState == MouseButtonState.PRESSED) 
           //button is pressed - set pressed state 
           newButtonState = ImageButtonState.PRESSED; 
          else 
           //button not pressed - set hoover state 
           newButtonState = ImageButtonState.HOOVER; 
         } 
         else 
         { 
          //mouse not over the current button - set normal state 
          newButtonState = ImageButtonState.NORMAL; 
         } 

         //if button state not modified, do not repaint it. 
         if (newButtonState != crtBtn.ButtonState) 
         { 
          crtBtn.ButtonState = newButtonState; 
          crtBtn.DrawButton(m.HWnd); 
         } 
        } 
        break; 
       // WM_NCLBUTTONDOWN  
       case 0xA1: 
        //Get current mouse position 
        p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits 
        p.Y = (int)m.LParam >> 16;  // Extract the most significant 16 bits  
        p.X -= windowRect.Left; 
        p.Y -= windowRect.Top; 

        //Call base method 
        base.WndProc(ref m); 

        foreach (TitleBarImageButton crtBtn in titleBarButtons.Values) 
        { 
         if (crtBtn.HitTest(p)) 
         { 
          crtBtn.MouseButtonState = MouseButtonState.PRESSED; 
          crtBtn.ButtonState = ImageButtonState.PRESSED; 
          crtBtn.DrawButton(m.HWnd); 
         } 
        } 
        break; 
       // WM_NCLBUTTONUP 
       case 0xA2: 
       case 0x202: 
        //Get current mouse position 
        p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits 
        p.Y = (int)m.LParam >> 16;  // Extract the most significant 16 bits 
        p.X -= windowRect.Left; 
        p.Y -= windowRect.Top; 

        //Call base method 
        base.WndProc(ref m); 
        foreach (TitleBarImageButton crtBtn in titleBarButtons.Values) 
        { 
         //if button is press 
         if (crtBtn.ButtonState == ImageButtonState.PRESSED) 
         { 
          //Rasie button's click event 
          crtBtn.OnClick(EventArgs.Empty); 

          if (crtBtn.HitTest(p)) 
           crtBtn.ButtonState = ImageButtonState.HOOVER; 
          else 
           crtBtn.ButtonState = ImageButtonState.NORMAL; 
         } 

         crtBtn.MouseButtonState = MouseButtonState.NOTPESSED; 
         crtBtn.DrawButton(m.HWnd); 
        } 
        break; 
       // WM_NCHITTEST  
       case 0x84: 
        //Get current mouse position 
        p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits 
        p.Y = (int)m.LParam >> 16;  // Extract the most significant 16 bits 
        p.X -= windowRect.Left; 
        p.Y -= windowRect.Top; 

        bool isAnyButtonHit = false; 
        foreach (TitleBarImageButton crtBtn in titleBarButtons.Values) 
        { 
         //if mouse is over the button, or mouse is pressed 
         //(do not process messages when mouse was pressed on a button) 
         if (crtBtn.HitTest(p) || crtBtn.MouseButtonState == MouseButtonState.PRESSED) 
         { 
          //return 18 (do not process further) 
          m.Result = (IntPtr)18; 
          //we have a hit 
          isAnyButtonHit = true; 
          //return 
          break; 
         } 
         else 
         {//mouse is not pressed and not over the button, redraw button if needed 
          if (crtBtn.ButtonState != ImageButtonState.NORMAL) 
          { 
           crtBtn.ButtonState = ImageButtonState.NORMAL; 
           crtBtn.DrawButton(m.HWnd); 
          } 
         } 
        } 
        //if we have a hit, do not process further 
        if (!isAnyButtonHit) 
         //Call base method 
         base.WndProc(ref m); 
        break; 
       default: 
        //Call base method 
        base.WndProc(ref m); 
        //Console.WriteLine(m.Msg + "(0x" + m.Msg.ToString("x") + ")"); 
        break; 
      } 

該代碼演示了要處理的消息以及如何處理它們。 該代碼使用自定義TitleBarButton對象的集合。這個班級太大而不能包含在這裏,但是如果需要的話,我可以提供這個班級和一個例子。