He estado pensando en esto por un par de días pero creo que me falta una comprensión básica de cómo Windows y wpf trabajan internamente para resolver esto.El marco de la ventana dwm dibujado a mano parpadea al cambiar el tamaño si la ventana contiene un elemento HwndHost
El problema es el siguiente:
creé una ventana que debería dejarme dibujar controles WPF en una barra de título aerodinámico (como Office). Esto funciona bien siempre que no agregue un elemento Hwndhost a la ventana, en este caso cada vez que lo cambio de tamaño, el marco y el HwndHost comienzan a parpadear bastante mal (otros elementos parecen reproducirse correctamente). También traté de usar la implementación de ventana de marco personalizada desde el WPF Shell Integration library y el resultado es el mismo, así que estoy pensando que no es completamente mi culpa.
El siguiente código es un programa simple compilable que reproduce el problema. La muestra está en C# pero la respuesta no tiene que ser así.
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) { }
}
}
¿Alguna vez encontrar soluciones alternativas? – Seth
@Seth No, para ser sincero, dejé de buscar, encontré este ... ¡háganmelo saber si resuelven algo! – Roald