問題描述:當Form的WindowState設定為Maximized或者Minimized後,無法通過Size屬性設定窗體的大小
使用場景:有一個Panel和一個Form,該Form的WindowState設定為Maximized,并且該Form是作為Panel的子控件,Form的Dock設定為Fill,現在要求在改變Panel大小時,Form始終填充滿Panel。
form.Dock = DockStyle.Fill;
form.WindowState = FormWindowState.Maximized;
panelControl1.Controls.Add(form);
form.Show();
實際上,當Panel改變大小時,Form的大小始終固定不變。
問題分析:
懷疑可能是WindowState設定為Maximized導緻的,将其修改成Normal後Form便可以随着Panel的大小改變而改變了。好了,到此問題已經解決,但我想看看為什麼它的WindowState設定為Maximized就無法改變大小。
還好,X軟已經公布了.netframework的源碼,我就從它開始……
打開RefSrc\Source\.Net\4.0\DEVDIV_TFS\Dev10\Releases\RTMRel\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs(尼瑪目錄太深了)下面的源碼檔案,看看設定大小時究竟做了什麼事情。
首先找到設定Size的位置:
最終設定大小的是SetBoundsCore,那就看看它到底幹了什麼。
關鍵的一部分代碼如下:
if (!GetState(STATE_SIZELOCKEDBYOS)) {
int flags = NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE;
if (this.x == x && this.y == y) {
flags |= NativeMethods.SWP_NOMOVE;
}
if (this.width == width && this.height == height) {
flags |= NativeMethods.SWP_NOSIZE;
}
//
// Give a chance for derived controls to do what they want, just before we resize.
OnBoundsUpdate(x, y, width, height);
SafeNativeMethods.SetWindowPos(new HandleRef(window, Handle), NativeMethods.NullHandleRef, x, y, width, height, flags);
// NOTE: SetWindowPos causes a WM_WINDOWPOSCHANGED which is processed
// synchonously so we effectively end up in UpdateBounds immediately following
// SetWindowPos.
//
//UpdateBounds(x, y, width, height);
}
SetWindowPos,多麼熟悉的函數,真正設定視窗大小的就是它!
那為何當WindowState為Maximized時就不能設定呢?懷疑是根本沒有調用SetWindowPos,看到上面有個GetState函數(欠扁的命名) ,進入看看再說吧。
/// <devdoc>
/// Retrieves the current value of the specified bit in the control's state.
/// </devdoc>
internal bool GetState(int flag) {
return(state & flag) != 0;
}
當WindowState為Maximized或Minimized時,state為0x0003000C,此時根本無法通過GetState函數的檢查,當WindowState為normal時則可以順利調用API設定視窗大小。
到此,原因已經找到,可為什麼會有這種限制呢?
打開Form源碼,找到SetBoundsCore方法,此方法重寫的Control的SetBoundsCore方法。
if (WindowState != FormWindowState.Normal) {
// See RestoreWindowBoundsIfNecessary for an explanation of this
// VSWhidbey 503385. Only restore position when x,y is not -1,-1
if (x != -1 || y != -1)
{
restoredWindowBoundsSpecified |= (specified & (BoundsSpecified.X | BoundsSpecified.Y));
}
restoredWindowBoundsSpecified |= (specified & (BoundsSpecified.Width | BoundsSpecified.Height));
if ((specified & BoundsSpecified.X) != 0)
restoredWindowBounds.X = x;
if ((specified & BoundsSpecified.Y) != 0)
restoredWindowBounds.Y = y;
if ((specified & BoundsSpecified.Width) != 0) {
restoredWindowBounds.Width = width;
formStateEx[FormStateExWindowBoundsWidthIsClientSize] = 0;
}
if ((specified & BoundsSpecified.Height) != 0) {
restoredWindowBounds.Height = height;
formStateEx[FormStateExWindowBoundsHeightIsClientSize] = 0;
}
}
原來當WindowState不為Normal時,設定的位置和大小等資訊都被緩存在restoredWindowBounds中,當窗體狀态恢複為Normal時,重新設定緩存的位置和大小。RestoreWindowBoundsIfNecessary方法正是完成此功能的。
既然這樣,那如果我自己通過API來設定窗體大小,而不管WindowState狀态是什麼,這樣會不會有效呢。實際上,無論WindowState是什麼,調用SetWindowPos都可以改變視窗的大小。由此可見,framework隻是人為做了一些限制,是以有人說,framework是新的windows api,元芳,你怎麼看?