Browse Source

Merge pull request #2128 from AvaloniaUI/feature/win32-hide-system-decorations-with-working-frame

Win32 Hide System Decorations compatible with Window Snap feature from Win 7, 8, 10
pull/2156/head
Nikita Tsukanov 8 years ago
committed by GitHub
parent
commit
73559a92ee
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      samples/ControlCatalog/DecoratedWindow.xaml
  2. 30
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  3. 176
      src/Windows/Avalonia.Win32/WindowImpl.cs

13
samples/ControlCatalog/DecoratedWindow.xaml

@ -1,9 +1,8 @@
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.DecoratedWindow"
Title="Avalonia Control Gallery"
Icon="/Assets/test_icon.ico"
xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False">
xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False" Name="Window">
<Grid RowDefinitions="5,*,5" ColumnDefinitions="5,*,5">
<DockPanel Grid.Column="1" Grid.Row="1" >
<Grid Name="TitleBar" Background="LightBlue" DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto">
@ -20,7 +19,13 @@
</StackPanel>
</Grid>
<Border Background="White" Margin="5">
<TextBlock>Hello world!</TextBlock>
<StackPanel>
<TextBlock>Hello world!</TextBlock>
<CheckBox IsChecked="{Binding ElementName=Window, Path=HasSystemDecorations}">Decorated</CheckBox>
<CheckBox IsChecked="{Binding ElementName=Window, Path=CanResize}">CanResize</CheckBox>
</StackPanel>
</Border>
</DockPanel>
<Border Name="TopLeft" Background="Red"/>

30
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1172,6 +1172,8 @@ namespace Avalonia.Win32.Interop
public int right;
public int bottom;
public int Width => right - left;
public int Height => bottom - top;
public RECT(Rect rect)
{
left = (int)rect.X;
@ -1179,6 +1181,34 @@ namespace Avalonia.Win32.Interop
right = (int)(rect.X + rect.Width);
bottom = (int)(rect.Y + rect.Height);
}
public void Offset(POINT pt)
{
left += pt.X;
right += pt.X;
top += pt.Y;
bottom += pt.Y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public uint flags;
}
[StructLayout(LayoutKind.Sequential)]
public struct NCCALCSIZE_PARAMS
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public RECT[] rgrc;
public WINDOWPOS lppos;
}
public struct TRACKMOUSEEVENT

