天天看點

Form的WindowState與Size屬性的關聯

問題描述:當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的位置:

Form的WindowState與Size屬性的關聯
Form的WindowState與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,元芳,你怎麼看?

繼續閱讀