2011-06-28 23 views
5

Tôi đã suy nghĩ về điều này trong một vài ngày nhưng tôi nghĩ rằng tôi thiếu một số hiểu biết cơ bản về cách windows và wpf làm việc trong nội bộ để tìm ra điều này.Tùy chỉnh kích thước cửa sổ được vẽ trên các phần tử HwndHost

Vấn đề là thế này:

Tôi tạo ra một cửa sổ mà nên hãy để tôi vẽ điều khiển WPF trên thanh tiêu đề aero (như văn phòng). Điều này làm việc tốt miễn là tôi không thêm phần tử Hwndhost vào cửa sổ, trong trường hợp này bất cứ khi nào tôi thay đổi kích cỡ khung và HwndHost bắt đầu nhấp nháy khá nặng (các yếu tố khác có vẻ hiển thị chính xác). Tôi cũng đã thử sử dụng triển khai khung cửa sổ tùy chỉnh từ WPF Shell Integration library và kết quả là như nhau, vì vậy tôi nghĩ rằng đó không hoàn toàn là lỗi của tôi.

Mã sau đây là một chương trình đơn giản có thể biên dịch được để tái tạo sự cố. Mẫu thử trong C# nhưng câu trả lời không nhất thiết phải như vậy.

using System; 
using System.Runtime.InteropServices; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Interop; 
using System.Windows.Media; 
using System.Windows.Threading; 

namespace DwmTest { 
    class Program { 
     [STAThread] 
     static void Main() { 
      var w = new CustomFrameWindow{ Content = new WindowHost() }; 
      w.Show(); 
      ((Border)VisualTreeHelper.GetChild(w, 0)).Margin = new Thickness(11, 33, 11, 11); 
      Dispatcher.Run(); 
     } 
    } 

    public class CustomFrameWindow : Window { 

     const int resizeFrameWidth = 11; 
     const int captionHeight = 33; 

     public enum HT { CLIENT = 1, CAPTION = 2, LEFT = 10, RIGHT, TOP, TOPLEFT, TOPRIGHT, BOTTOM, BOTTOMLEFT, BOTTOMRIGHT } 

     [StructLayout(LayoutKind.Sequential)] 
     public struct Margins { public int left, right, top, bottom; } 

     [DllImport("user32.dll")] 
     public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int flags); 

     [DllImport("dwmapi.dll")] 
     public static extern bool DwmDefWindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, out IntPtr result); 

     [DllImport("dwmapi.dll", PreserveSig = false)] 
     public static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref Margins pMarInset); 

     protected override void OnSourceInitialized(EventArgs e) { 
      base.OnSourceInitialized(e); 

      var hWndSource = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle); 
      hWndSource.CompositionTarget.BackgroundColor = Colors.Transparent; 

      var nonClientArea = new Margins{ 
       left = resizeFrameWidth, top = captionHeight, bottom = resizeFrameWidth, right = resizeFrameWidth 
      }; 
      DwmExtendFrameIntoClientArea(hWndSource.Handle, ref nonClientArea); 

      hWndSource.AddHook(WndProc); 

      // FRAMECHANGED | NOMOVE | NOSIZE 
      SetWindowPos(hWndSource.Handle, new IntPtr(), 0, 0, 0, 0, 0x0020 | 0x0002 | 0x0001); 
     } 

     private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { 

      switch(msg) { 
       case 0x0083: // NCCALCSIZE 
        if(wParam != IntPtr.Zero) handled = true; 
        break; 
       case 0x0084: // NCHITTEST 
        handled = true; 

        IntPtr dwmHitTest; 
        if(DwmDefWindowProc(hwnd, msg, wParam, lParam, out dwmHitTest)) { 
         return dwmHitTest; 
        } 

        var mousePosition = PointFromScreen(new Point(lParam.ToInt32() & 0xFFFF, lParam.ToInt32() >> 16)); 

        var isTop = mousePosition.Y <= resizeFrameWidth; 
        var isBottom = mousePosition.Y >= ActualHeight - resizeFrameWidth; 
        var isLeft = mousePosition.X <= resizeFrameWidth; 
        var isRight = mousePosition.X >= ActualWidth - resizeFrameWidth; 

        var hitTest = HT.CLIENT; 
        if(isTop) { 
         if(isLeft) hitTest = HT.TOPLEFT; 
         else if(isRight) hitTest = HT.TOPRIGHT; 
         else hitTest = HT.TOP; 
        } 
        else if(isBottom) { 
         if(isLeft) hitTest = HT.BOTTOMLEFT; 
         else if(isRight) hitTest = HT.BOTTOMRIGHT; 
         else hitTest = HT.BOTTOM; 
        } 
        else if(isLeft) hitTest = HT.LEFT; 
        else if(isRight) hitTest = HT.RIGHT; 
        else if(mousePosition.Y <= captionHeight) hitTest = HT.CAPTION; 

        return new IntPtr((int)hitTest); 
      } 
      return IntPtr.Zero; 
     } 
    } 

    public class WindowHost : HwndHost { 
     [DllImport("user32.dll", SetLastError = true)] 
     static extern IntPtr CreateWindowEx(IntPtr exStyle, string lpClassName,string lpWindowName,int dwStyle,int x,int y,int nWidth,int nHeight,IntPtr hWndParent,IntPtr hMenu,IntPtr hInstance,IntPtr lpParam); 

     protected override HandleRef BuildWindowCore(HandleRef hWndParent) { 
      return new HandleRef(this, CreateWindowEx(IntPtr.Zero, "static", "", 0x40000000, 0, 0, 200, 200, hWndParent.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)); 
     } 
     protected override void DestroyWindowCore(HandleRef hwnd) { } 
    } 
} 

Trả lời

0

Vâng, cuối cùng tôi tìm thấy một sửa chữa nhưng tôi nghĩ rằng đây là một biên giới trên ma thuật đen ... anyway nó chỉ ra rằng ứng phó với WM_NCCALCSIZE với bất kỳ rect nhỏ hơn so với cửa sổ giải quyết vấn đề. Vì vậy, ví dụ như việc thay đổi trình xử lý trông giống như dưới đây sẽ loại bỏ nhấp nháy!

  case WM.NCCALCSIZE: 
       if(wParam != IntPtr.Zero) { 
        handled = true; 
        var client = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT)); 
        client.Bottom -= 1; 
        Marshal.StructureToPtr(client, lParam, false); 
       } 
       break; 

Tôi không biết tại sao điều này làm việc và tôi chắc chắn rằng giải pháp saner tồn tại, vì vậy tôi rất vui nếu ai đó có thể khai sáng cho tôi.

+0

Bạn đã bao giờ tìm thấy bất kỳ giải pháp thay thế? – Seth

+0

@Seth Không, nhưng thành thật mà nói tôi đã ngừng tìm kiếm sau khi tôi tìm thấy điều này .. cho tôi biết nếu bạn tìm ra một cái gì đó! – Roald

2

tôi giải quyết nó bằng cách thêm WS_CLIPCHILDREN như một phong cách riêng khi CreatWindowEx

protected override HandleRef BuildWindowCore(HandleRef hwndParent) 
{ 
    _hwndHost = Win32Api.CreateWindowEx(0, "Static", "", 
         (int) (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN), 
         0, 0, 
         _hostWidth, _hostHeight, 
         hwndParent.Handle, 
         IntPtr.Zero, 
         IntPtr.Zero, 
         0); 

} 
Các vấn đề liên quan