Как применить MinWidth & MinHeight в окне WPF, где WindowStyle = "None"? - PullRequest
9 голосов
/ 12 ноября 2009

У меня есть приложение WPF, в котором художественное оформление главного окна настраивается с помощью WindowStyle = "None". Я рисую свой собственный заголовок и кнопки min / max / close. К сожалению, Windows не применяет мои свойства MinWidth и MinHeight при изменении размера окна, что позволяет изменять размер окна вплоть до 3x3 (appx - достаточно, чтобы показать маркеры для увеличения окна).

Мне уже приходится перехватывать события окна (sp. 0x0024), чтобы исправить ошибку максимизации (когда она будет максимизироваться по панели задач Windows), вызванную WindowStyle = none. Я не боюсь перехватывать больше событий, чтобы достичь того, что мне нужно.

Кто-нибудь знает, как заставить мое окно не изменять размеры ниже моих свойств MinWidth и MinHeight, если это вообще возможно? Спасибо !!

Ответы [ 5 ]

13 голосов
/ 12 июня 2012

Я смог решить эту проблему, установив handled (последний параметр для WindowProc()) на false в случае 0x0024 (о котором упоминал ОП, он уже перехватывал, чтобы исправить максимизацию), а затем установил MinHeight и MinWidth в вашем Window XAML. Это позволяет обрабатывать это оконное сообщение с помощью механизмов WPF по умолчанию.

Таким образом, атрибуты Min * в Window управляют минимальным размером, а пользовательский код GetMinMaxInfo управляет максимальным размером.

11 голосов
/ 12 ноября 2009

Для этого нужно обработать сообщение Windows, но это не сложно.

Вы должны обработать сообщение WM_WINDOWPOSCHANGING, для этого в WPF требуется немного стандартного кода, как вы можете видеть ниже, фактическая логика - всего две строки кода.

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;
}
2 голосов
/ 15 апреля 2016

Код ниже будет работать для любых настроек 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 голосов
/ 21 сентября 2016

Решение работает, но есть ошибка. Окно движется, когда я пытаюсь изменить размер окна, перетаскивая верхнюю или левую границу. Это случилось, когда changePos - правда. Вот код без этой ошибки:

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;
}
0 голосов
/ 12 ноября 2009

Я не могу проверить это в данный момент, потому что я на ноутбуке Mac, но я полагаю, что я сделал это раньше, обработав событие SizeChanged и затем обнаружив, нарушается ли MinWidth / Height и, если да, , просто установив для свойства Width / Height значение min.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...