ToolStripSplitButton.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / WinForms / Managed / System / WinForms / ToolStripSplitButton.cs / 1305376 / ToolStripSplitButton.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

 
namespace System.Windows.Forms { 
    using System;
    using System.Security; 
    using System.Security.Permissions;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Drawing.Imaging; 
    using System.ComponentModel;
    using System.Windows.Forms.Design; 
    using System.Diagnostics.CodeAnalysis; 
    using System.Globalization;
    using System.Diagnostics; 
    using System.Windows.Forms.Layout;
    using System.Runtime.Versioning;

    ///  
    /// 
    [ 
    ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip), 
    DefaultEvent("ButtonClick")
    ] 
    public class ToolStripSplitButton : ToolStripDropDownItem {

        private ToolStripItem                    defaultItem                =  null;
        private ToolStripSplitButtonButton       splitButtonButton          =  null; 
        private Rectangle                     dropDownButtonBounds       =  Rectangle.Empty;
        private ToolStripSplitButtonButtonLayout splitButtonButtonLayout    =  null; 
        private int                           dropDownButtonWidth        =  0; 
        private int                           splitterWidth              =  1;
        private Rectangle                     splitterBounds             =  Rectangle.Empty; 
        private byte                          openMouseId                =  0;
        private long lastClickTime = 0;

        private const int DEFAULT_DROPDOWN_WIDTH = 11; 

        private static readonly object EventDefaultItemChanged                         = new object(); 
        private static readonly object EventButtonClick                                = new object(); 
        private static readonly object EventButtonDoubleClick                          = new object();
        private static readonly object EventDropDownOpened                             = new object(); 
        private static readonly object EventDropDownClosed                             = new object();

        /// 
        ///  
        /// Summary of ToolStripSplitButton.
        ///  
        public ToolStripSplitButton() { 
            Initialize(); // all additional work should be done in Initialize
        } 
        public ToolStripSplitButton(string text):base(text,null,(EventHandler)null) {
            Initialize();
        }
        public ToolStripSplitButton(Image image):base(null,image,(EventHandler)null) { 
            Initialize();
        } 
        public ToolStripSplitButton(string text, Image image):base(text,image,(EventHandler)null) { 
            Initialize();
        } 
        public ToolStripSplitButton(string text, Image image, EventHandler onClick):base(text,image,onClick) {
            Initialize();
        }
        public ToolStripSplitButton(string text, Image image, EventHandler onClick, string name) :base(text,image,onClick,name){ 
            Initialize();
        } 
        public ToolStripSplitButton(string text, Image image, params ToolStripItem[] dropDownItems):base(text,image,dropDownItems) { 
            Initialize();
        } 

        [DefaultValue(true)]
        public new bool AutoToolTip {
            get { 
                return base.AutoToolTip;
            } 
            set { 
                base.AutoToolTip = value;
            } 
        }


        ///  
        /// 
        /// Summary of ToolStripSplitButton. 
        ///  
        [Browsable(false)]
        public Rectangle ButtonBounds { 
            get {
                //Rectangle bounds = SplitButtonButton.Bounds;
                //bounds.Offset(this.Bounds.Location);
                return SplitButtonButton.Bounds; 
            }
        } 
 
        /// 
        ///  
        /// Summary of ButtonPressed.
        /// 
        [Browsable(false)]
        public bool ButtonPressed { 
            get {
                return SplitButtonButton.Pressed; 
 
            }
        } 

        /// 
        /// 
        /// Summary of ButtonPressed. 
        /// 
        [Browsable(false)] 
        public bool ButtonSelected { 
            get {
                return SplitButtonButton.Selected || DropDownButtonPressed; 
            }
        }

        ///  
        /// 
        /// Occurs when the button portion of a split button is clicked. 
        ///  
        [
        SRCategory(SR.CatAction), 
        SRDescription(SR.ToolStripSplitButtonOnButtonClickDescr)
        ]
        public event EventHandler ButtonClick {
            add { 
                Events.AddHandler(EventButtonClick, value);
            } 
            remove { 
                Events.RemoveHandler(EventButtonClick, value);
            } 
        }
        /// 
        /// 
        /// Occurs when the utton portion of a split button  is double clicked. 
        /// 
        [ 
        SRCategory(SR.CatAction), 
        SRDescription(SR.ToolStripSplitButtonOnButtonDoubleClickDescr)
        ] 
        public event EventHandler ButtonDoubleClick {
            add {
                Events.AddHandler(EventButtonDoubleClick, value);
            } 
            remove {
                Events.RemoveHandler(EventButtonDoubleClick, value); 
            } 
        }
 

        protected override bool DefaultAutoToolTip {
            get {
               return true; 
            }
        } 
 
        /// 
        ///  
        /// Summary of DefaultItem.
        /// 
        [DefaultValue(null), Browsable(false)]
        public ToolStripItem DefaultItem { 
            get {
                return defaultItem; 
            } 
            set {
                if (defaultItem != value) { 
                    OnDefaultItemChanged(new EventArgs());
                    defaultItem = value;
                }
            } 
        }
 
 
        /// 
        ///  
        /// Occurs when the default item has changed
        /// 
        [
        SRCategory(SR.CatAction), 
        SRDescription(SR.ToolStripSplitButtonOnDefaultItemChangedDescr)
        ] 
        public event EventHandler DefaultItemChanged { 
            add {
                Events.AddHandler(EventDefaultItemChanged, value); 
            }
            remove {
                Events.RemoveHandler(EventDefaultItemChanged, value);
            } 
        }
 	 
        ///  
        /// 
        /// specifies the default behavior of these items on ToolStripDropDowns when clicked. 
        /// 
        internal protected override bool DismissWhenClicked {
            get {
                return DropDown.Visible != true; 
            }
 
        } 

        internal override Rectangle DropDownButtonArea { 
               get { return this.DropDownButtonBounds; }
        }

        ///  
        /// 
        /// The bounds of the DropDown in ToolStrip coordinates. 
        ///  
        [Browsable(false)]
        public Rectangle DropDownButtonBounds { 
            get {
                 return dropDownButtonBounds;
            }
 
        }
        ///  
        ///  
        /// Summary of DropDownButtonBounds.
        ///  
        [Browsable(false)]
        public bool DropDownButtonPressed {
            get {
                // 
                return DropDown.Visible;
            } 
        } 
        /// 
        ///  
        /// Summary of DropDownButtonSelected.
        /// 
        [Browsable(false)]
        public bool DropDownButtonSelected{ 
            get {
                return this.Selected; 
            } 
        }
        ///  
        /// 
        /// Summary of DropDownButtonWidth.
        /// 
        [ 
        SRCategory(SR.CatLayout),
        SRDescription(SR.ToolStripSplitButtonDropDownButtonWidthDescr) 
        ] 
        public int DropDownButtonWidth {
            get{ 
                return dropDownButtonWidth;
            }
            set {
                if (value < 0) { 
                    // throw if less than 0.
                    throw new ArgumentOutOfRangeException("DropDownButtonWidth", SR.GetString(SR.InvalidLowBoundArgumentEx, "DropDownButtonWidth", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); 
                } 

                if (dropDownButtonWidth != value) { 
                    dropDownButtonWidth = value;
                    InvalidateSplitButtonLayout();
                    InvalidateItemLayout(PropertyNames.DropDownButtonWidth, true);
                } 
             }
        } 
 
        /// 
        /// This is here for serialization purposes. 
        /// 
        private int DefaultDropDownButtonWidth {
            get {
                // lets start off with a size roughly equivilant to a combobox dropdown 
                return DEFAULT_DROPDOWN_WIDTH;
            } 
        } 

 

        /// 
        /// Just used as a convenience to help manage layout
        ///  
        private ToolStripSplitButtonButton SplitButtonButton {
            get { 
                if (splitButtonButton == null) { 
                    splitButtonButton = new ToolStripSplitButtonButton(this);
                } 
                splitButtonButton.Image = this.Image;
                splitButtonButton.Text = this.Text;
                splitButtonButton.BackColor = this.BackColor;
                splitButtonButton.ForeColor = this.ForeColor; 
                splitButtonButton.Font = this.Font;
                splitButtonButton.ImageAlign = this.ImageAlign; 
                splitButtonButton.TextAlign = this.TextAlign; 
                splitButtonButton.TextImageRelation = this.TextImageRelation;
                return splitButtonButton; 
            }
        }
        /// 
        /// Summary of SplitButtonButtonLayout. 
        /// 	
        internal ToolStripItemInternalLayout SplitButtonButtonLayout { 
            get { 
                // For preferred size caching reasons, we need to keep our two
                // internal layouts (button, dropdown button) in [....]. 

                if (InternalLayout != null /*if layout is invalid - calls CreateInternalLayout - which resets splitButtonButtonLayout to null*/
                    && splitButtonButtonLayout == null) {
                    splitButtonButtonLayout = new ToolStripSplitButtonButtonLayout(this); 
                }
                return splitButtonButtonLayout; 
            } 
        }
 
        /// 
        /// 
        /// the width of the separator between the default and drop down button
        ///  
        [
        SRDescription(SR.ToolStripSplitButtonSplitterWidthDescr), 
        SRCategory(SR.CatLayout), 
        Browsable(false),
        EditorBrowsable(EditorBrowsableState.Advanced) 
        ]
        internal int SplitterWidth {
            get {
                return splitterWidth; 
            }
            set { 
                if (value < 0) { 
                    splitterWidth = 0;
                } 
                else {
                    splitterWidth = value;
                }
                InvalidateSplitButtonLayout(); 
            }
        } 
        ///  
        /// 
        /// the boundaries of the separator between the default and drop down button, exposed for custom 
        /// painting purposes.
        /// 
        [Browsable(false)]
        public Rectangle SplitterBounds { 
            get {
                return splitterBounds; 
            } 
        }
        ///  
        /// Summary of CalculateLayout.
        /// 	
        private void CalculateLayout() {
 
            // Figure out where the DropDown image goes.
            Rectangle dropDownButtonBounds = new Rectangle(Point.Empty, this.Size); 
            Rectangle splitButtonButtonBounds = Rectangle.Empty; 
			
 
            dropDownButtonBounds = new Rectangle(Point.Empty, new Size(Math.Min(this.Width, DropDownButtonWidth), this.Height));
   			
            // Figure out the height and width of the selected item.
            int splitButtonButtonWidth = Math.Max(0, this.Width - dropDownButtonBounds.Width); 
            int splitButtonButtonHeight = Math.Max(0, this.Height);
 
            splitButtonButtonBounds = new Rectangle(Point.Empty, new Size(splitButtonButtonWidth, splitButtonButtonHeight)); 

            // grow the selected item by one since we're overlapping the borders. 
            splitButtonButtonBounds.Width -= splitterWidth;

            if (this.RightToLeft == RightToLeft.No) {
                // the dropdown button goes on the right 
                dropDownButtonBounds.Offset(splitButtonButtonBounds.Right+splitterWidth, 0);
                splitterBounds = new Rectangle(splitButtonButtonBounds.Right, splitButtonButtonBounds.Top, splitterWidth, splitButtonButtonBounds.Height); 
            } 
            else {
                // the split button goes on the right. 
                splitButtonButtonBounds.Offset(DropDownButtonWidth+splitterWidth, 0);
                splitterBounds = new Rectangle(dropDownButtonBounds.Right, dropDownButtonBounds.Top, splitterWidth, dropDownButtonBounds.Height);

            } 

            this.SplitButtonButton.SetBounds(splitButtonButtonBounds); 
            this.SetDropDownButtonBounds(dropDownButtonBounds); 

        } 

        protected override AccessibleObject CreateAccessibilityInstance() {
           return new ToolStripSplitButtonAccessibleObject(this);
       } 

        protected override ToolStripDropDown CreateDefaultDropDown() { 
             // AutoGenerate a Winbar DropDown - set the property so we hook events 
              return new ToolStripDropDownMenu(this, /*isAutoGenerated=*/true);
         } 


        internal override ToolStripItemInternalLayout CreateInternalLayout() {
            // whenever the master layout is invalidated - invalidate the splitbuttonbutton layout. 
            this.splitButtonButtonLayout = null;
            return new ToolStripItemInternalLayout(this); 
 
        }
 
        /// 
        public override Size GetPreferredSize(Size constrainingSize) {
            Size preferredSize = SplitButtonButtonLayout.GetPreferredSize(constrainingSize);
            preferredSize.Width += DropDownButtonWidth + SplitterWidth + Padding.Horizontal; 
            return preferredSize;
        } 
 
        /// 
        /// Summary of InvalidateSplitButtonLayout. 
        /// 	
        private void InvalidateSplitButtonLayout() {
            this.splitButtonButtonLayout = null;	
            CalculateLayout(); 
        }
 
        private void Initialize() { 
            dropDownButtonWidth = DefaultDropDownButtonWidth;
            SupportsSpaceKey = true; 
        }

        [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
        protected internal override bool ProcessDialogKey(Keys keyData) { 
            if (Enabled && (keyData == Keys.Enter || (SupportsSpaceKey && keyData == Keys.Space))) {
               PerformButtonClick(); 
               return true; 
            }
 
            return base.ProcessDialogKey(keyData);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'charCode' matches control.cs
        [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] 
        protected internal override bool ProcessMnemonic(char charCode) {
           // checking IsMnemonic is not necessary - toolstrip does this for us 
           PerformButtonClick(); 
           return true;
        } 

        /// 
        /// 
        /// called when the button portion of a split button is clicked 
        /// if there is a default item, this will route the click to the default item
        ///  
        protected virtual void OnButtonClick(System.EventArgs e) { 

            if (DefaultItem != null) { 
                DefaultItem.FireEvent(ToolStripItemEventType.Click);
            }

            EventHandler handler = (EventHandler)Events[EventButtonClick]; 
            if (handler != null) handler(this, e);
 
        } 

        ///  
        /// 
        /// called when the button portion of a split button is double clicked
        /// if there is a default item, this will route the double click to the default item
        ///  
        public virtual void OnButtonDoubleClick(System.EventArgs e) {
            if (DefaultItem != null) { 
                DefaultItem.FireEvent(ToolStripItemEventType.DoubleClick); 
            }
 
            EventHandler handler = (EventHandler)Events[EventButtonDoubleClick];
            if (handler != null) handler(this,e);
        }
 

        ///  
        ///  
        /// Inheriting classes should override this method to handle this event.
        ///  
        protected virtual void OnDefaultItemChanged(EventArgs e) {
            InvalidateSplitButtonLayout();
            if (CanRaiseEvents) {
                EventHandler eh = Events[EventDefaultItemChanged] as EventHandler; 
                if (eh != null) {
                    eh(this, e); 
                } 
            }
 
        }

        /// 
        ///  
        /// Summary of OnMouseDown.
        ///  
        protected override void OnMouseDown(MouseEventArgs e) { 
		
            if (DropDownButtonBounds.Contains(e.Location)) { 
                if (e.Button == MouseButtons.Left) {

                    if (!DropDown.Visible) {
                        Debug.Assert(ParentInternal != null, "Parent is null here, not going to get accurate ID"); 
                        openMouseId = (ParentInternal == null) ? (byte)0: ParentInternal.GetMouseId();
                        this.ShowDropDown(/*mousePress = */true); 
                    } 
                }
            } 
            else {
                SplitButtonButton.Push(true);
            }
 
        }
 
 
        /// 
        ///  
        /// Summary of OnMouseUp.
        /// 
        protected override void OnMouseUp(MouseEventArgs e) {
            if (!Enabled) { 
                return;
            } 
 

            SplitButtonButton.Push(false); 

            if (DropDownButtonBounds.Contains(e.Location)) {
                if (e.Button == MouseButtons.Left) {
                    if (DropDown.Visible) { 
                        Debug.Assert(ParentInternal != null, "Parent is null here, not going to get accurate ID");
                        byte closeMouseId = (ParentInternal == null) ? (byte)0: ParentInternal.GetMouseId(); 
                        if (closeMouseId != openMouseId) { 
                            openMouseId = 0;  // reset the mouse id, we should never get this value from toolstrip.
                            ToolStripManager.ModalMenuFilter.CloseActiveDropDown(DropDown, ToolStripDropDownCloseReason.AppClicked); 
                            Select();
                       }
                    }
                } 
            }
            Point clickPoint = new Point(e.X, e.Y); 
            if ((e.Button == MouseButtons.Left) && this.SplitButtonButton.Bounds.Contains(clickPoint)) { 
                bool shouldFireDoubleClick = false;
                if (DoubleClickEnabled) { 
                    long newTime = DateTime.Now.Ticks;
                    long deltaTicks = newTime - lastClickTime;
                    lastClickTime = newTime;
                    // use >= for cases where the succession of click events is so fast it's not picked up by 
                    // DateTime resolution.
                    Debug.Assert(deltaTicks >= 0, "why are deltaticks less than zero? thats some mighty fast clicking"); 
                    // if we've seen a mouse up less than the double click time ago, we should fire. 
                    if (deltaTicks >= 0 && deltaTicks < DoubleClickTicks) {
                        shouldFireDoubleClick = true; 
                    }
                }
                if (shouldFireDoubleClick) {
                    OnButtonDoubleClick(new System.EventArgs()); 
                    // VSWhidbey 486983: if we actually fired DoubleClick - reset the lastClickTime.
                    lastClickTime = 0; 
                } 
                else {
                    OnButtonClick(new System.EventArgs()); 
                }
            }

        } 
        /// 
        protected override void OnMouseLeave(EventArgs e) { 
            openMouseId = 0;  // reset the mouse id, we should never get this value from toolstrip. 
            SplitButtonButton.Push(false);
            base.OnMouseLeave(e); 
        }
      	
        /// 
        ///  
        /// Summary of OnRightToLeftChanged.
        ///  
        protected override void OnRightToLeftChanged(EventArgs e) { 
            base.OnRightToLeftChanged(e);
            InvalidateSplitButtonLayout();			 
        }
        /// 
        /// 
        /// Summary of OnPaint. 
        /// 
        ///  
        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { 

             ToolStripRenderer renderer = this.Renderer; 
             if (renderer != null) {
                 InvalidateSplitButtonLayout();
                 Graphics g = e.Graphics;
 
                 renderer.DrawSplitButton(new ToolStripItemRenderEventArgs(g, this));
 
                 if ((DisplayStyle & ToolStripItemDisplayStyle.Image) != ToolStripItemDisplayStyle.None)  { 
                     renderer.DrawItemImage(new ToolStripItemImageRenderEventArgs(g, this, SplitButtonButtonLayout.ImageRectangle));
                 } 

                 if ((DisplayStyle & ToolStripItemDisplayStyle.Text) != ToolStripItemDisplayStyle.None) {
                      renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(g, this, SplitButtonButton.Text, SplitButtonButtonLayout.TextRectangle, this.ForeColor, this.Font, SplitButtonButtonLayout.TextFormat));
                 } 
             }
        } 
 
        public void PerformButtonClick() {
            if (Enabled && Available) { 
                PerformClick();
                OnButtonClick(EventArgs.Empty);
            }
        } 

        ///  
        ///  
        /// Resets the RightToLeft to be the default.
        ///  
        [EditorBrowsable(EditorBrowsableState.Never)]
        public virtual void ResetDropDownButtonWidth() {
            DropDownButtonWidth = DefaultDropDownButtonWidth;
        } 

        ///  
        /// Summary of SetDropDownBounds. 
        /// 
        private void SetDropDownButtonBounds(Rectangle rect) { 
            dropDownButtonBounds = rect;
        }
        /// 
        /// Determines if the  property needs to be persisted. 
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)] 
        internal virtual bool ShouldSerializeDropDownButtonWidth() { 
            return  (DropDownButtonWidth != DefaultDropDownButtonWidth);
        } 

        /// 
        ///  This class represents the item to the left of the dropdown [ A |v]  (e.g the "A")
        ///  It exists so that we can use our existing methods for text and image layout 
        ///  and have a place to stick certain state information like pushed and selected
        ///  Note since this is NOT an actual item hosted on the Winbar - it wont get things 
        ///  like MouseOver, wont be laid out by the ToolStrip, etc etc.  This is purely internal 
        ///  convenience.
        ///  
        private class ToolStripSplitButtonButton : ToolStripButton {

            private ToolStripSplitButton owner = null;
 
            public ToolStripSplitButtonButton(ToolStripSplitButton owner) {
                   this.owner = owner; 
            } 

            public override bool Enabled { 
                get {
                    return owner.Enabled;
                }
                set { 
                    // do nothing
                } 
            } 

 
            public override ToolStripItemDisplayStyle DisplayStyle {
                get {
                    return owner.DisplayStyle;
                } 
                set {
                    // do nothing 
                } 
            }
 
            public override Padding Padding {
                get {
                    return this.owner.Padding;
                } 
                set {
                    // do nothing 
                } 
            }
 

            public override ToolStripTextDirection TextDirection {
                get {
                    return owner.TextDirection; 
                }
            } 
 

            public override Image Image { 
                [ResourceExposure(ResourceScope.Machine)]
                [ResourceConsumption(ResourceScope.Machine)]
                get {
                    if ((owner.DisplayStyle & ToolStripItemDisplayStyle.Image) == ToolStripItemDisplayStyle.Image) { 
                        return owner.Image;
                    } 
                    else { 
                        return null;
                    } 
                }
                set {
                    // do nothing
                } 
            }
 
            public override bool Selected { 
                get {
 
                    if (owner != null) {
                        return owner.Selected;
                    }
                    return base.Selected; 
                }
            } 
 
            public override string Text {
                get { 
                    if ((owner.DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) {
                        return owner.Text;
                    }
                    else { 
                        return null;
                    } 
                } 
                set {
                    // do nothing 
                }
            }

        } 

        ///  
        ///  This class performs internal layout for the "split button button" portion of a split button. 
        ///  Its main job is to make sure the inner button has the same parent as the split button, so
        ///  that layout can be performed using the correct graphics context. 
        /// 
        private class ToolStripSplitButtonButtonLayout : ToolStripItemInternalLayout {

            ToolStripSplitButton owner; 

            public ToolStripSplitButtonButtonLayout(ToolStripSplitButton owner) : base(owner.SplitButtonButton) { 
                this.owner = owner; 
            }
 
            protected override ToolStripItem Owner {
                get { return owner; }
            }
 
            protected override ToolStrip ParentInternal {
                get { 
                    return owner.ParentInternal; 
                }
            } 
            public override Rectangle ImageRectangle {
                get {
                    Rectangle imageRect = base.ImageRectangle;
                    // translate to ToolStripItem coordinates 
                    imageRect.Offset(owner.SplitButtonButton.Bounds.Location);
                    return imageRect; 
                } 
            }
 
            public override Rectangle TextRectangle {
                get {
                    Rectangle textRect = base.TextRectangle;
                    // translate to ToolStripItem coordinates 
                    textRect.Offset(owner.SplitButtonButton.Bounds.Location);
                    return textRect; 
                } 
            }
        } 

        /// 
        [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
        public class ToolStripSplitButtonAccessibleObject : ToolStripItem.ToolStripItemAccessibleObject { 
            private ToolStripSplitButton owner;
 
            public ToolStripSplitButtonAccessibleObject(ToolStripSplitButton item) : base(item) { 
                owner = item;
            } 

            /// 
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override void DoDefaultAction() { 
                owner.PerformButtonClick();
            } 
 
        }
 

    }

} 

 
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

 
namespace System.Windows.Forms { 
    using System;
    using System.Security; 
    using System.Security.Permissions;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Drawing.Imaging; 
    using System.ComponentModel;
    using System.Windows.Forms.Design; 
    using System.Diagnostics.CodeAnalysis; 
    using System.Globalization;
    using System.Diagnostics; 
    using System.Windows.Forms.Layout;
    using System.Runtime.Versioning;

    ///  
    /// 
    [ 
    ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip), 
    DefaultEvent("ButtonClick")
    ] 
    public class ToolStripSplitButton : ToolStripDropDownItem {

        private ToolStripItem                    defaultItem                =  null;
        private ToolStripSplitButtonButton       splitButtonButton          =  null; 
        private Rectangle                     dropDownButtonBounds       =  Rectangle.Empty;
        private ToolStripSplitButtonButtonLayout splitButtonButtonLayout    =  null; 
        private int                           dropDownButtonWidth        =  0; 
        private int                           splitterWidth              =  1;
        private Rectangle                     splitterBounds             =  Rectangle.Empty; 
        private byte                          openMouseId                =  0;
        private long lastClickTime = 0;

        private const int DEFAULT_DROPDOWN_WIDTH = 11; 

        private static readonly object EventDefaultItemChanged                         = new object(); 
        private static readonly object EventButtonClick                                = new object(); 
        private static readonly object EventButtonDoubleClick                          = new object();
        private static readonly object EventDropDownOpened                             = new object(); 
        private static readonly object EventDropDownClosed                             = new object();

        /// 
        ///  
        /// Summary of ToolStripSplitButton.
        ///  
        public ToolStripSplitButton() { 
            Initialize(); // all additional work should be done in Initialize
        } 
        public ToolStripSplitButton(string text):base(text,null,(EventHandler)null) {
            Initialize();
        }
        public ToolStripSplitButton(Image image):base(null,image,(EventHandler)null) { 
            Initialize();
        } 
        public ToolStripSplitButton(string text, Image image):base(text,image,(EventHandler)null) { 
            Initialize();
        } 
        public ToolStripSplitButton(string text, Image image, EventHandler onClick):base(text,image,onClick) {
            Initialize();
        }
        public ToolStripSplitButton(string text, Image image, EventHandler onClick, string name) :base(text,image,onClick,name){ 
            Initialize();
        } 
        public ToolStripSplitButton(string text, Image image, params ToolStripItem[] dropDownItems):base(text,image,dropDownItems) { 
            Initialize();
        } 

        [DefaultValue(true)]
        public new bool AutoToolTip {
            get { 
                return base.AutoToolTip;
            } 
            set { 
                base.AutoToolTip = value;
            } 
        }


        ///  
        /// 
        /// Summary of ToolStripSplitButton. 
        ///  
        [Browsable(false)]
        public Rectangle ButtonBounds { 
            get {
                //Rectangle bounds = SplitButtonButton.Bounds;
                //bounds.Offset(this.Bounds.Location);
                return SplitButtonButton.Bounds; 
            }
        } 
 
        /// 
        ///  
        /// Summary of ButtonPressed.
        /// 
        [Browsable(false)]
        public bool ButtonPressed { 
            get {
                return SplitButtonButton.Pressed; 
 
            }
        } 

        /// 
        /// 
        /// Summary of ButtonPressed. 
        /// 
        [Browsable(false)] 
        public bool ButtonSelected { 
            get {
                return SplitButtonButton.Selected || DropDownButtonPressed; 
            }
        }

        ///  
        /// 
        /// Occurs when the button portion of a split button is clicked. 
        ///  
        [
        SRCategory(SR.CatAction), 
        SRDescription(SR.ToolStripSplitButtonOnButtonClickDescr)
        ]
        public event EventHandler ButtonClick {
            add { 
                Events.AddHandler(EventButtonClick, value);
            } 
            remove { 
                Events.RemoveHandler(EventButtonClick, value);
            } 
        }
        /// 
        /// 
        /// Occurs when the utton portion of a split button  is double clicked. 
        /// 
        [ 
        SRCategory(SR.CatAction), 
        SRDescription(SR.ToolStripSplitButtonOnButtonDoubleClickDescr)
        ] 
        public event EventHandler ButtonDoubleClick {
            add {
                Events.AddHandler(EventButtonDoubleClick, value);
            } 
            remove {
                Events.RemoveHandler(EventButtonDoubleClick, value); 
            } 
        }
 

        protected override bool DefaultAutoToolTip {
            get {
               return true; 
            }
        } 
 
        /// 
        ///  
        /// Summary of DefaultItem.
        /// 
        [DefaultValue(null), Browsable(false)]
        public ToolStripItem DefaultItem { 
            get {
                return defaultItem; 
            } 
            set {
                if (defaultItem != value) { 
                    OnDefaultItemChanged(new EventArgs());
                    defaultItem = value;
                }
            } 
        }
 
 
        /// 
        ///  
        /// Occurs when the default item has changed
        /// 
        [
        SRCategory(SR.CatAction), 
        SRDescription(SR.ToolStripSplitButtonOnDefaultItemChangedDescr)
        ] 
        public event EventHandler DefaultItemChanged { 
            add {
                Events.AddHandler(EventDefaultItemChanged, value); 
            }
            remove {
                Events.RemoveHandler(EventDefaultItemChanged, value);
            } 
        }
 	 
        ///  
        /// 
        /// specifies the default behavior of these items on ToolStripDropDowns when clicked. 
        /// 
        internal protected override bool DismissWhenClicked {
            get {
                return DropDown.Visible != true; 
            }
 
        } 

        internal override Rectangle DropDownButtonArea { 
               get { return this.DropDownButtonBounds; }
        }

        ///  
        /// 
        /// The bounds of the DropDown in ToolStrip coordinates. 
        ///  
        [Browsable(false)]
        public Rectangle DropDownButtonBounds { 
            get {
                 return dropDownButtonBounds;
            }
 
        }
        ///  
        ///  
        /// Summary of DropDownButtonBounds.
        ///  
        [Browsable(false)]
        public bool DropDownButtonPressed {
            get {
                // 
                return DropDown.Visible;
            } 
        } 
        /// 
        ///  
        /// Summary of DropDownButtonSelected.
        /// 
        [Browsable(false)]
        public bool DropDownButtonSelected{ 
            get {
                return this.Selected; 
            } 
        }
        ///  
        /// 
        /// Summary of DropDownButtonWidth.
        /// 
        [ 
        SRCategory(SR.CatLayout),
        SRDescription(SR.ToolStripSplitButtonDropDownButtonWidthDescr) 
        ] 
        public int DropDownButtonWidth {
            get{ 
                return dropDownButtonWidth;
            }
            set {
                if (value < 0) { 
                    // throw if less than 0.
                    throw new ArgumentOutOfRangeException("DropDownButtonWidth", SR.GetString(SR.InvalidLowBoundArgumentEx, "DropDownButtonWidth", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); 
                } 

                if (dropDownButtonWidth != value) { 
                    dropDownButtonWidth = value;
                    InvalidateSplitButtonLayout();
                    InvalidateItemLayout(PropertyNames.DropDownButtonWidth, true);
                } 
             }
        } 
 
        /// 
        /// This is here for serialization purposes. 
        /// 
        private int DefaultDropDownButtonWidth {
            get {
                // lets start off with a size roughly equivilant to a combobox dropdown 
                return DEFAULT_DROPDOWN_WIDTH;
            } 
        } 

 

        /// 
        /// Just used as a convenience to help manage layout
        ///  
        private ToolStripSplitButtonButton SplitButtonButton {
            get { 
                if (splitButtonButton == null) { 
                    splitButtonButton = new ToolStripSplitButtonButton(this);
                } 
                splitButtonButton.Image = this.Image;
                splitButtonButton.Text = this.Text;
                splitButtonButton.BackColor = this.BackColor;
                splitButtonButton.ForeColor = this.ForeColor; 
                splitButtonButton.Font = this.Font;
                splitButtonButton.ImageAlign = this.ImageAlign; 
                splitButtonButton.TextAlign = this.TextAlign; 
                splitButtonButton.TextImageRelation = this.TextImageRelation;
                return splitButtonButton; 
            }
        }
        /// 
        /// Summary of SplitButtonButtonLayout. 
        /// 	
        internal ToolStripItemInternalLayout SplitButtonButtonLayout { 
            get { 
                // For preferred size caching reasons, we need to keep our two
                // internal layouts (button, dropdown button) in [....]. 

                if (InternalLayout != null /*if layout is invalid - calls CreateInternalLayout - which resets splitButtonButtonLayout to null*/
                    && splitButtonButtonLayout == null) {
                    splitButtonButtonLayout = new ToolStripSplitButtonButtonLayout(this); 
                }
                return splitButtonButtonLayout; 
            } 
        }
 
        /// 
        /// 
        /// the width of the separator between the default and drop down button
        ///  
        [
        SRDescription(SR.ToolStripSplitButtonSplitterWidthDescr), 
        SRCategory(SR.CatLayout), 
        Browsable(false),
        EditorBrowsable(EditorBrowsableState.Advanced) 
        ]
        internal int SplitterWidth {
            get {
                return splitterWidth; 
            }
            set { 
                if (value < 0) { 
                    splitterWidth = 0;
                } 
                else {
                    splitterWidth = value;
                }
                InvalidateSplitButtonLayout(); 
            }
        } 
        ///  
        /// 
        /// the boundaries of the separator between the default and drop down button, exposed for custom 
        /// painting purposes.
        /// 
        [Browsable(false)]
        public Rectangle SplitterBounds { 
            get {
                return splitterBounds; 
            } 
        }
        ///  
        /// Summary of CalculateLayout.
        /// 	
        private void CalculateLayout() {
 
            // Figure out where the DropDown image goes.
            Rectangle dropDownButtonBounds = new Rectangle(Point.Empty, this.Size); 
            Rectangle splitButtonButtonBounds = Rectangle.Empty; 
			
 
            dropDownButtonBounds = new Rectangle(Point.Empty, new Size(Math.Min(this.Width, DropDownButtonWidth), this.Height));
   			
            // Figure out the height and width of the selected item.
            int splitButtonButtonWidth = Math.Max(0, this.Width - dropDownButtonBounds.Width); 
            int splitButtonButtonHeight = Math.Max(0, this.Height);
 
            splitButtonButtonBounds = new Rectangle(Point.Empty, new Size(splitButtonButtonWidth, splitButtonButtonHeight)); 

            // grow the selected item by one since we're overlapping the borders. 
            splitButtonButtonBounds.Width -= splitterWidth;

            if (this.RightToLeft == RightToLeft.No) {
                // the dropdown button goes on the right 
                dropDownButtonBounds.Offset(splitButtonButtonBounds.Right+splitterWidth, 0);
                splitterBounds = new Rectangle(splitButtonButtonBounds.Right, splitButtonButtonBounds.Top, splitterWidth, splitButtonButtonBounds.Height); 
            } 
            else {
                // the split button goes on the right. 
                splitButtonButtonBounds.Offset(DropDownButtonWidth+splitterWidth, 0);
                splitterBounds = new Rectangle(dropDownButtonBounds.Right, dropDownButtonBounds.Top, splitterWidth, dropDownButtonBounds.Height);

            } 

            this.SplitButtonButton.SetBounds(splitButtonButtonBounds); 
            this.SetDropDownButtonBounds(dropDownButtonBounds); 

        } 

        protected override AccessibleObject CreateAccessibilityInstance() {
           return new ToolStripSplitButtonAccessibleObject(this);
       } 

        protected override ToolStripDropDown CreateDefaultDropDown() { 
             // AutoGenerate a Winbar DropDown - set the property so we hook events 
              return new ToolStripDropDownMenu(this, /*isAutoGenerated=*/true);
         } 


        internal override ToolStripItemInternalLayout CreateInternalLayout() {
            // whenever the master layout is invalidated - invalidate the splitbuttonbutton layout. 
            this.splitButtonButtonLayout = null;
            return new ToolStripItemInternalLayout(this); 
 
        }
 
        /// 
        public override Size GetPreferredSize(Size constrainingSize) {
            Size preferredSize = SplitButtonButtonLayout.GetPreferredSize(constrainingSize);
            preferredSize.Width += DropDownButtonWidth + SplitterWidth + Padding.Horizontal; 
            return preferredSize;
        } 
 
        /// 
        /// Summary of InvalidateSplitButtonLayout. 
        /// 	
        private void InvalidateSplitButtonLayout() {
            this.splitButtonButtonLayout = null;	
            CalculateLayout(); 
        }
 
        private void Initialize() { 
            dropDownButtonWidth = DefaultDropDownButtonWidth;
            SupportsSpaceKey = true; 
        }

        [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
        protected internal override bool ProcessDialogKey(Keys keyData) { 
            if (Enabled && (keyData == Keys.Enter || (SupportsSpaceKey && keyData == Keys.Space))) {
               PerformButtonClick(); 
               return true; 
            }
 
            return base.ProcessDialogKey(keyData);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'charCode' matches control.cs
        [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] 
        protected internal override bool ProcessMnemonic(char charCode) {
           // checking IsMnemonic is not necessary - toolstrip does this for us 
           PerformButtonClick(); 
           return true;
        } 

        /// 
        /// 
        /// called when the button portion of a split button is clicked 
        /// if there is a default item, this will route the click to the default item
        ///  
        protected virtual void OnButtonClick(System.EventArgs e) { 

            if (DefaultItem != null) { 
                DefaultItem.FireEvent(ToolStripItemEventType.Click);
            }

            EventHandler handler = (EventHandler)Events[EventButtonClick]; 
            if (handler != null) handler(this, e);
 
        } 

        ///  
        /// 
        /// called when the button portion of a split button is double clicked
        /// if there is a default item, this will route the double click to the default item
        ///  
        public virtual void OnButtonDoubleClick(System.EventArgs e) {
            if (DefaultItem != null) { 
                DefaultItem.FireEvent(ToolStripItemEventType.DoubleClick); 
            }
 
            EventHandler handler = (EventHandler)Events[EventButtonDoubleClick];
            if (handler != null) handler(this,e);
        }
 

        ///  
        ///  
        /// Inheriting classes should override this method to handle this event.
        ///  
        protected virtual void OnDefaultItemChanged(EventArgs e) {
            InvalidateSplitButtonLayout();
            if (CanRaiseEvents) {
                EventHandler eh = Events[EventDefaultItemChanged] as EventHandler; 
                if (eh != null) {
                    eh(this, e); 
                } 
            }
 
        }

        /// 
        ///  
        /// Summary of OnMouseDown.
        ///  
        protected override void OnMouseDown(MouseEventArgs e) { 
		
            if (DropDownButtonBounds.Contains(e.Location)) { 
                if (e.Button == MouseButtons.Left) {

                    if (!DropDown.Visible) {
                        Debug.Assert(ParentInternal != null, "Parent is null here, not going to get accurate ID"); 
                        openMouseId = (ParentInternal == null) ? (byte)0: ParentInternal.GetMouseId();
                        this.ShowDropDown(/*mousePress = */true); 
                    } 
                }
            } 
            else {
                SplitButtonButton.Push(true);
            }
 
        }
 
 
        /// 
        ///  
        /// Summary of OnMouseUp.
        /// 
        protected override void OnMouseUp(MouseEventArgs e) {
            if (!Enabled) { 
                return;
            } 
 

            SplitButtonButton.Push(false); 

            if (DropDownButtonBounds.Contains(e.Location)) {
                if (e.Button == MouseButtons.Left) {
                    if (DropDown.Visible) { 
                        Debug.Assert(ParentInternal != null, "Parent is null here, not going to get accurate ID");
                        byte closeMouseId = (ParentInternal == null) ? (byte)0: ParentInternal.GetMouseId(); 
                        if (closeMouseId != openMouseId) { 
                            openMouseId = 0;  // reset the mouse id, we should never get this value from toolstrip.
                            ToolStripManager.ModalMenuFilter.CloseActiveDropDown(DropDown, ToolStripDropDownCloseReason.AppClicked); 
                            Select();
                       }
                    }
                } 
            }
            Point clickPoint = new Point(e.X, e.Y); 
            if ((e.Button == MouseButtons.Left) && this.SplitButtonButton.Bounds.Contains(clickPoint)) { 
                bool shouldFireDoubleClick = false;
                if (DoubleClickEnabled) { 
                    long newTime = DateTime.Now.Ticks;
                    long deltaTicks = newTime - lastClickTime;
                    lastClickTime = newTime;
                    // use >= for cases where the succession of click events is so fast it's not picked up by 
                    // DateTime resolution.
                    Debug.Assert(deltaTicks >= 0, "why are deltaticks less than zero? thats some mighty fast clicking"); 
                    // if we've seen a mouse up less than the double click time ago, we should fire. 
                    if (deltaTicks >= 0 && deltaTicks < DoubleClickTicks) {
                        shouldFireDoubleClick = true; 
                    }
                }
                if (shouldFireDoubleClick) {
                    OnButtonDoubleClick(new System.EventArgs()); 
                    // VSWhidbey 486983: if we actually fired DoubleClick - reset the lastClickTime.
                    lastClickTime = 0; 
                } 
                else {
                    OnButtonClick(new System.EventArgs()); 
                }
            }

        } 
        /// 
        protected override void OnMouseLeave(EventArgs e) { 
            openMouseId = 0;  // reset the mouse id, we should never get this value from toolstrip. 
            SplitButtonButton.Push(false);
            base.OnMouseLeave(e); 
        }
      	
        /// 
        ///  
        /// Summary of OnRightToLeftChanged.
        ///  
        protected override void OnRightToLeftChanged(EventArgs e) { 
            base.OnRightToLeftChanged(e);
            InvalidateSplitButtonLayout();			 
        }
        /// 
        /// 
        /// Summary of OnPaint. 
        /// 
        ///  
        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { 

             ToolStripRenderer renderer = this.Renderer; 
             if (renderer != null) {
                 InvalidateSplitButtonLayout();
                 Graphics g = e.Graphics;
 
                 renderer.DrawSplitButton(new ToolStripItemRenderEventArgs(g, this));
 
                 if ((DisplayStyle & ToolStripItemDisplayStyle.Image) != ToolStripItemDisplayStyle.None)  { 
                     renderer.DrawItemImage(new ToolStripItemImageRenderEventArgs(g, this, SplitButtonButtonLayout.ImageRectangle));
                 } 

                 if ((DisplayStyle & ToolStripItemDisplayStyle.Text) != ToolStripItemDisplayStyle.None) {
                      renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(g, this, SplitButtonButton.Text, SplitButtonButtonLayout.TextRectangle, this.ForeColor, this.Font, SplitButtonButtonLayout.TextFormat));
                 } 
             }
        } 
 
        public void PerformButtonClick() {
            if (Enabled && Available) { 
                PerformClick();
                OnButtonClick(EventArgs.Empty);
            }
        } 

        ///  
        ///  
        /// Resets the RightToLeft to be the default.
        ///  
        [EditorBrowsable(EditorBrowsableState.Never)]
        public virtual void ResetDropDownButtonWidth() {
            DropDownButtonWidth = DefaultDropDownButtonWidth;
        } 

        ///  
        /// Summary of SetDropDownBounds. 
        /// 
        private void SetDropDownButtonBounds(Rectangle rect) { 
            dropDownButtonBounds = rect;
        }
        /// 
        /// Determines if the  property needs to be persisted. 
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)] 
        internal virtual bool ShouldSerializeDropDownButtonWidth() { 
            return  (DropDownButtonWidth != DefaultDropDownButtonWidth);
        } 

        /// 
        ///  This class represents the item to the left of the dropdown [ A |v]  (e.g the "A")
        ///  It exists so that we can use our existing methods for text and image layout 
        ///  and have a place to stick certain state information like pushed and selected
        ///  Note since this is NOT an actual item hosted on the Winbar - it wont get things 
        ///  like MouseOver, wont be laid out by the ToolStrip, etc etc.  This is purely internal 
        ///  convenience.
        ///  
        private class ToolStripSplitButtonButton : ToolStripButton {

            private ToolStripSplitButton owner = null;
 
            public ToolStripSplitButtonButton(ToolStripSplitButton owner) {
                   this.owner = owner; 
            } 

            public override bool Enabled { 
                get {
                    return owner.Enabled;
                }
                set { 
                    // do nothing
                } 
            } 

 
            public override ToolStripItemDisplayStyle DisplayStyle {
                get {
                    return owner.DisplayStyle;
                } 
                set {
                    // do nothing 
                } 
            }
 
            public override Padding Padding {
                get {
                    return this.owner.Padding;
                } 
                set {
                    // do nothing 
                } 
            }
 

            public override ToolStripTextDirection TextDirection {
                get {
                    return owner.TextDirection; 
                }
            } 
 

            public override Image Image { 
                [ResourceExposure(ResourceScope.Machine)]
                [ResourceConsumption(ResourceScope.Machine)]
                get {
                    if ((owner.DisplayStyle & ToolStripItemDisplayStyle.Image) == ToolStripItemDisplayStyle.Image) { 
                        return owner.Image;
                    } 
                    else { 
                        return null;
                    } 
                }
                set {
                    // do nothing
                } 
            }
 
            public override bool Selected { 
                get {
 
                    if (owner != null) {
                        return owner.Selected;
                    }
                    return base.Selected; 
                }
            } 
 
            public override string Text {
                get { 
                    if ((owner.DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) {
                        return owner.Text;
                    }
                    else { 
                        return null;
                    } 
                } 
                set {
                    // do nothing 
                }
            }

        } 

        ///  
        ///  This class performs internal layout for the "split button button" portion of a split button. 
        ///  Its main job is to make sure the inner button has the same parent as the split button, so
        ///  that layout can be performed using the correct graphics context. 
        /// 
        private class ToolStripSplitButtonButtonLayout : ToolStripItemInternalLayout {

            ToolStripSplitButton owner; 

            public ToolStripSplitButtonButtonLayout(ToolStripSplitButton owner) : base(owner.SplitButtonButton) { 
                this.owner = owner; 
            }
 
            protected override ToolStripItem Owner {
                get { return owner; }
            }
 
            protected override ToolStrip ParentInternal {
                get { 
                    return owner.ParentInternal; 
                }
            } 
            public override Rectangle ImageRectangle {
                get {
                    Rectangle imageRect = base.ImageRectangle;
                    // translate to ToolStripItem coordinates 
                    imageRect.Offset(owner.SplitButtonButton.Bounds.Location);
                    return imageRect; 
                } 
            }
 
            public override Rectangle TextRectangle {
                get {
                    Rectangle textRect = base.TextRectangle;
                    // translate to ToolStripItem coordinates 
                    textRect.Offset(owner.SplitButtonButton.Bounds.Location);
                    return textRect; 
                } 
            }
        } 

        /// 
        [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
        public class ToolStripSplitButtonAccessibleObject : ToolStripItem.ToolStripItemAccessibleObject { 
            private ToolStripSplitButton owner;
 
            public ToolStripSplitButtonAccessibleObject(ToolStripSplitButton item) : base(item) { 
                owner = item;
            } 

            /// 
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override void DoDefaultAction() { 
                owner.PerformButtonClick();
            } 
 
        }
 

    }

} 

 
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK