2017-07-25 62 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的問題,但刪除它們似乎沒有導致返回。 –

相關問題