Usually a window always has a resize border. Now, when you maximize the window Windows inflates the window size by the thickness of the resize border. This way the border gets pushed out of the screen i.e. the resize border will be hidden giving the window a clean full-screen appearance.
The important point is that Windows expects this resize border, while WPF allows to drop it from the Window
template.
If you override the window chrome or effectively set Window.WindowStyle
to WindowStyle.None
you will lose the resize border around the window. Windows won't recognize this and continues to inflate the Window
with the goal to hide the resize border. Because in our case (WindowStyle.None
) the border got dropped from the template Windows effectively clips the window itself.
The solution is to compensate for the expected resize border thickness yourself.
You can do this by adding an additional resize Border
to your Window
template and define a Trigger
to handle the WindowState.Maximized
case (see Solution 1).
Alternatively, simply inflate and deflate the current Window.BorderThickness
(see Solution 2).
Instead of using a randomly guessed thickness value like 8
as suggested by other answers you should use the SystemParameters.WindowResizeBorderThickness
constant.
Solution 1
Add the window resize Border back to the template. It gets dropped when setting Window.WindowStyle
to WindowStyle.None
.
<Style TargetType="Window">
<Setter Property="WindowStyle"
Value="None" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border x:Name="ResizeBorder">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <!-- Title bar row -->
<RowDefinition /> <!-- Window.Content row -->
<RowDefinition Height="Auto" /> <!-- Resize gripper row -->
</Grid.RowDefinitions>
<AdornerDecorator Grid.Row="1">
<Border Background="Transparent"
Margin="{x:Static SystemParameters.WindowNonClientFrameThickness}">
<ContentPresenter />
</Border>
</AdornerDecorator>
</Grid>
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="WindowState"
Value="Maximized">
<Setter TargetName="ResizeBorder"
Property="BorderThickness"
Value="{x:Static SystemParameters.WindowResizeBorderThickness}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Solution 2
Inflate the Window.BorderThickness
when the Window.WindowState
property changes:
MainWindow.xaml.cs
partial class MainWindow : Window
{
private WindowState OldWindowState { get; set; }
public MainWindow()
{
InitializeComponent();
}
protected override void OnStateChanged(EventArgs e)
{
base.OnStateChanged(e);
this.BorderThickness = this.WindowState switch
{
WindowState.Maximized => InflateBorder(SystemParameters.WindowResizeBorderThickness),
WindowState.Normal or WindowState.Minimized when this.OldWindowState == WindowState.Maximized => DeflateBorder(SystemParameters.WindowResizeBorderThickness),
_ => this.BorderThickness
};
this.OldWindowState = this.WindowState;
}
private Thickness InflateBorder(Thickness thickness)
{
double left = this.BorderThickness.Left + thickness.Left;
double top = this.BorderThickness.Top + thickness.Top;
double right = this.BorderThickness.Right + thickness.Right;
double bottom = this.BorderThickness.Bottom + thickness.Bottom;
return new Thickness(left, top, right, bottom);
}
private Thickness DeflateBorder(Thickness thickness)
{
double left = this.BorderThickness.Left - thickness.Left;
double top = this.BorderThickness.Top - thickness.Top;
double right = this.BorderThickness.Right - thickness.Right;
double bottom = this.BorderThickness.Bottom - thickness.Bottom;
return new Thickness(left, top, right, bottom);
}