2011-06-28 8 views
5

これについて数日間考えていましたが、Windowsとwpfが内部的にどのように動作するかを理解できていないと思います。ウィンドウにHwndHost要素が含まれている場合、サイズ変更時にカスタムdwm描画ウィンドウ枠がちらつく

問題はこれです:

私は私が(オフィスなど)エアロタイトルバーにWPFコントロールを描画させてくださいウィンドウを作成しました。 これは、ウィンドウにHwndhost要素を追加しない限り、この場合はうまく動作します。この場合、フレームサイズを変更し、HwndHostがちらつきを開始する(他の要素は適切にレンダリングされるようです)。私もWPF Shell Integration libraryからのカスタムフレームウィンドウの実装を使ってみました。結果は同じです。だから、それは私のせいではないと思っています。

次のコードは、問題を再現する単純なコンパイル可能なプログラムです。 サンプルはC#にありますが、答えはそうである必要はありません。

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

答えて

0

まあ、私は最終的に修正が見つかりましたが、私はとにかくそれがウィンドウよりも小さい任意の矩形でWM_NCCALCSIZEに応答しても問題が解決していることが判明し...黒魔術で、この1つの国境を考えます。 例えば、以下のようにハンドラを変更すると、ちらつきがなくなります!

  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; 

私はなぜこれが動作していると私は正気ソリューションが存在することを確信しているので、誰かが私を啓発することができれば、私は幸せになるだろう見当がつかない。

+0

あなたは今までどんな代替ソリューションを見つけましたとき、私はスタイルとしてWS_CLIPCHILDRENを追加することによってそれを解決? – Seth

+0

@Sethいいえ、正直言って私はこの1つを見つけたのを見て止めました。あなたが何かを見つけたら教えてください! – Roald

2

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); 

} 
関連する問題