HandyControl icon indicating copy to clipboard operation
HandyControl copied to clipboard

Windows 11 Support for Window Control

Open ghost1372 opened this issue 2 years ago • 7 comments

These are the problems that exist right now

  • [ ] GlowWindow CornerRadius
  • [ ] Windows 11 Glow Window Support (Show accent color on window borders)
  • [x] ~~Snap Layout Support~~

GlowWindow CornerRadius

GlowWindow should use the CornerRadius here is some docs

image

Windows 11 Glow Window Support (Show accent color on window borders)

In Windows 11, by activating enable accent color on title bars and windows borders, All windows display the accent color in their borders. Hc:Window, GlowWindow, BlurWindow must support it.

Untitled

ghost1372 avatar Oct 30 '21 10:10 ghost1372

I did some research and I can say I found a solution for all. I am uploading a demo. All these issues have been resolved in this demo. But the demo is a bit vague for me, so I can not send a pr.

To display SnapLayout, we need to add an Attached property to the Maximum button In the demo it is like this controlzEx:NonClientControlProperties.HitTestResult="MAXBUTTON" then we need to handle WM_NCHITTEST. 3

for fixing glowWindow CornerRadius and Showing accent color on window borders we need to use something called PreferDWMBorderColor

4

Demo.zip

ghost1372 avatar Oct 30 '21 10:10 ghost1372

I finally found a way to show SnapLayout😁, but another problem...😕

00

ghost1372 avatar Nov 01 '21 13:11 ghost1372

My Example:

public class Windows11Snap : Window
{
	public Windows11Snap()
	{
		WindowStyle = System.Windows.WindowStyle.None;
		_btnMax = new Button { Content = "MaxButton", Width = 100, Height = 100, Margin = new Thickness(5) };
		var _btnMisc = new Button { Content = "MiscControl", Width = 100, Height = 100, Margin = new Thickness(5) };
		var p = new StackPanel { };
		p.Orientation = Orientation.Horizontal;
		p.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
		p.Children.Add(_btnMax);
		p.Children.Add(_btnMisc);
		Content = p;
		this.SourceInitialized += (sender, args) =>
		{
			IntPtr hwnd = new WindowInteropHelper(this).Handle;
			HwndSource.FromHwnd(hwnd).AddHook(new HwndSourceHook(HwndSourceHook));
		};
		MouseLeftButtonDown += (sender, args) =>
		{
			DragMove();
		};
		_btnMax.Click += (sender, args) =>
		{
			MessageBox.Show("_btnMax clicked!");
		};
		_btnMisc.Click += (sender, args) =>
		{
			MessageBox.Show("_btnMisc clicked!");
		};

	}
	Button _btnMax;
	private const int HTMAXBUTTON = 9;
	/// <summary>DPI Scale for current display</summary>
	private const double DPI_SCALE = 1.5;
	private SolidColorBrush _bgNormal = new SolidColorBrush(Color.FromArgb(0xff, 0xbe, 0xe6, 0xfe));
	private SolidColorBrush _bgMouseHover = new SolidColorBrush(Color.FromArgb(0xff, 0xdd, 0xdd, 0xdd));
	private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wparam,
		IntPtr lparam, ref bool handled)
	{
		switch (msg)
		{
			case 0x0084:
				try
				{
					int x = lparam.ToInt32() & 0xffff;
					int y = lparam.ToInt32() >> 16;
					var rect = new Rect(_btnMax.PointToScreen(
						new Point()),
						new Size(_btnMax.Width * DPI_SCALE, _btnMax.Height * DPI_SCALE));
					if (rect.Contains(new Point(x, y)))
					{
						handled = true;
						_btnMax.Background = _bgNormal;
					}
					else
					{
						_btnMax.Background = _bgMouseHover;
					}
					return new IntPtr(HTMAXBUTTON);
				}
				catch (OverflowException)
				{
					handled = true;
				}
				break;
			case 0x00A1:
				{
					int x = lparam.ToInt32() & 0xffff;
					int y = lparam.ToInt32() >> 16;
					var rect = new Rect(_btnMax.PointToScreen(
						new Point()),
						new Size(_btnMax.Width * DPI_SCALE, _btnMax.Height * DPI_SCALE));
					if (rect.Contains(new Point(x, y)))
					{
						handled = true;
						IInvokeProvider invokeProv = new ButtonAutomationPeer(_btnMax).GetPattern(PatternInterface.Invoke) as IInvokeProvider;
						invokeProv?.Invoke();
					}
				}
				break;
			default:
				handled = false;
				break;
		}
		return IntPtr.Zero;
	}
}

lanzhiquan avatar Dec 03 '21 04:12 lanzhiquan

Hi @lanzhiquan Thank you its working...❤️👌😅 Should the DPI scale be fixed at 1.5? Or should we extract its specifications according to the monitor?

ghost1372 avatar Dec 04 '21 07:12 ghost1372

public static class InteropMethods
{
	[DllImport("user32.dll", CharSet = CharSet.Auto)]
	internal static extern IntPtr GetDC(IntPtr ptr);
	[DllImport("gdi32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
	internal static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
	[DllImport("user32.dll", SetLastError = true)]
	internal static extern int ReleaseDC(IntPtr window, IntPtr dc);
}


public static Point GetDpi()
{
	var hDc = InteropMethods.GetDC(IntPtr.Zero);
	const int LOGPIXELSX = 88;
	const int LOGPIXELSY = 90;
	var dpiX = InteropMethods.GetDeviceCaps(hDc, LOGPIXELSX);
	var dpiY = InteropMethods.GetDeviceCaps(hDc, LOGPIXELSY);

	InteropMethods.ReleaseDC(IntPtr.Zero, hDc);
	return new Point(dpiX, dpiY);
}
public static Point GetDpiScale()
{
	var dpi=GetDpi();
	return new Point(dpi.X/96,dpi.Y/96);
}

lanzhiquan avatar Dec 04 '21 11:12 lanzhiquan

@lanzhiquan Your code output for me is 1/25,1/25 Are the values of GetDpiScale().X and GetDpiScale().Y always equal?

ghost1372 avatar Dec 04 '21 14:12 ghost1372

System.Windows.Point is (double X, double Y)
System.Drawing.Point is (int X, int Y)

lanzhiquan avatar Dec 05 '21 02:12 lanzhiquan