Benjamen Kuhman Benjamen Kuhman - 2 months ago 44
C# Question

WPF - Borderless Window Not Maximizing Correctly

Alright, so I've been scouring Google for a couple hours now and can't seem to find a direct answer to the problem I'm having. I have a custom window with

WindowStyle = "None"
and
AllowsTransparency = "True"
When I click on my maximize button:

private void MaximizeButton_Click(object sender, RoutedEventArgs e)
{
if(this.WindowState == WindowState.Normal)
{

App.Current.MainWindow.WindowState = WindowState.Maximized;
}
else
{
App.Current.MainWindow.WindowState = WindowState.Normal;
}
}


It maximizes almost exactly the way it's supposed except it seems like there's a -6px margin on the top and left side of the window.

Here's what it looks like

I don't want that white space to be there (it's only white because Google Chrome is open behind it, it's actually transparent). I need the app to maximize to fit the entire screen, excluding the taskbar. So far the only fix I've found is setting the margin of the window to
Margin = "6, 6, 0, 0"
when the maximize button is pressed. Here is the rest of the code for reference:

StartUp.xaml

<Window x:Class="Expense_Calculator.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Expense_Calculator"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
WindowStartupLocation="CenterScreen"
WindowStyle="None"
AllowsTransparency="True">
<Grid Name="Container" Background="#323232">
<Grid.RowDefinitions>
<RowDefinition Height="33"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid>
<DockPanel Style="{StaticResource TitleDockPanel}">
<Label Style="{StaticResource TitleBarTitle}">App Name</Label>
<Button Name="CloseButton" Click="CloseButton_Click" DockPanel.Dock="Right" Style="{StaticResource TitleBarButtonClose}">
<Image Source="images/close.png"/>
</Button>
<Button Name="MaximizeButton" Click="MaximizeButton_Click" DockPanel.Dock="Right" Style="{StaticResource TitleBarButton}">
<Image Source="images/maximize.png"/>
</Button>
<Button Name="MinimizeButton" Click="MinimizeButton_Click" DockPanel.Dock="Right" Style="{StaticResource TitleBarButton}">
<Image Source="images/minimize.png"/>
</Button>
</DockPanel>
</Grid>
<Grid Style="{StaticResource UserArea}" Grid.Row="1">
<Grid Name="WelcomePage">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Style="{StaticResource Label1}">Welcome to your Expense Calculator!</Label>
<Button Cursor="Hand" Style="{StaticResource Button1}" Grid.Row="1">Get Started</Button>
</Grid>
</Grid>
</Grid>




StartUp.xaml.cs

using System.Windows;

namespace Expense_Calculator
{
/// <summary>
/// Interaction logic for StartUp.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.MaxHeight = SystemParameters.WorkArea.Height;
this.MaxWidth = SystemParameters.WorkArea.Width;
InitializeComponent();
}

private void CloseButton_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}

private void MaximizeButton_Click(object sender, RoutedEventArgs e)
{
if(this.WindowState == WindowState.Normal)
{
App.Current.MainWindow.WindowState = WindowState.Maximized;
}
else
{
App.Current.MainWindow.WindowState = WindowState.Normal;
}
}

private void MinimizeButton_Click(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
}
}


App.xaml

<Application x:Class="Expense_Calculator.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Expense_Calculator"
StartupUri="StartUp.xaml">
<Application.Resources>

<!--Title Bar-->
<Style x:Key="TitleDockPanel" TargetType="DockPanel">
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Background" Value="#323232"/>
<Setter Property="Height" Value="33"/>
</Style>
<Style x:Key="TitleBarTitle" TargetType="Label">
<Setter Property="Foreground" Value="White"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontWeight" Value="DemiBold"/>
<Setter Property="Padding" Value="10, 0"/>
</Style>
<Style x:Key="TitleBarButton" TargetType="Button">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border" Background="#323232" Height="33" Width="33">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" Width="15" Height="15"></ContentPresenter>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:.1"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="Background.Color" To="#464646" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="Background.Color" To="#3774FF" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TitleBarButtonClose" TargetType="Button">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border" Background="#323232" Height="33" Width="33">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" Width="15" Height="15"></ContentPresenter>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:.1"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="Background.Color" To="Firebrick" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="Background.Color" To="#781414" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--End - Title Bar-->

