l marco de ventana dibujado dwm personalizado parpadea al cambiar el tamaño si la ventana contiene un elemento HwndHost

He estado pensando en esto durante un par de días, pero creo que me falta una comprensión básica de cómo Windows y WPF funcionan internamente para resolver esto.

El problema es este:

Creé una ventana que debería permitirme dibujar controles wpf en una barra de título aerodinámica (como office). Esto funciona bien siempre que no agregue un elemento Hwndhost a la ventana, en este caso cada vez que cambie el tamaño del marco y el HwndHost comience a parpadear bastante mal (otros elementos parecen renderizarse correctamente). También intenté usar la implementación de ventana de marco personalizada deWPF Shell Integration library y el resultado es el mismo, así que estoy pensando que no es del todo culpa mía.

El siguiente código es un programa compilable simple que reproduce el problema. La muestra está en c # pero la respuesta no tiene que ser.

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

Respuestas a la pregunta(2)

Su respuesta a la pregunta