SplitButton control, written in C#

A week ago I posted a short article describing the process of fixing a SplitButton control. The control was based on this control by a former MS employee.

My SplitButton fixes many of the bugs in her implementation, but owes to her stable base.

Comments, suggestions, and bug-fixes are welcome.

Update July 21, 2007: v1.4 is out, which fixes a null exception if the SplitMenu isn't set. In this version, if you don't set the SplitMenu property, the SplitButton will act like a regular button.

Update July 28, 2007: v1.5 is out. This fixes the small bug of the focus rectangle (i.e. the dotted line) being shown in all cases of focus, when it's only supposed to be shown when the button receives focus from the keyboard.

Update February 8, 2008: v1.6 is out. I've added support for image display and both image and text alignment. There are also many small fixes.

Update May 16, 2008: v2.0 is out. The SplitButton now works with 'ContextMenu' controls in addition to the existing support for 'ContextMenuStrip' controls. This was added so you can use the newly released VistaMenu with the Split Button.

More information, the latest changes, and the 'How-to-use' info.

down_16.pngDownload the SplitButton now. It works with all .NET 2.0 and above languages (C#, VB.NET, etc.)

This doesn't exactly fall into the category of "Comments, suggestions, and bug-fixes", but more or less a question. I like the feature of a splitbutton, but I am unable to get it to do anything besides throw down a menu and make as selection (and click it). How do you set up the event handler so that when you click the button (after the selection is made) to read the selection (from the context menu). I've been really frustrated trying to figure this out and I figure your a good enough person to ask, since you've implemented it and everything.

Thank you,

Fare

This doesn't exactly fall into the category of "Comments, suggestions, and bug-fixes", but more or less a question. I like the feature of a splitbutton, but I am unable to get it to do anything besides throw down a menu and make as selection (and click it). How do you set up the event handler so that when you click the button (after the selection is made) to read the selection (from the context menu). I've been really frustrated trying to figure this out and I figure your a good enough person to ask, since you've implemented it and everything.

Thank you,

Fare

Your question, if I understand it correctly, is how to get event handlers for the menu items. If this is your question, you can do it two ways.

1. The easy way

In Visual Studio designer, click the menu you want to add the even to, then double click one of the sub-items. Visual Studio will automatically generate the click method for that sub-item.

2. The more flexible way

You can also handle more than one of the sub-items in the same method:[attachment=0]multimnuclick.png[/attachment]

After you double click that event handler from the properties window, you can use this code to figure out which item was selected (where menuUpload is the name of my sub-menu):

        private void menuUpload_ItemClicked(object sender, ToolStripItemClickedEventArgs e)        {            int clickedIndex = menuUpload.Items.IndexOf(e.ClickedItem);


            //clickedIndex is the index of the click menu Item            //now use clickedIndex to determine you action        }

I hope I've answered your question.

yes and no...

Yes,in a sense because you showed me that I am setting it up wrong.

Your menuupload sub-menu is what your designing, but it appears that I was designing the actual split button class.

I have the designer set up like...

            //             // splitButton            //             this.splitButton.ContextMenuStrip = new System.Windows.Forms.ContextMenuStrip();            this.splitButton.ContextMenuStrip.Items.Add("Item1");            this.splitButton.ContextMenuStrip.Items.Add("Item2");            this.splitButton.ContextMenuStrip.Items.Add("Item3");            this.splitButton.AutoSize = true;            yadda yadda yadda

This is the way that it was set up on the jfo's site...I think

Is there something else I need to declare? I noticed the path for the menuUpload was System.Windows.etc.etc.etc

Thank you again,

Fare

It almost looks like you're directly editing the "FormName.Designer.cs" file. Normally that's not recommended.

What you want to do is double click the the FormName:

[attachment=1]solexplorer.png[/attachment]

Then, in the ToolBox, click the ContextMenuStrip and drag it to the form.

[attachment=0]dragtoform.png[/attachment]

Visual Studio will automatically take care of all the namespaces (like using System.Windows.Forms; //etc.)

lol, wow...

As you can probably tell, I'm new to the IDE scene...This is a long way from c++ in VIM... It almost seems like cheating...

I got it all to work, and I thank you for that. I was forced to go into the designer class source code to manually add the new contextmenu to the splitbutton, is there a way you can do it from the designer window?

Thank you again, and sorry for the inconvenience (I'm sure this wasn't the intent of your original post),

Fare

... is there a way you can do it from the designer window?

Sure, just click the contextMenuStrip you created, and click the "..." button in the Items property:

[attachment=0]edititems.png[/attachment]

From there you can Add, edit, and remove the menu items.

Thank you again, and sorry for the inconvenience (I'm sure this wasn't the intent of your original post)

It's no problem at all. I was glad to help.

Sure, just click the contextMenuStrip you created, and click the "..." button in the Items property:

I was talking more along the lines of adding the context menu to the splitbutton

this.splitButton1.ContextMenuStrip = this.contextMenuStrip1;

I couldn't find that option in the Items field...

Oh, ok. Well, with my fixed version of the SplitButton it's the SplitMenu property you'll need to set:

[attachment=0]sbmenu.png[/attachment]

I hid the ContextMenuStrip property in my SplitButton because it has quirky behavior when you right click the SplitButton in runtime.

ah, the splitmenu field ( I should have known that after going through that code a hundred times...)

thank you again,

Fare

I'm glad I found your post. I was looking for some cool new (free, of course) components to kind of spice up a new internal application. I could be using this to replace quite a few comboboxes.

Personally, I'm a VBer, but I can read much of C#. I know it isn't of any particular consequence, but I have converted the latest version to VB.NET 2.0 for anyone interested.

Thanks,

I'm glad I found your post. I was looking for some cool new (free, of course) components to kind of spice up a new internal application. I could be using this to replace quite a few comboboxes.

Personally, I'm a VBer, but I can read much of C#. I know it isn't of any particular consequence, but I have converted the latest version to VB.NET 2.0 for anyone interested.

Thanks,

Sure, e-mail it to me and I'll add it to the first post.

-Wyatt

Hi,

The FlatStyle "Flat" does not work for this SplitButton. Do you have an idea how i can accomplish that?

Thanks,Aaron

Hi,

The FlatStyle "Flat" does not work for this SplitButton. Do you have an idea how i can accomplish that?

Thanks,Aaron

The flat style is not a theme-inherited style - that is, it doesn't look like the themed buttons in Vista & XP. This means it will not change from one version of Windows to another. So, to accomplish the 'Flat' look is actually a rather simple task.

Just open up the project in Visual Studio (or whichever development environment you use) and edit the 'OnPaint(PaintEventArgs pevent)' method in the SplitButton.cs file. Then it's just a matter of replacing the existing code with a switch statement for each of the button's states:

switch(State){   case PushButtonState.Default:      //TODO: insert painting code   break;   case PushButtonState.Disabled:      //TODO: insert painting code   break;   case PushButtonState.Hot:      //TODO: insert painting code   break;   case PushButtonState.Normal:      //TODO: insert painting code   break;   case PushButtonState.Pressed:      //TODO: insert painting code   break;}

Then where each comment is, replace it with drawing code that draws the rectangle background and the 1px/2px black frame for that particular button state.

The reason I didn't (and won't) implement the alternate FlatStyles is because they look amateur.

However, if you're developing for a client with those particular stylistic needs, then who am I to impose my own standards?

What I'd like is a simple dropdown that allows me to have a different width for the control than for the dropdown list. Since I can't find a forms-friendly control that does that (ie, not a toolstrip bound control), I use your split button, even though I don't need the split itself.

So, given this - is there any way I can get the drop down list to drop if the user clicks anywhere in the button, as opposed to just the right side? I want it to function exactly as if they had right clicked, essentially.

What I'd like is a simple dropdown that allows me to have a different width for the control than for the dropdown list. Since I can't find a forms-friendly control that does that (ie, not a toolstrip bound control), I use your split button, even though I don't need the split itself.

So, given this - is there any way I can get the drop down list to drop if the user clicks anywhere in the button, as opposed to just the right side? I want it to function exactly as if they had right clicked, essentially.

Well, you'll have to change a few methods. For example, the OnKeyDown method will have to be changed to

        protected override void OnKeyDown(KeyEventArgs kevent)        {            if (showSplit)            {                if (kevent.KeyCode.Equals(Keys.Down) || kevent.KeyCode.Equals(Keys.Space))                {                    ShowContextMenuStrip();                }            }


            base.OnKeyDown(kevent);        }

and make similar changes to OnKeyUp. Though, the main thing you'll need to change is to OnMouseDown:

        protected override void OnMouseDown(MouseEventArgs e)        {            if (!showSplit)            {                base.OnMouseDown(e);                return;            }


            //always show the context menu, no matter where clicked            ShowContextMenuStrip();        }

And obviously you'll want to change the drawing code to never draw the "split" line. There will be other subtleties that you'll need to work out to get it working perfectly, but that should get you started.

If you need more help, feel free to ask.

Hey you, just wanted to say a big thank you. Your control is EXACTLY what i was looking for!!! Really good work. Thanks a lot.regardsjOe

First thing's first: this is so far the best SplitButton implementation I've come across. Thanks!

Now, on to the whining! 🙂

If I set an image on the SplitButton, and have nothing set for the "SplitMenu" property, then the image is drawn on the button and everything is right in the world. But as soon as I set the SplitMenu property, the button is redrawn without the image. This happens on all the "TextImageRelation" enums I've tried. I assume it's gotta be happening in the OnPaint override, but I'm having trouble tracking it down. Any ideas good sir?

Thanks!

First thing's first: this is so far the best SplitButton implementation I've come across. Thanks!

I'm glad this control is so popular.

Now, on to the whining! 🙂

If I set an image on the SplitButton, and have nothing set for the "SplitMenu" property, then the image is drawn on the button and everything is right in the world. But as soon as I set the SplitMenu property, the button is redrawn without the image.

That's because I didn't implement the image painting in the code. I'll have a fixed version listed on the first post (and a blog post) tomorrow night.

Edit, Feb. 8, 2008: The new version (1.6) has image support. Tell me if you find any bugs.

Thanks Wyatt! 🙂

Hi,

I want the SplitButton to have a blue background so I set splitButton1.BackColor = Color.BlueThis has no effect that I can see, the button still has its default color.Is there something else that I need to do to be able to change the backcolor?

Thanks,

dlarkin77

I want the SplitButton to have a blue background so I set splitButton1.BackColor = Color.BlueThis has no effect that I can see, the button still has its default color.Is there something else that I need to do to be able to change the backcolor?

I didn't implement BackColor, so setting the property has no effect. This is similar to another question. All you need to do is open up the SplitButton project and edit the OnPaint method.

It might take a couple of hours to get it the way you like it, But you can ask for help if you have any trouble understanding the code.

I have a few parting questions: Why do you want to override the default theme for XP / Vista? Do your users really want that? Why not just disable the theme on your computer, and set the control color to blue? That way you'll have uniformity across all your apps.

I have been using your button, and I would like to hide the "button" part of the splitbutton so when it loses focus, only shows the dropdown arrow, and If the user goes and click the arrow, the button shows again. Any idea?Thanks.

m0by, I'm not quite sure what you mean. Are you asking for a combo-box? If not, what you're asking for sounds confusing and non-intuitive.

Maybe I'm misunderstanding you. Perhaps you could post a picture of what you're looking for.

How do i use this with Visual Studio 2005.I added referance from bin folder & then choose item in toolbox & check the split button checkbox then nothing happens.Am i doing something wrong 😳

You need to add it to your form. Click the Split Button in the toolbox, and drag it to your form.

Has anyone else experienced SplitButton appearance changes between XP and Windows 7?

I had a form with a few SplitButtons that I created in WinXP, but when I got a new computer with Windows 7, the SplitButtons appeared to be a slightly different size and a slightly different location. I assumed that the form had gotten mangled somehow and fixed they layout. However, when I load the form on a WinXP machine, the SplitButtons are a slightly different size again. All of the standard .NET Buttons are the correct size, though.

Has anyone else seen this? Is there a fix for it that I missed?

Thank you for help,

Andy

Hey Andy,

Font sizes on Windows XP vs. Windows 7 are different (see: Windows Vista & 7 Font, Segoe UI, in C# and VB.NET).

Do you have the AutoScaleMode property of your form set? If so, this will move & resize elements on your form.

What should AutoScaleMode be set to? I haven't modified it, and it is set to Font.

I don't know if this is really the issue though. You see, all of the split buttons are a different size than the non split buttons. If my entire GUI was mangled, I could understand it being a property like this, but only the split buttons appear to be affected.

However, if I am the only one seeing this, then it must be some property or setting that is causing the problem. If anyone knows what settings might be the problem, I would be much obliged.

Thanks,

Andy

My mistake. As it turns out, it isn't a Windows XP / Windows 7 issue, but a large font, small font issue. I designed my form with large fonts, and when I compiled it and move it to a computer that didn't have large fonts turned on, the button was too big.

I think that the problem is in CalculateButtonAutoSize(); there are a couple of places where constants are added to the height and width, and I think those are causing issue when switching between regular fonts and large fonts. There aren't any comments beyond "Pad the text size" and "Pad the result", so I'm not sure where those numbers are coming from. I think that those numbers need to be replaced by values from the operating system, but I don't know which values to get. Does anyone have any suggestions?

Thanks,

Andy

They're arbitrary values. They only add a few pixels to the height and width. I can't reproduce the issues you're having with font sizes.

Hello

Thanks for your nice controlbut there is a color issue.

the ForeColor property is not working.

cheers

fred

Ok, I've fixed it. Redownload the SplitButton.

Thanks for spotting this.

Pretty nice control but there is a problem with the backcolor property: if you give the control a custom background color and use a Classic Windows desktop background it works perfectly but if you change the desktop to Aero the custom background color change to some standard color.

When we wrote the SplitButton the custom background color property was a second class citizen. By default the splitbutton uses Windows default styling (i.e. the correct way). If you want to use custom colors you can edit the source code. Specifically look at the drawing code.

I can't find anyway the tutorial how to use your SplitButton component. Please help.

  1. Add the button to your form.
  2. Use the SplitMenu or SplitMenuStrip properties.

Hi,

Download the splitbutton control. One problem with it is that the control doesn't handle flat style, flat style appearance and background color at all.

Is there a way to get it to work?

The SplitButton doesn't support the flat style (as per design). You can always add it. It's failry simple to do.

Hi,thanks for the SplitButton - it works great and looks good. Unfortunately with one little exception 🙂The text of the button is placed exactly one pixel lower than on normal WinForm buttons.Wouldn't notice this if there wasn't a normal button directly besides my splitbutton.Do you have an idea how to fix this?

I'm developing with .NET 3.5 and experience this problem with Win XP. Haven't testedit with Win 7 yet.

Thanks & cheers

Just edit the paint method.