WPF window state when restored from maximized gets stuck in odd state -
i'm seeing strange behaviour wpf. have form 3 buttons on it. 1 button should make window fullscreen, 1 should center on monitor on, third button should restore window normal position.
the xaml is
<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>
and code below. strange behaviour when maximize, , restore window, position correctly restored window still thinks it's maximized (the maximize button looks restore button , can't resize window though resizemode has been set canresizewithgrip).
when maximized window has been restored, , thinks still maximized though window position isn't maximized, moving window manually dragging title bar enough cause correct non-maximized mode.
also, if maximize restore window , maximize again without moving it, maximized window position incorrect (not in top left).
and mystery deepens. if maximize restore window, press alt, press down (to window menu) , select 'move' , move window around keyboard, stays stuck in 'bogus not-mazimized mode' though window being moved, seems way unstick move mouse.
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 , 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 , state 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 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 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 { { return right - left; } } public int height { { 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 rect of 'size size' should centered on 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); } } }
i don't have complete answer you. find code starts working lot better once remove hide() , show() calls.
private void restore_click(object sender, routedeventargs e) { // hide(); restorewindowpos(); // show(); }
i'm sure put in reduce flicker, think happening hide() , show() calls flipping ws_visible bit in window style word of underlying os window same word contains ws_maximize , ws_border , other things manipulating. see https://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx
it take more research figure out going on, fundamental problem believe "leaky abstraction". code sets top, left, style , state if these independent uncoupled variables. not! set left, os setwindowpos() function must called requires not upper left coordinate, window size, z order visibility flags , whether windows maximized! see https://msdn.microsoft.com/en-us/library/windows/desktop/ms633545(v=vs.85).aspx. each time set 1 of these "independent" variables pounding setwindowpos(). api call harks bad old days when cpu cycles precious , need pack functionality possible each api call.
ironically making code inefficient. think thing straighten out bypass leaking abstraction of system.windows.window , call setwindowpos , possibility other api functions directly user32.dll. things lot more predicable.
Comments
Post a Comment