File: winforms\Managed\System\WinForms\MDIClient.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="MDIClient.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Windows.Forms {
 
    using Microsoft.Win32;
    using System;
    using System.Security.Permissions;
    using System.Collections;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.ComponentModel.Design.Serialization;
    using System.Diagnostics;
    using System.Drawing;
    using System.Globalization;
    using System.Runtime.InteropServices;
    using System.Runtime.Remoting;
    using System.Threading;
    using System.Windows.Forms;
    
    /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient"]/*' />
    /// <internalonly/>
    /// <devdoc>
    ///    <para> 
    ///       Summary to
    ///       Come</para>
    /// </devdoc>
    [
    ComVisible(true),
    ClassInterface(ClassInterfaceType.AutoDispatch),
    ToolboxItem(false),
    DesignTimeVisible(false)
    ]
    public sealed class MdiClient : Control {
 
        // kept in add order, not ZOrder. Need to return the correct
        // array of items...
        //
        private ArrayList children = new ArrayList();
 
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.MdiClient"]/*' />
        /// <devdoc>
        ///     Creates a new MdiClient.
        /// </devdoc>
        /// <internalonly/>
        public MdiClient() : base() {
            SetStyle(ControlStyles.Selectable, false);
            BackColor = SystemColors.AppWorkspace;
            Dock = DockStyle.Fill;
        }
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.BackgroundImage"]/*' />
        /// <devdoc>
        ///      Use parent's BackgroundImage if our BackgroundImage isn't set.
        /// </devdoc>
        [
        Localizable(true)
        ]
        public override Image BackgroundImage {
            get {
                Image result = base.BackgroundImage;
                if (result == null && ParentInternal != null)
                    result = ParentInternal.BackgroundImage;
                return result;
            }
            
            set {
                base.BackgroundImage = value;
            }
        }
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MDIClient.BackgroundImageLayout"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public override ImageLayout BackgroundImageLayout {
            get {
                Image backgroundImage = BackgroundImage;
                if (backgroundImage != null && ParentInternal != null)
                {
                    ImageLayout imageLayout = base.BackgroundImageLayout;
                    if (imageLayout != ParentInternal.BackgroundImageLayout)
                    {
                        // if the Layout is set on the parent use that.
                        return ParentInternal.BackgroundImageLayout;
                    }
                }
                return base.BackgroundImageLayout;
            }
            set {
                base.BackgroundImageLayout = value;
            }
        }
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.CreateParams"]/*' />
        /// <devdoc>
        /// </devdoc>
        /// <internalonly/>
        protected override CreateParams CreateParams {
            [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
            get {
                CreateParams cp = base.CreateParams;
 
                cp.ClassName = "MDICLIENT";
 
                // Note: Don't set the MDIS_ALLCHILDSTYLES CreatParams.Style bit, it prevents an MDI child form from getting activated 
                // when made visible (no WM_MDIACTIVATE sent to it), and forcing activation on it changes the activation event sequence
                // (MdiChildActivate/Enter/Focus/Activate/etc.).
                // Comment for removed code: 
                // VSWhidbey 93518, 93544, 93547, 93563, and 93568: Add the style MDIS_ALLCHILDSTYLES
                // so that MDI Client windows can have the WS_VISIBLE style removed from the window style
                // to make them not visible but still present.
                cp.Style |= NativeMethods.WS_VSCROLL | NativeMethods.WS_HSCROLL;
                cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE;
                cp.Param = new NativeMethods.CLIENTCREATESTRUCT(IntPtr.Zero, 1);
                ISite site = (ParentInternal == null) ? null : ParentInternal.Site;
                if (site != null && site.DesignMode) {
                      cp.Style |= NativeMethods.WS_DISABLED;
                      SetState(STATE_ENABLED, false);
                }
                
                if (this.RightToLeft == RightToLeft.Yes && this.ParentInternal != null && this.ParentInternal.IsMirrored) {
                    //We want to turn on mirroring for MdiClient explicitly.
                    cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL | NativeMethods.WS_EX_NOINHERITLAYOUT;
                    //Don't need these styles when mirroring is turned on.
                    cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR);
                }
 
                return cp;
            }
        }
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.MdiChildren"]/*' />
        /// <devdoc>
        ///     The list of MDI children contained. This list
        ///     will be sorted by the order in which the children were
        ///     added to the form, not the current ZOrder.
        /// </devdoc>
        public Form[] MdiChildren {
            get {
                Form[] temp = new Form[children.Count];
                children.CopyTo(temp, 0);
                return temp;
            }
        }
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.CreateControlsInstance"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        protected override Control.ControlCollection CreateControlsInstance() {
            return new ControlCollection(this);
        }
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.LayoutMdi"]/*' />
        /// <devdoc>
        ///     Arranges the MDI child forms according to value, which should be a
        ///     member of the MdiLayout enum.
        /// </devdoc>
        public void LayoutMdi(MdiLayout value) {
            if (Handle == IntPtr.Zero)
                return;
 
            switch (value) {
                case MdiLayout.Cascade:
                    SendMessage(NativeMethods.WM_MDICASCADE, 0, 0);
                    break;
                case MdiLayout.TileVertical:
                    SendMessage(NativeMethods.WM_MDITILE, NativeMethods.MDITILE_VERTICAL, 0);
                    break;
                case MdiLayout.TileHorizontal:
                    SendMessage(NativeMethods.WM_MDITILE, NativeMethods.MDITILE_HORIZONTAL, 0);
                    break;
                case MdiLayout.ArrangeIcons:
                    SendMessage(NativeMethods.WM_MDIICONARRANGE, 0, 0);
                    break;
            }
        }
 
        
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.OnResize"]/*' />
        /// <devdoc>
        /// </devdoc>
        /// <internalonly/>
        protected override void OnResize(EventArgs e) {
            ISite site = (ParentInternal == null) ? null : ParentInternal.Site;
            if (site != null && site.DesignMode && Handle != IntPtr.Zero) {
                SetWindowRgn();
            }
            base.OnResize(e);
        }
 
        
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.ScaleCore"]/*' />
        /// <devdoc>
        ///     Performs the work of scaling the entire control and any child controls.
        /// </devdoc>
        [EditorBrowsable(EditorBrowsableState.Never)]
        protected override void ScaleCore(float dx, float dy) {
 
            // Don't scale child forms...
            //
 
            SuspendLayout();
            try {
                Rectangle bounds = Bounds;
                int sx = (int)Math.Round(bounds.X * dx);
                int sy = (int)Math.Round(bounds.Y * dy);
                int sw = (int)Math.Round((bounds.X + bounds.Width) * dx - sx);
                int sh = (int)Math.Round((bounds.Y + bounds.Height) * dy - sy);
                SetBounds(sx, sy, sw, sh, BoundsSpecified.All);
            }
            finally {
                ResumeLayout();
            }
        }
 
        /// <include file='doc\Form.uex' path='docs/doc[@for="Form.ScaleControl"]/*' />
        /// <devdoc>
        ///     Scale this form.  Form overrides this to enforce a maximum / minimum size.
        /// </devdoc>
        protected override void ScaleControl(SizeF factor, BoundsSpecified specified) {
            // never scale X and Y of an MDI client form
            specified &= ~BoundsSpecified.Location;
            base.ScaleControl(factor, specified);
        }
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.SetBoundsCore"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
            ISite site = (ParentInternal == null) ? null : ParentInternal.Site;
            if (IsHandleCreated && (site == null || !site.DesignMode)) {
                Rectangle oldBounds = Bounds;
                base.SetBoundsCore(x, y, width, height, specified);
                Rectangle newBounds = Bounds;
 
                int yDelta = oldBounds.Height - newBounds.Height;
                if (yDelta != 0) {
                    // NOTE: This logic is to keep minimized MDI children anchored to
                    // the bottom left of the client area, normally they are anchored
                    // to the top right which just looks wierd!
                    //
                    NativeMethods.WINDOWPLACEMENT wp = new NativeMethods.WINDOWPLACEMENT();
                    wp.length = Marshal.SizeOf(typeof(NativeMethods.WINDOWPLACEMENT));
 
                    for (int i=0; i < Controls.Count; i++) {
                        Control ctl = Controls[i];
                        if (ctl != null && ctl is Form) {
                            Form child = (Form)ctl;
                            // VSWhidbey 93551: Only adjust the window position for visible MDI Child windows to prevent
                            // them from being re-displayed.
                            if (child.CanRecreateHandle() && child.WindowState == FormWindowState.Minimized) {
                                UnsafeNativeMethods.GetWindowPlacement(new HandleRef(child, child.Handle), ref wp);
                                wp.ptMinPosition_y -= yDelta;
                                if (wp.ptMinPosition_y == -1) {
                                    if (yDelta < 0) {
                                        wp.ptMinPosition_y = 0;
                                    }
                                    else {
                                        wp.ptMinPosition_y = -2;
                                    }
                                }
                                wp.flags = NativeMethods.WPF_SETMINPOSITION;
                                UnsafeNativeMethods.SetWindowPlacement(new HandleRef(child, child.Handle), ref wp);
                                wp.flags = 0;
                            }
                        }
                    }
                }
            }
            else {
                base.SetBoundsCore(x, y, width, height, specified);
            }
        }
 
        /// <devdoc>
        /// Refer to 496832. This code is required to set the correct window region during the resize of the Form at design time.
        /// There is case when the form contains a MainMenu and also has IsMdiContainer property set, in which, the MdiClient fails to 
        /// resize and hence draw the correct backcolor.
        /// </devdoc>
        /// <internalonly/>
        private void SetWindowRgn() {
            IntPtr rgn1 = IntPtr.Zero;
            IntPtr rgn2 = IntPtr.Zero;
            NativeMethods.RECT rect = new NativeMethods.RECT();
            CreateParams cp = CreateParams;
 
            AdjustWindowRectEx(ref rect, cp.Style, false, cp.ExStyle);
 
            Rectangle bounds = Bounds;
            rgn1 = SafeNativeMethods.CreateRectRgn(0, 0, bounds.Width, bounds.Height);
            try {
                rgn2 = SafeNativeMethods.CreateRectRgn(-rect.left, -rect.top,
                                             bounds.Width - rect.right, bounds.Height - rect.bottom);
                try {
                    if (rgn1 == IntPtr.Zero || rgn2 == IntPtr.Zero)
                        throw new InvalidOperationException(SR.GetString(SR.ErrorSettingWindowRegion));
 
                    if (SafeNativeMethods.CombineRgn(new HandleRef(null, rgn1), new HandleRef(null, rgn1), new HandleRef(null, rgn2), NativeMethods.RGN_DIFF) == 0)
                        throw new InvalidOperationException(SR.GetString(SR.ErrorSettingWindowRegion));
 
                    if (UnsafeNativeMethods.SetWindowRgn(new HandleRef(this, Handle), new HandleRef(null, rgn1), true) == 0) {
                        throw new InvalidOperationException(SR.GetString(SR.ErrorSettingWindowRegion));
                    }
                    else {
                        // The hwnd now owns the region.
                        rgn1 = IntPtr.Zero;
                    }
                }
                finally {
                    if (rgn2 != IntPtr.Zero) {
                        SafeNativeMethods.DeleteObject(new HandleRef(null, rgn2));
                    }
                }
            }
            finally {
                if (rgn1 != IntPtr.Zero) {
                    SafeNativeMethods.DeleteObject(new HandleRef(null, rgn1)); 
                }
            }
        }
 
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.ShouldSerializeBackColor"]/*' />
        internal override bool ShouldSerializeBackColor() {
            return BackColor != SystemColors.AppWorkspace;
        }
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.ShouldSerializeLocation"]/*' />
        private bool ShouldSerializeLocation() {
            return false;
        }
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.ShouldSerializeSize"]/*' />
        internal override bool ShouldSerializeSize() {
            return false;
        }
        
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.WndProc"]/*' />
        /// <devdoc>
        /// </devdoc>
        /// <internalonly/>
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
        protected override void WndProc(ref Message m) {
            switch (m.Msg) {
                
                case NativeMethods.WM_CREATE:
                    if (ParentInternal != null && ParentInternal.Site != null && ParentInternal.Site.DesignMode && Handle != IntPtr.Zero) {
                        SetWindowRgn();
                    }
                    break;
 
                case NativeMethods.WM_SETFOCUS:
                    InvokeGotFocus(ParentInternal, EventArgs.Empty);
                    Form childForm = null;
                    if (ParentInternal is Form) {
                        childForm = ((Form)ParentInternal).ActiveMdiChildInternal;
                    }
                    if (childForm == null && MdiChildren.Length > 0 && MdiChildren[0].IsMdiChildFocusable) {
                        childForm = MdiChildren[0];
                    }
                    if (childForm != null && childForm.Visible) {
                        childForm.Active = true;
                    }
 
                    // Do not use control's implementation of WmSetFocus
                    // as it will improperly activate this control. 
                    WmImeSetFocus();
                    DefWndProc(ref m);
                    InvokeGotFocus(this, EventArgs.Empty);
                    return;
                case NativeMethods.WM_KILLFOCUS:
                    InvokeLostFocus(ParentInternal, EventArgs.Empty);
                    break;
            }
            base.WndProc(ref m);
        }
 
        internal override void OnInvokedSetScrollPosition(object sender, EventArgs e) {
            Application.Idle += new EventHandler(this.OnIdle); //do this on idle (it must be mega-delayed).
        }
 
        private void OnIdle(object sender, EventArgs e) {
            Application.Idle -= new EventHandler(this.OnIdle);
            base.OnInvokedSetScrollPosition(sender, e);
        }
 
        /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.ControlCollection"]/*' />
        /// <devdoc>
        ///     Collection of controls...
        /// </devdoc>
        [ComVisible(false)]
        new public class ControlCollection : Control.ControlCollection {
            private MdiClient owner;
 
            /*C#r: protected*/
 
            /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.ControlCollection.ControlCollection"]/*' />
            /// <devdoc>
            ///    <para>[To be supplied.]</para>
            /// </devdoc>
            public ControlCollection(MdiClient owner)
            : base(owner) {
                this.owner = owner;
            }
 
            /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.ControlCollection.Add"]/*' />
            /// <devdoc>
            ///     Adds a control to the MDI Container. This child must be
            ///     a Form that is marked as an MDI Child to be added to the
            ///     container. You should not call this directly, but rather
            ///     set the child form's (ctl) MDIParent property:
            /// <code>
            ///     //     wrong
            ///     Form child = new ChildForm();
            ///     this.getMdiClient().add(child);
            ///     //     right
            ///     Form child = new ChildForm();
            ///     child.setMdiParent(this);
            /// </code>
            /// </devdoc>
            public override void Add(Control value) {
                if (value == null) {
                    return;
                }
                if (!(value is Form) || !((Form)value).IsMdiChild) {
                    throw new ArgumentException(SR.GetString(SR.MDIChildAddToNonMDIParent), "value");
                }
                if (owner.CreateThreadId != value.CreateThreadId) {
                    throw new ArgumentException(SR.GetString(SR.AddDifferentThreads), "value");
                }
                owner.children.Add((Form)value);
                base.Add(value);
            }
 
            /// <include file='doc\MDIClient.uex' path='docs/doc[@for="MdiClient.ControlCollection.Remove"]/*' />
            /// <devdoc>
            ///     Removes a child control.
            /// </devdoc>
            public override void Remove(Control value) {
                owner.children.Remove(value);
                base.Remove(value);
            }
        }
    }
}