2012-03-22 44 views
1

我已經使用WPF Shell集成庫(http://archive.msdn.microsoft.com/WPFShell)實現了自定義窗口鑲邊。當ResizeMode設置爲NoResize時,系統關閉自定義窗口鑲邊中的按鈕表面

Chrome的工作原理是將ReSizeMode設置爲NoResize。然後,如果將鼠標懸停在關閉按鈕上,我注意到自定義鉻合金關閉按鈕下方顯示了基礎系統關閉按鈕。 Custom Chrome Button partially hides System Close Button

預期的行爲是基本關閉按鈕應該永遠不會顯示。如果我移動窗口或在桌面上選擇另一個窗口並回到這個窗口,我注意到系統關閉按鈕再次隱藏。 所以可能基本上是當(1)應用程序第一次啓動時和(2)當ResizeMode = NoResize時。

我的第一個懷疑是我們如何處理Custom Chrome中的WM.NCHITTEST。如果我修改這個函數返回HTCLient,那麼這個問題就解決了。但是,我失去了拖放功能,並且右鍵單擊標題欄。

這是WindowsChromeWorker類的WM.NCHITTEST消息處理程序的代碼。

private IntPtr _HandleNCHitTest(WM uMsg, IntPtr wParam, IntPtr lParam, out bool handled) { 
     IntPtr lRet = IntPtr.Zero; 
     handled = false; 

     // Give DWM a chance at this first. 
     if (Utility.IsOSVistaOrNewer && _chromeInfo.GlassFrameThickness != default(Thickness) && _isGlassEnabled) { 
      // If we're on Vista, give the DWM a chance to handle the message first. 
      handled = NativeMethods.DwmDefWindowProc(_hwnd, uMsg, wParam, lParam, out lRet); 
     } 


     // Handle letting the system know if we consider the mouse to be in our effective non-client area. 
     // If DWM already handled this by way of DwmDefWindowProc, then respect their call. 
     if (IntPtr.Zero == lRet) { 
      var mousePosScreen = new Point(Utility.GET_X_LPARAM(lParam), Utility.GET_Y_LPARAM(lParam)); 
      Rect windowPosition = _GetWindowRect(); 

      HT ht = _HitTestNca(
       DpiHelper.DeviceRectToLogical(windowPosition), 
       DpiHelper.DevicePixelsToLogical(mousePosScreen)); 

      // Don't blindly respect HTCAPTION. 
      // We want UIElements in the caption area to be actionable so run through a hittest first. 
      if (ht != HT.CLIENT) { 
       Point mousePosWindow = mousePosScreen; 
       mousePosWindow.Offset(-windowPosition.X, -windowPosition.Y); 
       mousePosWindow = DpiHelper.DevicePixelsToLogical(mousePosWindow); 
       IInputElement inputElement = _window.InputHitTest(mousePosWindow); 
       if (inputElement != null && WindowChrome.GetIsHitTestVisibleInChrome(inputElement)) { 
        ht = HT.CLIENT; 
       } 
      } 
      //handled = false; 
      handled = true; 
      lRet = new IntPtr((int)ht); 
     } 
     return lRet; 
    } 

    private static readonly HT[,] _HitTestBorders = new[,] 
    { 
     { HT.TOPLEFT, HT.TOP,  HT.TOPRIGHT }, 
     { HT.LEFT,  HT.CLIENT, HT.RIGHT  }, 
     { HT.BOTTOMLEFT, HT.BOTTOM, HT.BOTTOMRIGHT }, 
    }; 

    private HT _HitTestNca(Rect windowPosition, Point mousePosition) { 
     // Determine if hit test is for resizing, default middle (1,1). 
     int uRow = 1; 
     int uCol = 1; 
     bool onResizeBorder = false; 

     //if (_window.ResizeMode == ResizeMode.NoResize) 
      // _chromeInfo.ResizeBorderThickness = new Thickness(0); 

     // Determine if the point is at the top or bottom of the window. 
     if (mousePosition.Y >= windowPosition.Top && mousePosition.Y < windowPosition.Top + _chromeInfo.ResizeBorderThickness.Top + _chromeInfo.CaptionHeight) { 
      onResizeBorder = (mousePosition.Y < (windowPosition.Top + _chromeInfo.ResizeBorderThickness.Top)); 
      uRow = 0; // top (caption or resize border) 
     } else if (mousePosition.Y < windowPosition.Bottom && mousePosition.Y >= windowPosition.Bottom - (int)_chromeInfo.ResizeBorderThickness.Bottom) { 
      uRow = 2; // bottom 
     } 

     // Determine if the point is at the left or right of the window. 
     if (mousePosition.X >= windowPosition.Left && mousePosition.X < windowPosition.Left + (int)_chromeInfo.ResizeBorderThickness.Left) { 
      uCol = 0; // left side 
     } else if (mousePosition.X < windowPosition.Right && mousePosition.X >= windowPosition.Right - _chromeInfo.ResizeBorderThickness.Right) { 
      uCol = 2; // right side 
     } 

     // If the cursor is in one of the top edges by the caption bar, but below the top resize border, 
     // then resize left-right rather than diagonally. 
     if (uRow == 0 && uCol != 1 && !onResizeBorder) { 
      uRow = 1; 
     } 

     HT ht = _HitTestBorders[uRow, uCol]; 

     if (ht == HT.TOP && !onResizeBorder) { 
      ht = HT.CAPTION; 
     } 

     return ht; 
    } 

任何想法如何解決這個問題?

非常感謝, 阿瓊

回答

2

所有rite..I也沒搞清楚這個問題了。

首先,我最初的猜疑是錯誤的。 WM.NCHITTEST消息的處理不是不正確。這真的是一個窗口樣式的問題。

最好的解決方案是隱藏系統關閉按鈕,讓Chrome關閉按鈕完成其工作。但是在網上找到的解決方案,即在窗口樣式http://winsharp93.wordpress.com/2009/07/21/wpf-hide-the-window-buttons-minimize-restore-and-close-and-the-icon-of-a-window/中切換SYSMENU位標誌在我的情況下不起作用。

關閉按鈕隱藏,但設置爲NoResize的ResizeMode的inspite,我注意到,無論是大小調整光標和調整菜單{最大值\最小值\還原}已啓用。

試錯的幾個小時後,我來到了這一段代碼:

//This property descriptor is used to hook-onto the resizemode change notification 

     private void Window_Loaded(object sender, System.Windows.RoutedEventArgs e) 
     { 
     // When the default handling of ResizeMode = NoResize causes problems - this is why custom handling is required. 
    System.ComponentModel.DependencyPropertyDescriptor _resizeModePropertyDescriptor; 

    _resizeModePropertyDescriptor = System.ComponentModel.DependencyPropertyDescriptor.FromProperty(Window.ResizeModeProperty, 
      typeof(Window)); 
     _resizeModePropertyDescriptor.AddValueChanged(this._window, new EventHandler(this._Window_ResizeModePropertyChanged)); 
     } 

    /// <summary> 
    /// This property change handler only reacts when the ReSizeMode is set to NoResize. 
    /// In the default window style when Resize = NoResize, causes the underlying system close button to show up under the Chrome Close Button. 
    /// This is a fix to handle that problem. [ Refer to defect #5134 for further details. 
    /// </summary> 
    /// <param name="sender">ChromeWorker object</param> 
    /// <param name="e">Event Args - Not really used</param> 
    private void _Window_ResizeModePropertyChanged(object sender, EventArgs e) 
    { 
     if (_window.ResizeMode == ResizeMode.NoResize) 
     { 
      //Got these styles merely by trial and error. 
      _ModifyStyle(
       WS.SYSMENU | WS.DLGFRAME | WS.BORDER | WS.CLIPCHILDREN | WS.CLIPSIBLINGS, //Styles to Remove 
       WS.POPUPWINDOW);               //Style to Add 
     } 
    } 

    /// <summary>Add and remove a native WindowStyle from the HWND.</summary> 
    /// <param name="removeStyle">The styles to be removed. These can be bitwise combined.</param> 
    /// <param name="addStyle">The styles to be added. These can be bitwise combined.</param> 
    /// <returns>Whether the styles of the HWND were modified as a result of this call.</returns> 
    private bool _ModifyStyle(WS removeStyle, WS addStyle) { 
     Assert.IsNotDefault(_hwnd); 
     var dwStyle = (WS)NativeMethods.GetWindowLongPtr(_hwnd, GWL.STYLE).ToInt32(); 
     var dwNewStyle = (dwStyle & ~removeStyle) | addStyle; 
     if (dwStyle == dwNewStyle) { 
      return false; 
     } 

     NativeMethods.SetWindowLongPtr(_hwnd, GWL.STYLE, new IntPtr((int)dwNewStyle)); 
     return true; 
    } 

您還可以使用SourceInitialized事件的wireup,但我還沒有真正測試過。 那就是它!這就是爲期一週的探索Window Chromes!

我不得不承認我有點好奇 - 沃金的WPF這麼久didnt讓我認識到,有win32的外面的整個世界這是更爲強大的

(和令人沮喪的!)
相關問題