176
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -35,6 +35,7 @@ namespace Avalonia.Win32
private bool _decorated = true;
private bool _resizable = true;
private bool _topmost = false;
private bool _taskbarIcon = true;
private double _scaling = 1;
private WindowState _showWindowState;
private WindowState _lastWindowState;
@ -94,17 +95,25 @@ namespace Avalonia.Win32
{
get
{
var style = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE);
var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE);
var padding = new UnmanagedMethods.RECT();
if (UnmanagedMethods.AdjustWindowRectEx(ref padding, style, false, exStyle))
if (_decorated)
{
return new Thickness(-padding.left, -padding.top, padding.right, padding.bottom);
var style = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE);
var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE);
var padding = new RECT();
if (UnmanagedMethods.AdjustWindowRectEx(ref padding, style, false, exStyle))
{
return new Thickness(-padding.left, -padding.top, padding.right, padding.bottom);
}
else
{
throw new Win32Exception();
}
}
else
{
throw new Win32Exception();
return new Thickness();
}
}
}
@ -147,8 +156,7 @@ namespace Avalonia.Win32
if (value != ClientSize)
{
value *= Scaling;
value += BorderThickness;
UnmanagedMethods.SetWindowPos(
_hwnd,
IntPtr.Zero,
@ -265,63 +273,7 @@ namespace Avalonia.Win32
return;
}
var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE);
style |= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW;
if (!value)
{
style ^= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW;
}
UnmanagedMethods.RECT windowRect;
UnmanagedMethods.GetWindowRect(_hwnd, out windowRect);
Rect newRect;
var oldThickness = BorderThickness;
UnmanagedMethods.SetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE, (uint)style);
if (value)
{
var thickness = BorderThickness;
newRect = new Rect(
windowRect.left - thickness.Left,
windowRect.top - thickness.Top,
(windowRect.right - windowRect.left) + (thickness.Left + thickness.Right),
(windowRect.bottom - windowRect.top) + (thickness.Top + thickness.Bottom));
}
else
{
newRect = new Rect(
windowRect.left + oldThickness.Left,
windowRect.top + oldThickness.Top,
(windowRect.right - windowRect.left) - (oldThickness.Left + oldThickness.Right),
(windowRect.bottom - windowRect.top) - (oldThickness.Top + oldThickness.Bottom));
}
UnmanagedMethods.SetWindowPos(_hwnd, IntPtr.Zero, (int)newRect.X, (int)newRect.Y, (int)newRect.Width,
(int)newRect.Height,
UnmanagedMethods.SetWindowPosFlags.SWP_NOZORDER | UnmanagedMethods.SetWindowPosFlags.SWP_NOACTIVATE);
_decorated = value;
if(_decorated)
{
if (_resizable)
{
// If we switch decorations back on we need to restore WS_SizeFrame.
_resizable = false;
CanResize(true);
}
else
{
_resizable = true;
CanResize(false);
}
}
UpdateWMStyles(()=> _decorated = value);
}
public void Invalidate(Rect rect)
@ -486,6 +438,13 @@ namespace Avalonia.Win32
return IntPtr.Zero;
case WindowsMessage.WM_NCCALCSIZE:
if (ToInt32(wParam) == 1 && !_decorated)
{
return IntPtr.Zero;
}
break;
case UnmanagedMethods.WindowsMessage.WM_CLOSE:
bool? preventClosing = Closing?.Invoke();
if (preventClosing == true)
@ -646,6 +605,14 @@ namespace Avalonia.Win32
new Point(0, 0), GetMouseModifiers(wParam));
break;
case WindowsMessage.WM_NCPAINT:
case WindowsMessage.WM_NCACTIVATE:
if (!_decorated)
{
return IntPtr.Zero;
}
break;
case UnmanagedMethods.WindowsMessage.WM_PAINT:
UnmanagedMethods.PAINTSTRUCT ps;
if (UnmanagedMethods.BeginPaint(_hwnd, out ps) != IntPtr.Zero)
@ -688,7 +655,7 @@ namespace Avalonia.Win32
MINMAXINFO mmi = Marshal.PtrToStructure<UnmanagedMethods.MINMAXINFO>(lParam);
if (_minSize.Width > 0)
if (_minSize.Width > 0)
mmi.ptMinTrackSize.X = (int)((_minSize.Width * Scaling) + BorderThickness.Left + BorderThickness.Right);
if (_minSize.Height > 0)
@ -859,7 +826,7 @@ namespace Avalonia.Win32
{
MONITORINFO monitorInfo = MONITORINFO.Create();
if (GetMonitorInfo(monitor,ref monitorInfo))
if (GetMonitorInfo(monitor, ref monitorInfo))
{
RECT rcMonitorArea = monitorInfo.rcMonitor;
@ -888,13 +855,22 @@ namespace Avalonia.Win32
return (int)(ptr.ToInt64() & 0xffffffff);
}
public void ShowTaskbarIcon(bool value)
{
if (_taskbarIcon == value)
{
return;
}
_taskbarIcon = value;
var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE);
style &= ~(UnmanagedMethods.WindowStyles.WS_VISIBLE);
style |= UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW;
if (value)
style |= UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW;
else
@ -910,26 +886,68 @@ namespace Avalonia.Win32
}
}
public void CanResize(bool value)
private void UpdateWMStyles(Action change)
{
if (value == _resizable)
var oldDecorated = _decorated;
var oldThickness = BorderThickness;
change();
var style = (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE);
const WindowStyles controlledFlags = WindowStyles.WS_OVERLAPPEDWINDOW;
style = style | controlledFlags ^ controlledFlags;
style |= WindowStyles.WS_OVERLAPPEDWINDOW;
if (!_decorated)
{
return;
style ^= (WindowStyles.WS_CAPTION | WindowStyles.WS_SYSMENU);
}
if (_decorated)
if (!_resizable)
{
var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE);
style ^= (WindowStyles.WS_SIZEFRAME);
}
if (value)
style |= UnmanagedMethods.WindowStyles.WS_SIZEFRAME;
else
style &= ~(UnmanagedMethods.WindowStyles.WS_SIZEFRAME);
GetClientRect(_hwnd, out var oldClientRect);
var oldClientRectOrigin = new UnmanagedMethods.POINT();
ClientToScreen(_hwnd, ref oldClientRectOrigin);
oldClientRect.Offset(oldClientRectOrigin);
SetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE, (uint)style);
UnmanagedMethods.GetWindowRect(_hwnd, out var windowRect);
bool frameUpdated = false;
if (oldDecorated != _decorated)
{
var newRect = oldClientRect;
if (_decorated)
AdjustWindowRectEx(ref newRect, (uint)style, false,
GetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE));
SetWindowPos(_hwnd, IntPtr.Zero, newRect.left, newRect.top, newRect.Width, newRect.Height,
SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED);
frameUpdated = true;
}
UnmanagedMethods.SetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE, (uint)style);
if (!frameUpdated)
SetWindowPos(_hwnd, IntPtr.Zero, 0, 0, 0, 0,
SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOZORDER |
SetWindowPosFlags.SWP_NOACTIVATE
| SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE);
}
public void CanResize(bool value)
{
if (value == _resizable)
{
return;
}
_resizable = value;
UpdateWMStyles(()=> _resizable = value);
}
public void SetTopmost(bool value)

Loading…
Cancel
Save