File: winforms\Managed\System\WinForms\GroupBox.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="GroupBox.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
/*
 */
namespace System.Windows.Forms {
 
    using Microsoft.Win32;
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Drawing;
    using System.Windows.Forms.Internal;
    using System.Drawing.Text;
    using System.Runtime.InteropServices;
    using System.Runtime.Remoting;
    using System.Security.Permissions;
    using System.Windows.Forms.VisualStyles;
    using System.Windows.Forms.Layout;
    using System.Diagnostics.CodeAnalysis;
 
    /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox"]/*' />
    /// <devdoc>
    ///    <para>
    ///       Encapsulates
    ///       a standard Windows(r) group
    ///       box.
    ///    </para>
    /// </devdoc>
    [
    ComVisible(true),
    ClassInterface(ClassInterfaceType.AutoDispatch),
    DefaultEvent("Enter"),
    DefaultProperty("Text"),
    Designer("System.Windows.Forms.Design.GroupBoxDesigner, " + AssemblyRef.SystemDesign),
    SRDescription(SR.DescriptionGroupBox)
    ]
    public class GroupBox : Control {
        int fontHeight = -1;
        Font cachedFont;
        FlatStyle flatStyle = FlatStyle.Standard;
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.GroupBox"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.Windows.Forms.GroupBox'/> class.
        ///    </para>
        /// </devdoc>
        public GroupBox() : base() {
            // this class overrides GetPreferredSizeCore, let Control automatically cache the result
            SetState2(STATE2_USEPREFERREDSIZECACHE, true);  
 
            SetStyle(ControlStyles.ContainerControl, true);
            SetStyle(ControlStyles.SupportsTransparentBackColor |
                     ControlStyles.UserPaint |
                     ControlStyles.ResizeRedraw, OwnerDraw);
 
            SetStyle(ControlStyles.Selectable, false);
            TabStop = false;
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.AllowDrop"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether the control will allow drag and
        ///       drop operations and events to be used.
        ///    </para>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public override bool AllowDrop {
            get {
                return base.AllowDrop;
            }
            set {
                base.AllowDrop = value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.AutoSize"]/*' />
        /// <devdoc>
        ///    <para> Override to re-expose AutoSize.</para>
        /// </devdoc>
        [Browsable(true), EditorBrowsable(EditorBrowsableState.Always),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public override bool AutoSize
        {
            get
            {
                return base.AutoSize;
            }
            set
            {
                base.AutoSize = value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.AutoSizeChanged"]/*' />
        [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)]
        [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
        new public event EventHandler AutoSizeChanged
        {
            add
            {
                base.AutoSizeChanged += value;
            }
            remove
            {
                base.AutoSizeChanged -= value;
            }
        }
 
        /// <devdoc>
        ///     Allows the control to optionally shrink when AutoSize is true.
        /// </devdoc>
        [
        SRDescription(SR.ControlAutoSizeModeDescr),
        SRCategory(SR.CatLayout),        
        Browsable(true),
        DefaultValue(AutoSizeMode.GrowOnly),
        Localizable(true)
        ]
        public AutoSizeMode AutoSizeMode {
            get {
                return GetAutoSizeMode();
            }
            set {
                if (!ClientUtils.IsEnumValid(value, (int)value, (int)AutoSizeMode.GrowAndShrink, (int)AutoSizeMode.GrowOnly)){
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(AutoSizeMode));
                }
                
                if (GetAutoSizeMode() != value) {
                    SetAutoSizeMode(value);
                    if(ParentInternal != null) {
                        // DefaultLayout does not keep anchor information until it needs to.  When
                        // AutoSize became a common property, we could no longer blindly call into
                        // DefaultLayout, so now we do a special InitLayout just for DefaultLayout.
                        if(ParentInternal.LayoutEngine == DefaultLayout.Instance) {
                            ParentInternal.LayoutEngine.InitLayout(this, BoundsSpecified.Size);
                        }
                        LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.AutoSize);
                    }
                }
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.CreateParams"]/*' />
        protected override CreateParams CreateParams {
            [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
            get {
                CreateParams cp = base.CreateParams;
                if (!OwnerDraw) {
                    cp.ClassName = "BUTTON";
                    cp.Style |= NativeMethods.BS_GROUPBOX;
                }
                else {
                    // if we swap back to a different flat style
                    // we need to reset these guys.
                    cp.ClassName = null;
                    cp.Style &= ~NativeMethods.BS_GROUPBOX;
                }
                cp.ExStyle |= NativeMethods.WS_EX_CONTROLPARENT;
 
                return cp;
            }
        }
 
        /// <devdoc>
        ///     Set the default Padding to 3 so that it is consistent with Everett
        /// </devdoc>
        protected override Padding DefaultPadding {
            get {
                return new Padding(3);
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.DefaultSize"]/*' />
        /// <devdoc>
        ///     Deriving classes can override this to configure a default size for their control.
        ///     This is more efficient than setting the size in the control's constructor.
        /// </devdoc>
        protected override Size DefaultSize {
            get {
                return new Size(200, 100);
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.DisplayRectangle"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Gets a rectangle that represents the
        ///       dimensions of the <see cref='System.Windows.Forms.GroupBox'/>
        ///       .
        ///    </para>
        /// </devdoc>
        public override Rectangle DisplayRectangle {
            get {
                Size size = ClientSize;
 
                if (fontHeight == -1) {
                    fontHeight = (int)Font.Height;
                    cachedFont = Font;                        
                }
                else if (!object.ReferenceEquals(cachedFont, Font)) {
                    // Must also cache font identity here because
                    // we need to provide an accurate DisplayRectangle
                    // picture even before the OnFontChanged event bubbles
                    // through.
                    fontHeight = (int)Font.Height;
                    cachedFont = Font;
                }
 
                
                //for efficiency, so that we don't need to read property store four times
                Padding padding = Padding;
                return new Rectangle(padding.Left, fontHeight + padding.Top, Math.Max(size.Width - padding.Horizontal, 0), Math.Max(size.Height - fontHeight - padding.Vertical, 0));
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.FlatStyle"]/*' />
        [
            SRCategory(SR.CatAppearance),
            DefaultValue(FlatStyle.Standard),
            SRDescription(SR.ButtonFlatStyleDescr)
        ]
        public FlatStyle FlatStyle {
            get {
                return flatStyle;
            }
            set {
                //valid values are 0x0 to 0x3
                if (!ClientUtils.IsEnumValid(value, (int)value, (int)FlatStyle.Flat, (int)FlatStyle.System))
                {
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(FlatStyle));
                }
 
                if (flatStyle != value) {
 
                    bool originalOwnerDraw = OwnerDraw;
                    flatStyle = value;
 
                    // In CreateParams, we pick our class style based on OwnerDraw
                    // if this has changed we need to recreate
                    bool needRecreate = (OwnerDraw != originalOwnerDraw);
 
                    SetStyle(ControlStyles.ContainerControl, true);
 
                    SetStyle(ControlStyles.SupportsTransparentBackColor |
                             ControlStyles.UserPaint |
                             ControlStyles.ResizeRedraw |
                             ControlStyles.UserMouse, OwnerDraw);
 
                    if (needRecreate) {
                        RecreateHandle();
                    }
                    else {
                        Refresh();
                    }
 
 
                }
            }
        }
 
        private bool OwnerDraw {
            get {
                return FlatStyle != FlatStyle.System;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.TabStop"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether the user may
        ///       press the TAB key to give the focus to the <see cref='System.Windows.Forms.GroupBox'/>
        ///       .
        ///
        ///    </para>
        /// </devdoc>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        new public bool TabStop {
            get {
                return base.TabStop;
            }
            set {
                base.TabStop = value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.TabStopChanged"]/*' />
        /// <internalonly/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        new public event EventHandler TabStopChanged {
            add {
                base.TabStopChanged += value;
            }
            remove {
                base.TabStopChanged -= value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.Text"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [
        Localizable(true)
        ]
        public override string Text {
            get{
                return base.Text;
            }
            set {
               // the GroupBox controls immediately draws when teh WM_SETTEXT comes through, but
               // does so in the wrong font, so we suspend that behavior, and then
               // invalidate.
               bool suspendRedraw = this.Visible;
               try {
                    if (suspendRedraw && IsHandleCreated) {
                        SendMessage(NativeMethods.WM_SETREDRAW, 0, 0);
                    }
                    base.Text = value;
               }
               finally {
                    if (suspendRedraw && IsHandleCreated) {
                        SendMessage(NativeMethods.WM_SETREDRAW, 1, 0);
                    }
               }
               Invalidate(true);
            }
        }
 
        /// <devdoc>
        ///     Determines whether to use compatible text rendering engine (GDI+) or not (GDI).
        /// </devdoc>
        [
        DefaultValue(false),
        SRCategory(SR.CatBehavior),
        SRDescription(SR.UseCompatibleTextRenderingDescr)
        ]
        public bool UseCompatibleTextRendering {
            get{
                return base.UseCompatibleTextRenderingInt;
            }
            set{
                base.UseCompatibleTextRenderingInt = value;
            }
        }
 
        /// <devdoc>
        ///     Determines whether the control supports rendering text using GDI+ and GDI.
        ///     This is provided for container controls to iterate through its children to set UseCompatibleTextRendering to the same
        ///     value if the child control supports it.
        /// </devdoc>
        internal override bool SupportsUseCompatibleTextRendering {
            get {
                return true;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.Click"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public new event EventHandler Click {
            add {
                base.Click += value;
            }
            remove {
                base.Click -= value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.MouseClick"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public new event MouseEventHandler MouseClick {
            add {
                base.MouseClick += value;
            }
            remove {
                base.MouseClick -= value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.DoubleClick"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public new event EventHandler DoubleClick {
            add {
                base.DoubleClick += value;
            }
            remove {
                base.DoubleClick -= value;
            }
        }
 
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.MouseDoubleClick"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public new event MouseEventHandler MouseDoubleClick {
            add {
                base.MouseDoubleClick += value;
            }
            remove {
                base.MouseDoubleClick -= value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.KeyUp"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public new event KeyEventHandler KeyUp {
            add {
                base.KeyUp += value;
            }
            remove {
                base.KeyUp -= value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.KeyDown"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public new event KeyEventHandler KeyDown {
            add {
                base.KeyDown += value;
            }
            remove {
                base.KeyDown -= value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.KeyPress"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public new event KeyPressEventHandler KeyPress {
            add {
                base.KeyPress += value;
            }
            remove {
                base.KeyPress -= value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.MouseDown"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public new event MouseEventHandler MouseDown {
            add {
                base.MouseDown += value;
            }
            remove {
                base.MouseDown -= value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.MouseUp"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public new event MouseEventHandler MouseUp {
            add {
                base.MouseUp += value;
            }
            remove {
                base.MouseUp -= value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.MouseMove"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public new event MouseEventHandler MouseMove {
            add {
                base.MouseMove += value;
            }
            remove {
                base.MouseMove -= value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.MouseEnter"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public new event EventHandler MouseEnter {
            add {
                base.MouseEnter += value;
            }
            remove {
                base.MouseEnter -= value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.MouseLeave"]/*' />
        /// <internalonly/><hideinheritance/>
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
        public new event EventHandler MouseLeave {
            add {
                base.MouseLeave += value;
            }
            remove {
                base.MouseLeave -= value;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.OnPaint"]/*' />
        /// <internalonly/>
        protected override void OnPaint(PaintEventArgs e) {
            
        
            // BACKCOMPAT HACK:
            // Why the Height/Width >= 10 check? This is because uxtheme doesn't seem to handle those cases
            // similar to what we do for the non-themed case, so if someone is using the groupbox as a
            // separator, their app will look weird in Whidbey. We render the old way in these cases.
            if (Application.RenderWithVisualStyles && Width >= 10 && Height >= 10) {
                GroupBoxState gbState = Enabled ? GroupBoxState.Normal : GroupBoxState.Disabled;
                TextFormatFlags textFlags = TextFormatFlags.Default | TextFormatFlags.TextBoxControl | TextFormatFlags.WordBreak | TextFormatFlags.PreserveGraphicsTranslateTransform | TextFormatFlags.PreserveGraphicsClipping;
 
                if (!ShowKeyboardCues) {
                    textFlags |= TextFormatFlags.HidePrefix;
                }
 
                if (RightToLeft == RightToLeft.Yes) {
                    textFlags |= (TextFormatFlags.Right | TextFormatFlags.RightToLeft);
                }
 
                // We only pass in the text color if it is explicitly set, else we let the renderer use the
                // color specified by the theme. This is a temporary workaround till we find a good
                // solution for the "default theme color" issue.
                if (ShouldSerializeForeColor() || this.Enabled == false) {
                    Color textcolor = this.Enabled ? ForeColor : TextRenderer.DisabledTextColor(this.BackColor);
                    GroupBoxRenderer.DrawGroupBox(e.Graphics, new Rectangle(0, 0, Width, Height), Text, Font, textcolor, textFlags, gbState);
                }
                else {
                    GroupBoxRenderer.DrawGroupBox(e.Graphics, new Rectangle(0, 0, Width, Height), Text, Font, textFlags, gbState);
                }
            }
            else {
                DrawGroupBox(e);
            }
            base.OnPaint(e); // raise paint event
        }
 
        private void DrawGroupBox(PaintEventArgs e) {
            Graphics graphics = e.Graphics;
            Rectangle textRectangle = ClientRectangle;  // Max text bounding box passed to drawing methods to support RTL.
 
            int textOffset = 8;      // Offset from the left bound.
 
            Color backColor = DisabledColor;
 
            Pen light = new Pen(ControlPaint.Light(backColor, 1.0f));
            Pen dark = new Pen(ControlPaint.Dark(backColor, 0f));
            Size textSize;
 
            textRectangle.X += textOffset;
            textRectangle.Width -= 2 * textOffset;
 
            try {
                if( UseCompatibleTextRendering ){
                    using( Brush textBrush = new SolidBrush(ForeColor)){
                        using( StringFormat format = new StringFormat() ){
                            format.HotkeyPrefix = ShowKeyboardCues ? System.Drawing.Text.HotkeyPrefix.Show : System.Drawing.Text.HotkeyPrefix.Hide;
 
                            // Adjust string format for Rtl controls
 
                            if (RightToLeft == RightToLeft.Yes) {
                                format.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
                            }
 
                            textSize = Size.Ceiling(graphics.MeasureString(Text, Font, textRectangle.Width, format));
 
                            if (Enabled) {
                                graphics.DrawString(Text, Font, textBrush, textRectangle, format);
                            }
                            else {
                                ControlPaint.DrawStringDisabled(graphics, Text, Font, backColor, textRectangle, format);
                            }
                        }
                    }
                }
                else {
                    using( WindowsGraphics wg = WindowsGraphics.FromGraphics(graphics) ){
                        IntTextFormatFlags flags = IntTextFormatFlags.WordBreak | IntTextFormatFlags.TextBoxControl;
 
                        if(!ShowKeyboardCues){
                            flags |= IntTextFormatFlags.HidePrefix;
                        }
 
                        if( RightToLeft == RightToLeft.Yes ){
                            flags |= IntTextFormatFlags.RightToLeft;
                            flags |= IntTextFormatFlags.Right;
                        }
 
                        
                        using (WindowsFont wfont = WindowsGraphicsCacheManager.GetWindowsFont(this.Font)) {
                            textSize = wg.MeasureText(Text, wfont, new Size(textRectangle.Width , int.MaxValue), flags );
                            
                            if( Enabled ) {
                                wg.DrawText(Text, wfont, textRectangle, ForeColor, flags);
                            }
                            else{
                                ControlPaint.DrawStringDisabled(wg, Text, Font, backColor, textRectangle, ((TextFormatFlags) flags));
                            }
                        }
                    }
                }
 
                int textLeft = textOffset;    // Left side of binding box (independent on RTL).
 
                if (RightToLeft == RightToLeft.Yes)
                {
                    textLeft += textRectangle.Width - textSize.Width;
                }
 
                // Math.Min to assure we paint at least a small line.
                int textRight = Math.Min( textLeft + textSize.Width, Width - 6);
 
                int boxTop = FontHeight / 2;
 
                if (SystemInformation.HighContrast && AccessibilityImprovements.Level1) {
                    Color boxColor;
                    if (Enabled) {
                        boxColor = ForeColor;
                    }
                    else {
                        boxColor = SystemColors.GrayText;
                    }
                    bool needToDispose = !boxColor.IsSystemColor;
                    Pen boxPen = null;
                    try {
                        if (needToDispose) {
                            boxPen = new Pen(boxColor);
                        }
                        else {
                            boxPen = SystemPens.FromSystemColor(boxColor);
                        }
 
                        // left
                        graphics.DrawLine(boxPen, 0, boxTop, 0, Height);
                        //bottom
                        graphics.DrawLine(boxPen, 0, Height-1, Width, Height-1);
                        //top-left
                        graphics.DrawLine(boxPen, 0, boxTop, textLeft, boxTop);
                        //top-right
                        graphics.DrawLine(boxPen, textRight, boxTop, Width-1, boxTop);
                        //right
                        graphics.DrawLine(boxPen, Width-1, boxTop, Width-1, Height-1);
                    }
                    finally {
                        if (needToDispose && boxPen != null) {
                            boxPen.Dispose();
                        }
                    }
                }
                else {
                    // left
                    graphics.DrawLine(light, 1, boxTop, 1, Height - 1);
                    graphics.DrawLine(dark, 0, boxTop, 0, Height - 2);
 
                    // bottom
                    graphics.DrawLine(light, 0, Height - 1, Width, Height - 1);
                    graphics.DrawLine(dark, 0, Height - 2, Width - 1, Height - 2);
 
                    // top-left
 
                    graphics.DrawLine(dark, 0, boxTop - 1, textLeft, boxTop - 1);
                    graphics.DrawLine(light, 1, boxTop, textLeft, boxTop);
 
                    // top-right
                    graphics.DrawLine(dark, textRight, boxTop - 1, Width - 2, boxTop - 1);
                    graphics.DrawLine(light, textRight, boxTop, Width - 1, boxTop);
 
                    // right
                    graphics.DrawLine(light, Width - 1, boxTop - 1, Width - 1, Height - 1);
                    graphics.DrawLine(dark, Width - 2, boxTop, Width - 2, Height - 2);
                }
            }
            finally {
                light.Dispose();
                dark.Dispose();
            }
        }
 
        internal override Size GetPreferredSizeCore(Size proposedSize) {
            // Translating 0,0 from ClientSize to actual Size tells us how much space
            // is required for the borders.
            Size borderSize = SizeFromClientSize(Size.Empty);
            Size totalPadding = borderSize + new Size(0,fontHeight) + Padding.Size;
 
            Size prefSize = LayoutEngine.GetPreferredSize(this, proposedSize - totalPadding);
            return prefSize + totalPadding;
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.OnFontChanged"]/*' />
        /// <internalonly/>
        /// <devdoc>
        /// </devdoc>
        protected override void OnFontChanged(EventArgs e) {
            fontHeight = -1;
            cachedFont = null;
            Invalidate();
            base.OnFontChanged(e);
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.ProcessMnemonic"]/*' />
        /// <devdoc>
        ///     We use this to process mnemonics and send them on to the first child
        ///     control.
        /// </devdoc>
        /// <internalonly/>        
        [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
        protected internal override bool ProcessMnemonic(char charCode) {
            if (IsMnemonic(charCode, Text) && CanProcessMnemonic()) {
                // Reviewed: This seems safe, because SelectNextControl does not allow
                // you to step out of the current control - it only selects children. With
                // this assumption, it is okay to assert here.
                IntSecurity.ModifyFocus.Assert();
                try {
                    SelectNextControl(null, true, true, true, false);
                }
                finally {
                    System.Security.CodeAccessPermission.RevertAssert();
                }
                return true;
            }
            return false;
        }
        [SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")]
        protected override void ScaleControl(SizeF factor, BoundsSpecified specified) {
            
            if (factor.Width != 1F && factor.Height != 1F) {
                // VSWHIDBEY 471445: make sure when we're scaling by non-unity to clear the font cache
                // as the font has likely changed, but we dont know it yet as OnFontChanged has yet to
                // be called on us by our parent.
                fontHeight = -1;
                cachedFont = null;
            }
            base.ScaleControl(factor, specified);
        }
 
        internal override bool SupportsUiaProviders {
            get {
                return AccessibilityImprovements.Level3 && !DesignMode;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.ToString"]/*' />
        /// <devdoc>
        ///     Returns a string representation for this control.
        /// </devdoc>
        /// <internalonly/>
        public override string ToString() {
 
            string s = base.ToString();
            return s + ", Text: " + Text;
        }
 
        /// <summary>
        ///     The Windows group box doesn't erase the background so we do it
        ///     ourselves here.
        /// </summary>
        /// <internalonly/>
        private void WmEraseBkgnd(ref Message m) {
            NativeMethods.RECT rect = new NativeMethods.RECT();
            SafeNativeMethods.GetClientRect(new HandleRef(this, Handle), ref rect);
            using (Graphics graphics = Graphics.FromHdcInternal(m.WParam)) {
                using (Brush b = new SolidBrush(BackColor)) {
                    graphics.FillRectangle(b, rect.left, rect.top,
                                           rect.right - rect.left, rect.bottom - rect.top);
                }
            }
            m.Result = (IntPtr)1;
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.WndProc"]/*' />
        /// <internalonly/>
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
        protected override void WndProc(ref Message m) {
       
            if (OwnerDraw) {
                base.WndProc(ref m);
                return;
            }
 
            switch (m.Msg) {
                case NativeMethods.WM_ERASEBKGND:
                case NativeMethods.WM_PRINTCLIENT:
                    WmEraseBkgnd(ref m);
                    break;
                case NativeMethods.WM_GETOBJECT:
                    base.WndProc(ref m);
 
                    // Force MSAA to always treat a group box as a custom window. This ensures its child controls
                    // will always be exposed through MSAA. Reason: When FlatStyle=System, we map down to the Win32
                    // "Button" window class to get OS group box rendering; but the OS does not expose the children
                    // of buttons to MSAA (beacuse it assumes buttons won't have children).
                    if (unchecked((int)(long)m.LParam) == NativeMethods.OBJID_QUERYCLASSNAMEIDX) {
                        m.Result = IntPtr.Zero;
                    }
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }
 
        /// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.CreateAccessibilityInstance"]/*' />
        protected override AccessibleObject CreateAccessibilityInstance() {
            return new GroupBoxAccessibleObject(this);
        }
 
        [System.Runtime.InteropServices.ComVisible(true)]
        internal class GroupBoxAccessibleObject : ControlAccessibleObject
        {
            internal GroupBoxAccessibleObject(GroupBox owner) : base(owner) {
            }
 
            public override AccessibleRole Role {
                get {
                    AccessibleRole role = Owner.AccessibleRole;
                    if (role != AccessibleRole.Default) {
                        return role;
                    }
                    return AccessibleRole.Grouping;
                }
            }
 
            internal override bool IsIAccessibleExSupported() {
                if (AccessibilityImprovements.Level3) {
                    return true;
                }
 
                return base.IsIAccessibleExSupported();
            }
 
            internal override object GetPropertyValue(int propertyID) {
                switch (propertyID) {
                    case NativeMethods.UIA_ControlTypePropertyId:
                        return NativeMethods.UIA_GroupControlTypeId;
                    case NativeMethods.UIA_IsKeyboardFocusablePropertyId:
                        return true;
                }
 
                return base.GetPropertyValue(propertyID);
            }
        }
    }
 
}