2009-11-11 19 views
9

私はWindowStyle = "None"を使って、メインウィンドウの装飾がカスタムであるWPFアプリケーションを持っています。私は自分のタイトルバーと最小/最大/閉じるボタンを描きます。残念ながら、ウィンドウのサイズが変更されたときにMinWidthプロパティとMinHeightプロパティが強制されないため、ウィンドウのサイズを3x3(appx - ウィンドウを拡大するハンドルを表示するのに十分)に変更できます。WindowStyle = "None"のWPFウィンドウでMinWidth&MinHeightを実行する方法は?

WindowStyle = noneによって引き起こされる最大化バグ(Windowsタスクバー上で最大化する場所)を修正するために、既にウィンドウイベント(sp。0x0024)を傍受する必要があります。私は、私が必要とするものを達成するために、より多くの出来事を傍受することを恐れることはありません。

MinWidthプロパティとMinHeightプロパティの下でサイズ変更しないようにする方法を誰かが知っていますか?ありがとう!!

答えて

11

Windowsメッセージを処理する必要がありますが、複雑ではありません。

WM_WINDOWPOSCHANGINGメッセージを処理する必要があります。WPFでこれを行うには、定型コードを少し必要とします。実際のロジックはコードの2行だけです。

internal enum WM 
{ 
    WINDOWPOSCHANGING = 0x0046, 
} 

[StructLayout(LayoutKind.Sequential)] 
internal struct WINDOWPOS 
{ 
    public IntPtr hwnd; 
    public IntPtr hwndInsertAfter; 
    public int x; 
    public int y; 
    public int cx; 
    public int cy; 
    public int flags; 
} 

private void Window_SourceInitialized(object sender, EventArgs ea) 
{ 
    HwndSource hwndSource = (HwndSource)HwndSource.FromVisual((Window)sender); 
    hwndSource.AddHook(DragHook); 
} 

private static IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled) 
{ 
    switch ((WM)msg) 
    { 
     case WM.WINDOWPOSCHANGING: 
     { 
      WINDOWPOS pos = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS)); 
      if ((pos.flags & (int)SWP.NOMOVE) != 0) 
      { 
       return IntPtr.Zero; 
      } 

      Window wnd = (Window)HwndSource.FromHwnd(hwnd).RootVisual; 
      if (wnd == null) 
      { 
      return IntPtr.Zero; 
      } 

      bool changedPos = false; 

      // *********************** 
      // Here you check the values inside the pos structure 
      // if you want to override them just change the pos 
      // structure and set changedPos to true 
      // *********************** 

      // this is a simplified version that doesn't work in high-dpi settings 
      // pos.cx and pos.cy are in "device pixels" and MinWidth and MinHeight 
      // are in "WPF pixels" (WPF pixels are always 1/96 of an inch - if your 
      // system is configured correctly). 
      if(pos.cx < MinWidth) { pos.cx = MinWidth; changedPos = true; } 
      if(pos.cy < MinHeight) { pos.cy = MinHeight; changedPos = true; } 


      // *********************** 
      // end of "logic" 
      // *********************** 

      if (!changedPos) 
      { 
      return IntPtr.Zero; 
      } 

      Marshal.StructureToPtr(pos, lParam, true); 
      handeled = true; 
     } 
     break; 
    } 

    return IntPtr.Zero; 
} 
+0

ありがとうございました!これはまさに私が必要としていたものです。 0x0024(イベントを最大化する)を監視していたので、私はすでにソースを接続していたので、スイッチにケースを追加するだけでした。再度、感謝します! –

+0

ありがとうございます。それはそれを適切かつ適切に分類した。 – Dennis

+0

MinHeightまたはMinWidthのいずれかを超過しようとすると、「黒い」画面が点滅するのを防ぐ方法はありますか?フリッカーは悪いです。 –

0

私はMacノートパソコンを使用しているため、現時点では確認できませんが、これまではSizeChangedイベントを処理し、MinWidth/Heightが違反しているかどうかを検出していますWidth/Heightプロパティをminに設定するだけです。

+0

ありがとうございます。それは実際に動作しますが、奇妙な経験を提供します - 彼らはまだ完全に下にドラッグすることができますが、ウィンドウはそれらを "戦う"。下のもう1つの答えは、適切なUXを生成します。 –

+0

