2011-06-28 7 views
5

Myślałem o tym przez kilka dni, ale myślę, że brakuje mi podstawowej wiedzy o tym, jak okna i wpf działają wewnętrznie, aby to zrozumieć.Niestandardowa ramka okna wyciągnięta z dwm migocze podczas zmiany rozmiaru, jeśli okno zawiera element HwndHost

Problem jest taki:

stworzyłem okno, które powinny pozwolić mi zwrócić WPF formantów na pasku tytułowym aero (jak biura). Działa to dobrze, dopóki nie dodaję elementu Hwndhost do okna, w tym przypadku za każdym razem, gdy zmieniam rozmiar ramki i HwndHost zacznie migotać całkiem źle (inne elementy wydają się renderować poprawnie). Próbowałem również użyć niestandardowej implementacji okna ramki od WPF Shell Integration library, a wynik jest taki sam, więc myślę, że to nie do końca moja wina.

Poniższy kod jest prostym programem do kompilacji, który odtwarza problem. Próbka jest w języku C#, ale odpowiedź nie musi być.

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) { } 
    } 
} 

Odpowiedz

0

Cóż, w końcu znalazłem poprawkę, ale myślę, że ten granice na czarnej magii ... W każdym razie okazuje się, że odpowiadając na WM_NCCALCSIZE z dowolnym rect mniejszej niż okna rozwiązuje ten problem. Przykładowo, zmiana programu obsługi tak, aby wyglądała jak poniżej, usuwa migotanie!

  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; 

Nie mam pojęcia, dlaczego to działa, i jestem pewien, że istnieje rozwiązanie, więc będę szczęśliwy, jeśli ktoś mnie oświeci.

+0

Czy kiedykolwiek znaleźliśmy żadnych alternatywnych rozwiązań? – Seth

+0

@Seth No ale szczerze mówiąc przestałem się troszczyć, znalazłem ten ... daj mi znać, jeśli coś wymyślisz! – Roald

2

ja rozwiązałem dodając WS_CLIPCHILDREN jako styl gdy 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); 

} 
Powiązane problemy