<!--Welcome Page-->
<Style x:Key="UserArea" TargetType="Grid">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="Label1" TargetType="Label">
<Setter Property="FontSize" Value="20"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Margin" Value="0, 0, 0, 25"/>
</Style>
<Style x:Key="Button1" TargetType="Button">
<Setter Property="Width" Value="Auto"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border" Background="#323232" CornerRadius="16" BorderBrush="#505050" BorderThickness="1" Padding="15, 6">
<ContentPresenter x:Name="content" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.Foreground>
<SolidColorBrush Color="#7D7D7D"/>
</TextBlock.Foreground>
<TextBlock.FontSize>14</TextBlock.FontSize>
</ContentPresenter>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.15"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="Background.Color" To="#3C3C3C" Duration="0"/>
<ColorAnimation Storyboard.TargetName="content" Storyboard.TargetProperty="(TextBlock.Foreground).Color" To="White" Duration="0"/>
<ColorAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="BorderBrush.Color" To="#C8C8C8" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="Background.Color" To="#282828" Duration="0"/>
<ColorAnimation Storyboard.TargetName="content" Storyboard.TargetProperty="(TextBlock.Foreground).Color" To="White" Duration="0"/>
<ColorAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="BorderBrush.Color" To="#C8C8C8" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>

<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="border" Property="Opacity" Value=".25"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--End - Welcome Page-->
</Application.Resources>
</Application>

Answer

By design (for what reason, I don't know), when you have WindowStyle="None" and you maximize the window, it will extend beyond the actual edge of the screen by several pixels on all sides.

In your code, you are restricting the actual size of the window to the exact dimensions of the work area. Since the maximizing of the window still puts the top-left corner of the window those several pixels to the left and above the top-left corner of the work area, the visible portion of the window is necessarily less than the entire width of the work area, hence the exposed area on the right and the bottom.

As noted by commenter Evk, by removing the size restriction on the window (which you can do only when the window is maximized, if you like), the window can expand to the full size WPF wants, ensuring full coverage of the work area.

In your follow-up comment, it's not clear whether you actually want the taskbar to be covered or not. In either case, you may find these links useful to address your specific needs in that regard:
Maximize window with WindowState Problem (application will hide windows taskbar)
Maximizing window (with WindowStyle=None) considering Taskbar

Alternatively, you could still set the size restriction, but take into account the additional pixel margin that WPF insists on when the window is maximized, setting the dimensions larger than needed so that there is no exposed area.

For what it's worth, here is a simplified code example that focuses solely on the specific behavior here:

<Window x:Class="TestSO39578992MaximizeBorderless.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestSO39578992MaximizeBorderless"
        mc:Ignorable="d" Background="Yellow" 
        WindowStyle="None" AllowsTransparency="True"
        Title="MainWindow" Height="350" Width="525">
  <Window.Style>
    <p:Style TargetType="Window">
      <Setter Property="WindowState" Value="Normal"/>
      <!-- Uncomment "Topmost" setters to experiment with its effect on the task bar visibility -->
      <!--<Setter Property="Topmost" Value="False"/>-->
      <p:Style.Triggers>
        <DataTrigger Binding="{Binding IsChecked, ElementName=checkBox1}" Value="True">
          <Setter Property="WindowState" Value="Maximized"/>
          <!--<Setter Property="Topmost" Value="True"/>-->
        </DataTrigger>
      </p:Style.Triggers>
    </p:Style>
  </Window.Style>
  <!-- set the margin here, to account for the extra space WPF is adding -->
  <!-- <Grid Margin="6"> -->
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition/>
      <RowDefinition/>
    </Grid.RowDefinitions>
    <CheckBox x:Name="checkBox1" Content="Maximized" HorizontalAlignment="Left" VerticalAlignment="Top"/>
    <TextBlock Text="Upper Right" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Top"/>
    <TextBlock Text="Lower Left" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
    <TextBlock Text="Lower Right" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Column="1"/>
  </Grid>
</Window>

And of course:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        this.MaxHeight = SystemParameters.WorkArea.Height;
        this.MaxWidth = SystemParameters.WorkArea.Width;

        // Compensate for the extra space WPF adds by increasing the max width and height here
        //this.MaxHeight = SystemParameters.WorkArea.Height + 12;
        //this.MaxWidth = SystemParameters.WorkArea.Width + 12;

        InitializeComponent();
    }
}

I've included TextBlock elements in all four corners to make it easier to see how the window size is affected by the various property values.

Comments