上記の解決策は、低レベルのWindowsメッセージで手を汚すことを恐れていない限り、はるかに良いです。 :) –

12

私は(OPは彼がすでに最大化を修正するためにフックして述べたもの)0x0024のためのケースでfalseに(WindowProc()の最後のパラメータ)handledを設定することで、この問題を解決することができたし、その後MinHeightを設定し、ウィンドウXAMLのMinWidthこれにより、このウィンドウメッセージの処理がデフォルトのWPFメカニズムに移行します。

このようにして、WindowのMin *属性が最小サイズを管理し、カスタムGetMinMaxInfoコードが最大サイズを管理します。

+2

これはWindows 8の64ビットでも動作するため、これを投票しましたが、上記のソリューションでは、最小ディメンションを下回ってサイズを変更しようとするとCOMエラーが発生します。 – Geoffrey

2

以下のコードはDPIの設定に使用できます。

case 0x0046: //Window position message to be handled to restrict the min and max height of the window on 120% screen 
        { 
         WINDOWPOS pos = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS)); 
         if ((pos.flags & (int)SWP.NOMOVE) != 0) 
         { 
          return IntPtr.Zero; 
         } 

         System.Windows.Window wnd = (System.Windows.Window)HwndSource.FromHwnd(hwnd).RootVisual; 
         if (wnd == null) 
         { 
          return IntPtr.Zero; 
         } 

         bool changedPos = false; 

         //Convert the original to original size based on DPI setting. Need for 120% screen 
         PresentationSource MainWindowPresentationSource = PresentationSource.FromVisual(wnd); 
         Matrix m = MainWindowPresentationSource.CompositionTarget.TransformToDevice; 
         if (pos.cx < (wnd.MinWidth * m.M11)) { pos.cx = (int)(wnd.MinWidth * m.M11); changedPos = true; } 
         if (pos.cy < (wnd.MinHeight * m.M22)) { pos.cy = (int)(wnd.MinHeight * m.M22); changedPos = true; } 

         if (!changedPos) 
         { 
          return IntPtr.Zero; 
         } 

         Marshal.StructureToPtr(pos, lParam, true); 
         handled = true; 
        } 
        break; 
0

解決策は動作していますが、バグがあります。上または左の枠線をドラッグしてウィンドウのサイズを変更しようとすると、ウィンドウが動いています。 changePosがtrueのときに起こりました。 ここにはこのバグのないコードがあります:

private static WindowPos _prevPos = new WindowPos(); 
/// <summary> 
/// You do need to handle a windows message to do it, but it's not complicated. 
/// You have to handle the WM_WINDOWPOSCHANGING message, doing that in WPF requires 
/// a bit of boilerplate code, you can see below the actual logic is just two lines of code. 
/// </summary> 
/// <param name="hwnd"></param> 
/// <param name="lParam"></param> 
private static bool OnWmWindowPosChanging(IntPtr hwnd, IntPtr lParam) 
{ 
    // ReSharper disable once InconsistentNaming 
    const int SwpNoMove = 0x0002; 
    WindowPos pos = (WindowPos) Marshal.PtrToStructure(lParam, typeof (WindowPos)); 
    if ((pos.flags & SwpNoMove) != 0) return false; 

    Window wnd = (Window) HwndSource.FromHwnd(hwnd)?.RootVisual; 
    if (wnd == null) return false; 

    bool changePos = false; 

    if (pos.cx < wnd.MinWidth) 
    { 
     pos.cx = (int)wnd.MinWidth; 
     // here is we keep pos x 
     if (_prevPos.hwnd != IntPtr.Zero) 
      pos.x = _prevPos.x; 
     changePos = true; 
    } 

    if (pos.cy < wnd.MinHeight) 
    { 
     pos.cy = (int)wnd.MinHeight; 
     // here is we keep pos y 
     if (_prevPos.hwnd != IntPtr.Zero) 
      pos.y = _prevPos.y; 
     changePos = true; 
    } 

    // Debug.WriteLine($"x = {pos.x}, y = {pos.y}; w = {pos.cx}, h = {pos.cy}"); 

    if (!changePos) return false; 
    // Keep prev pos for bounded window 
    _prevPos = pos; 
    Marshal.StructureToPtr(pos, lParam, true); 

    return true; 
} 
関連する問題