2017-07-25 63 views
0

我看到WPF的一些奇怪的行为。我有一个三个按钮的形式。一个按钮应该使窗口全屏,一个应该将它置于当前显示器上,第三个按钮应该将窗口恢复到正常位置。WPF窗口状态从最大化恢复被卡住在奇数状态

的XAML是

<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> 

和代码如下。奇怪的行为是,当我最大化,然后恢复窗口,位置正确地恢复,但窗口仍然认为它是最大化(最大化按钮看起来像一个恢复按钮,即使ResizeMode已设置,您不能调整窗口大小CanResizeWithGrip)。

当最大化的窗口被恢复后,即使窗口位置没有最大化,它仍认为它仍然是最大化的,只需通过拖动标题栏来手动移动窗口就足以使它自己回到非正常状态,最大化模式。

此外,如果我最大化,然后恢复窗口,然后再次最大化它而不移动它,最大化的窗口位置是不正确的(不在左上角)。

神秘加深。如果我最大化然后恢复窗口,然后按alt,然后按下(以获得窗口菜单),然后选择'移动',然后用键盘移动窗口,它仍然停留在'虚假非麻烦模式'即使窗口正在移动,所以它似乎是解除它的唯一方法是用鼠标移动它。

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); 
     } 
    } 
} 

回答

0

我没有一个完整的答案给你。但是,一旦你删除了Hide()和Show()调用,你会发现你的代码开始工作得更好。

private void restore_click(object sender, RoutedEventArgs e) 
    { 
//   Hide(); 
     RestoreWindowPos(); 
//   Show(); 
    } 

我敢肯定,你把这个在减少闪烁,但我认为正在发生的是,隐藏()和show()调用翻转WS_VISIBLE位在底层操作系统的窗口样式字窗口,它是包含WS_MAXIMIZE和WS_BORDER以及您正在操作的其他一些内容的单词。请参阅https://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx

需要更多的研究来弄清楚究竟发生了什么,但我认为的根本问题是“漏洞抽象”。您的代码将顶部,左侧,样式和状态设置为好像这些是独立的非耦合变量。但他们不是!要设置左边,必须调用OS SetWindowPos()函数,它不需要左上角的坐标,窗口大小,Z顺序以及可视性标志以及窗口是否最大化!见https://msdn.microsoft.com/en-us/library/windows/desktop/ms633545(v=vs.85).aspx。所以每次你设置这些“独立”变量中的一个时,你都会敲打SetWindowPos()。这个API调用让人回想起CPU时代非常宝贵的糟糕的旧时代,并且您需要在每个API调用中尽可能多地包装功能。具有讽刺意味的是,这会让你的代码非常低效。我认为要解决这个问题的办法是绕过System.Windows.Window的泄漏抽象,并直接从user32.dll中调用SetWindowPos和其他API函数。然后事情将会更加可预测。

+0

谢谢,删除隐藏()和显示()修复它。他们在那里解决了之前设置WindowState的问题,但删除它们似乎没有导致返回。 –

相关问题