Я пытаюсь исправить следующие проблемы с максимизацией моего окна WindowChrome:
- Развернутое окно выходит за пределы экрана примерно на 8 пикселей с каждой стороны.
- Aero Peek показывает прозрачную область сверху и слева около 8 пикселей.
- Панель задач автоматического скрытия не работает с развернутым окном.
Я нашел отличную статью , в которой объясняется, как решить проблему с панелью задач. Кстати, при использовании этого решения Aero Peek, панель автоматического скрытия задач и максимизация окна работают, но только когда панель задач настроена на автоматическое скрытие. Если не задано автоматическое скрытие, код просто возвращает стандарт MINMAXINFO.
Странно то, что стандартный MINMAXINFO, похоже, должен обеспечить правильное размещение и размер окна при максимизации. Я знаю это, потому что если я просто вычту 1 пиксель из значения x положения MINMAXINFO или максимального размера, развернутое окно будет в правильном месте, но его ширина будет ровно на 1 пиксель слишком маленькой.
Однако, если я оставлю стандартный MINMAXINFO один, окно будет слишком большим, намного больше, чем один пиксель. Это похоже на то, как будто добавление или вычитание чего-либо из ptMaxPosition или ptMaxSize.x заставляет окно использовать пользовательский MINMAXINFO. Я попытался сложить и вычесть ноль, но, к сожалению, это не сработало.
Вот видео , которое показывает проблему и, мы надеемся, поможет прояснить проблему.
Вот код, который я использую:
public MainWindow()
{
SourceInitialized += new EventHandler(Window1_SourceInitialized);
InitializeComponent();
}
void Window1_SourceInitialized(object sender, EventArgs e)
{
WindowSizing.WindowInitialized(this);
}
public static class WindowSizing
{
const int MONITOR_DEFAULTTONEAREST = 0x00000002;
#region DLLImports
[DllImport("shell32", CallingConvention = CallingConvention.StdCall)]
public static extern int SHAppBarMessage(int dwMessage, ref APPBARDATA pData);
[DllImport("user32", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32")]
internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);
[DllImport("user32")]
internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);
#endregion
private static MINMAXINFO AdjustWorkingAreaForAutoHide(IntPtr monitorContainingApplication, MINMAXINFO mmi)
{
IntPtr hwnd = FindWindow("Shell_TrayWnd", null);
if (hwnd == null)
{
return mmi;
}
IntPtr monitorWithTaskbarOnIt = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (!monitorContainingApplication.Equals(monitorWithTaskbarOnIt))
{
return mmi;
}
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = hwnd;
SHAppBarMessage((int)ABMsg.ABM_GETTASKBARPOS, ref abd);
int uEdge = GetEdge(abd.rc);
bool autoHide = Convert.ToBoolean(SHAppBarMessage((int)ABMsg.ABM_GETSTATE, ref abd));
if (!autoHide)
{
return mmi;
}
switch (uEdge)
{
case (int)ABEdge.ABE_LEFT:
mmi.ptMaxPosition.x += 2;
mmi.ptMaxTrackSize.x -= 2;
mmi.ptMaxSize.x -= 2;
break;
case (int)ABEdge.ABE_RIGHT:
mmi.ptMaxSize.x -= 2;
mmi.ptMaxTrackSize.x -= 2;
break;
case (int)ABEdge.ABE_TOP:
mmi.ptMaxPosition.y += 2;
mmi.ptMaxTrackSize.y -= 2;
mmi.ptMaxSize.y -= 2;
break;
case (int)ABEdge.ABE_BOTTOM:
mmi.ptMaxSize.y -= 2;
mmi.ptMaxTrackSize.y -= 2;
break;
default:
return mmi;
}
return mmi;
}
private static int GetEdge(RECT rc)
{
int uEdge = -1;
if (rc.top == rc.left && rc.bottom > rc.right)
{
uEdge = (int)ABEdge.ABE_LEFT;
}
else if (rc.top == rc.left && rc.bottom < rc.right)
{
uEdge = (int)ABEdge.ABE_TOP;
}
else if (rc.top > rc.left)
{
uEdge = (int)ABEdge.ABE_BOTTOM;
}
else
{
uEdge = (int)ABEdge.ABE_RIGHT;
}
return uEdge;
}
public static void WindowInitialized(Window window)
{
IntPtr handle = (new WindowInteropHelper(window)).Handle;
HwndSource.FromHwnd(handle).AddHook(new HwndSourceHook(WindowProc));
}
private static IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case 0x0024:
WmGetMinMaxInfo(hwnd, lParam);
handled = true;
break;
}
return (IntPtr)0;
}
private static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
{
MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
IntPtr monitorContainingApplication = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitorContainingApplication != System.IntPtr.Zero)
{
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo(monitorContainingApplication, monitorInfo);
RECT rcWorkArea = monitorInfo.rcWork;
RECT rcMonitorArea = monitorInfo.rcMonitor;
mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
mmi.ptMaxTrackSize.x = mmi.ptMaxSize.x; //maximum drag X size for the window
mmi.ptMaxTrackSize.y = mmi.ptMaxSize.y; //maximum drag Y size for the window
mmi.ptMinTrackSize.x = 200; //minimum drag X size for the window
mmi.ptMinTrackSize.y = 40; //minimum drag Y size for the window
mmi = AdjustWorkingAreaForAutoHide(monitorContainingApplication, mmi); //need to adjust sizing if taskbar is set to autohide
}
Marshal.StructureToPtr(mmi, lParam, true);
}
public enum ABEdge
{
ABE_LEFT = 0,
ABE_TOP = 1,
ABE_RIGHT = 2,
ABE_BOTTOM = 3
}
public enum ABMsg
{
ABM_NEW = 0,
ABM_REMOVE = 1,
ABM_QUERYPOS = 2,
ABM_SETPOS = 3,
ABM_GETSTATE = 4,
ABM_GETTASKBARPOS = 5,
ABM_ACTIVATE = 6,
ABM_GETAUTOHIDEBAR = 7,
ABM_SETAUTOHIDEBAR = 8,
ABM_WINDOWPOSCHANGED = 9,
ABM_SETSTATE = 10
}
[StructLayout(LayoutKind.Sequential)]
public struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public bool lParam;
}
[StructLayout(LayoutKind.Sequential)]
public struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class MONITORINFO
{
public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
public RECT rcMonitor = new RECT();
public RECT rcWork = new RECT();
public int dwFlags = 0;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
public POINT(int x, int y)
{
this.x = x;
this.y = y;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
В этом коде:
private static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
{
MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
IntPtr monitorContainingApplication = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitorContainingApplication != System.IntPtr.Zero)
{
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo(monitorContainingApplication, monitorInfo);
RECT rcWorkArea = monitorInfo.rcWork;
RECT rcMonitorArea = monitorInfo.rcMonitor;
mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
mmi.ptMaxTrackSize.x = mmi.ptMaxSize.x; //maximum drag X size for the window
mmi.ptMaxTrackSize.y = mmi.ptMaxSize.y; //maximum drag Y size for the window
mmi.ptMinTrackSize.x = 200; //minimum drag X size for the window
mmi.ptMinTrackSize.y = 40; //minimum drag Y size for the window
mmi = AdjustWorkingAreaForAutoHide(monitorContainingApplication, mmi); //need to adjust sizing if taskbar is set to autohide
}
Marshal.StructureToPtr(mmi, lParam, true);
}
Если я изменю:
mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
Кому (для целей тестирования):
mmi.ptMaxSize.x = 1920 //Math.Abs(rcWorkArea.right - rcWorkArea.left);
mmi.ptMaxSize.y = 1080 //Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
Тогда развернутое окно за пределами экрана примерно на 8 пикселей. Однако, если я изменю это на:
mmi.ptMaxSize.x = 1919 //Math.Abs(rcWorkArea.right - rcWorkArea.left);
mmi.ptMaxSize.y = 1080 //Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
Тогда развернутое окно идеально, за исключением того, что его ширина на один пиксель слишком мала. Кто-нибудь знает, почему это происходит?
У меня есть новая информация для добавления. Мое окно wpf использует WindowStyle = none, поэтому для добавления анимаций я создал событие Window_Loaded и сбросил стиль окна следующим образом:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
hWnd = new WindowInteropHelper(this).Handle;
//IntPtr myStyle = new IntPtr(WS.WS_CAPTION | WS.WS_MINIMIZEBOX | WS.WS_MAXIMIZEBOX | WS.WS_SYSMENU | WS.WS_SIZEBOX);
IntPtr myStyle = new IntPtr(WS.WS_CAPTION | WS.WS_MINIMIZEBOX | WS.WS_SYSMENU | WS.WS_SIZEBOX);
SetWindowLongPtr(new HandleRef(null, hWnd), GWL_STYLE, myStyle);
}
Кажется, что-то в стиле WS_MAXIMIZE переопределяет максимальный размер, потому что если я удаляю этот стиль, как показано в приведенном выше коде, максимизированное окно идеально подходит! Однако я также теряю функцию аэросъемки и заголовок двойного щелчка, чтобы максимизировать, что обеспечивается стилем окна WS_MAXIMIZE. Если бы я мог найти способ добавить обратно аэроснимок, не передавая стиль WS_MAXIMIZE или способ предотвратить переопределение стиля WS_MAXIMIZE моего MINMAXINFO, мои проблемы были бы решены.
Я нашел это сообщение с дополнительной информацией по этой проблеме. Кажется, мои подозрения были верны. Проблема заключается в том, что оконный менеджер будет пересчитывать максимальный размер окна только в том случае, если размер, указанный в WM_GETMINMAXINFO, меньше размеров монитора. Таким образом, размеры правильные, они просто не используются, потому что они точно равны размерам монитора. Вместо этого оконный менеджер использует любые размеры максимизации по умолчанию, которые также добавляют 4 пикселя с каждой стороны для учета границ (которых у меня даже нет).
Обновление: я перепробовал почти все, что мог придумать с переопределениями WndProc, и я все еще ищу решение этой проблемы, но у меня есть некоторая новая информация, которую можно добавить, которая могла бы помочь.
Я просмотрел окно Twitch Launcher, которое имеет правильное поведение максимизации, со Spy ++. Я заметил, что это промежуточное окно D3D, где главное окно приложения является дочерним окном отключенного родительского окна.
Я очень новичок в API Windows, так что я в порядке над головой, но мне интересно, настроено ли дочернее окно только для заполнения размеров отключенного родительского окна, которые находятся в пределах экрана, и родительское окно, которое не видно, вероятно, все еще выходит за пределы экрана на 8 пикселей с каждой стороны. Я действительно не знаю, как создать окно в окне, но я собираюсь поэкспериментировать с ним и посмотреть, как оно работает. Если у меня получится, я отправлю ответ. Если у вас есть что добавить, что может помочь мне, пожалуйста, дайте мне знать.