2017-07-25 12 views
0

WPFから奇妙な動作が発生しています。私は3つのボタンがあるフォームを持っています。 1つのボタンはウィンドウを全画面にし、現在のモニター上にそのウィンドウを中央に配置し、3番目のボタンはウィンドウを通常の位置に戻す必要があります。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()の呼び出しは、基礎となるOSのウィンドウスタイルワードにWS_VISIBLEビットを反転していることですウィンドウには、WS_MAXIMIZEとWS_BORDERを含む同じ単語と、あなたが操作しているいくつかのものがあります。 https://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx

正確に何が起こっているか把握するにはもっと多くの研究が必要ですが、根本的な問題は「漏れ抽象」だと思います。あなたのコードは、独立した非結合変数であるかのように、上、左、スタイル、および状態を設定します。しかし、彼らはそうではありません!左に設定するには、左上座標、ウィンドウサイズ、Zオーダー、可視性フラグ、およびウィンドウが最大化されているかどうかを必要とするOS SetWindowPos()関数を呼び出さなければなりません! https://msdn.microsoft.com/en-us/library/windows/desktop/ms633545(v=vs.85).aspxを参照してください。したがって、これらの「独立」変数の1つを設定するたびに、SetWindowPos()が打たれます。このAPI呼び出しは、CPUサイクルが貴重で、各API呼び出しにできるだけ多くの機能をパックする必要がある、悪い昔のことを思い起こさせます。

皮肉なことに、これはあなたのコードを非常に非効率的にしています。この問題を解決するには、System.Windows.Windowの抽象概念を迂回してSetWindowPosなどを呼び出し、user32.dllから直接他のAPI関数を呼び出すことが望ましいと思います。そうすれば、事態はもっと予測可能になるでしょう。

+0

ありがとうございます、Hide()とShow()を削除すると、それが修正されます。彼らはそこに以前の問題を解決してWindowStateを設定することになっていましたが、それらを削除することはそれを戻す原因には見えませんでした。 –

関連する問題