HandyControl
HandyControl copied to clipboard
Windows 11 Support for Window Control
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
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.
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
.
for fixing glowWindow CornerRadius and Showing accent color on window borders we need to use something called PreferDWMBorderColor
I finally found a way to show SnapLayout😁, but another problem...😕
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;
}
}
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?
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 Your code output for me is 1/25,1/25 Are the values of GetDpiScale().X and GetDpiScale().Y always equal?
System.Windows.Point is (double X, double Y)
System.Drawing.Point is (int X, int Y)