I'm seeing some strange behaviour from WPF. I have a form with three buttons on it. One button should make the window fullscreen, one should center it on the monitor its currently on, the third button should restore the window to its normal position.
The XAML is
<Window x:Class="TestRestore.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:TestRestore"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" ResizeMode="CanResizeWithGrip" WindowStartupLocation="CenterScreen">
<Grid>
<Button Content="Max" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="94" Click="max_click" Name="max_button"/>
<Button Content="Center" HorizontalAlignment="Left" Margin="10,35,0,0" VerticalAlignment="Top" Width="94" Click="center_click" Name="center_button"/>
<Button Content="Restore" HorizontalAlignment="Left" Margin="227,143,0,0" VerticalAlignment="Top" Width="75" Click="restore_click" Name="restore_button" IsEnabled="False"/>
</Grid>
</Window>
and the code is below. The strange behaviour is that when I maximize, and then restore the window, the position is correctly restored but the window still thinks it's maximized (the maximize button looks like a restore button and you can't resize the window even though ResizeMode has been set to CanResizeWithGrip).
When the maximized window has been restored, and it thinks its still maximized even though the window position isn't maximized, just moving the window manually by dragging the title bar is enough to cause it to correct itself back to non-maximized mode.
Also, if I maximize then restore the window and then maximize it again without moving it, the maximized window position is incorrect (not in the top left).
And the mystery deepens. If I maximize then restore the window, then press alt, then press down (to get the window menu) and select 'Move' and then move the window around with the keyboard, it stays stuck in 'bogus not-mazimized mode' even though the window is being moved, so it seems the only way to unstick it is to move it with the mouse.
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace TestRestore
{
public partial class MainWindow : Window
{
WindowStyle old_window_style;
WindowState old_window_state;
double old_left;
double old_top;
double old_width;
double old_height;
public MainWindow()
{
InitializeComponent();
}
// remember position, style and state
private void SaveWindowPos()
{
old_window_style = WindowStyle;
old_window_state = WindowState;
old_left = Left;
old_top = Top;
old_width = Width;
old_height = Height;
max_button.IsEnabled = false;
center_button.IsEnabled = false;
restore_button.IsEnabled = true;
}
// put position, style and state back
private void RestoreWindowPos()
{
WindowStyle = old_window_style;
WindowState = old_window_state;
ResizeMode = ResizeMode.CanResizeWithGrip;
Left = old_left;
Top = old_top;
Width = old_width;
Height = old_height;
max_button.IsEnabled = true;
center_button.IsEnabled = true;
restore_button.IsEnabled = false;
}
// make it centered or fullscreen
private void SetActivePos(bool full_screen)
{
SaveWindowPos();
Hide();
if (full_screen)
{
ResizeMode = ResizeMode.NoResize;
WindowStyle = WindowStyle.None;
WindowState = WindowState.Maximized;
}
else
{
Size s = new Size(800, 600);
Point p = CenterRectInMonitor(this, s);
Left = p.X;
Top = p.Y;
Width = s.Width;
Height = s.Height;
ResizeMode = ResizeMode.NoResize;
WindowState = WindowState.Normal;
}
Show();
}
private void restore_click(object sender, RoutedEventArgs e)
{
Hide();
RestoreWindowPos();
Show();
}
private void max_click(object sender, RoutedEventArgs e)
{
SetActivePos(true);
}
private void center_click(object sender, RoutedEventArgs e)
{
SetActivePos(false);
}
// interop
public const Int32 MONITOR_DEFAULTTOPRIMARY = 0x00000001;
public const Int32 MONITOR_DEFAULTTONEAREST = 0x00000002;
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromWindow(IntPtr handle, Int32 flags);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MonitorInfoEx lpmi);
// size of a device name string
private const int CCHDEVICENAME = 32;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct MonitorInfoEx
{
public int Size;
public RectStruct Monitor;
public RectStruct WorkArea;
public uint Flags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
public string DeviceName;
public void Init()
{
this.Size = 40 + 2 * CCHDEVICENAME;
this.DeviceName = string.Empty;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RectStruct
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public int Width
{
get
{
return Right - Left;
}
}
public int Height
{
get
{
return Bottom - Top;
}
}
}
public static MonitorInfoEx GetMonitorFromWindow(Window w)
{
var hwnd = new WindowInteropHelper(w).EnsureHandle();
var monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
MonitorInfoEx monitor_info = new MonitorInfoEx();
monitor_info.Init();
GetMonitorInfo(monitor, ref monitor_info);
return monitor_info;
}
// work out how a rect of 'Size size' should be centered on the monitor containing 'Window w'
public static Point CenterRectInMonitor(Window w, Size size)
{
var source = PresentationSource.FromVisual(w);
double x_scale = source.CompositionTarget.TransformToDevice.M11;
double y_scale = source.CompositionTarget.TransformToDevice.M22;
var width = size.Width * x_scale;
var height = size.Height * y_scale;
var monitor_info = GetMonitorFromWindow(w);
Size s = new Size(monitor_info.Monitor.Width, monitor_info.Monitor.Height);
Point p = new Point(monitor_info.Monitor.Left, monitor_info.Monitor.Top);
Point c = new Point(p.X + s.Width / 2, p.Y + s.Height / 2);
return new Point((c.X - width / 2) / x_scale, (c.Y - height / 2) / y_scale);
}
}
}