|
You create 5 Bitmap objects and never dispose them. This leads to leaking GDI objects. Dispose method should be something like this:
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
if (disposing && _DefaultSplitImages != null)
{
foreach (System.Drawing.Bitmap element in _DefaultSplitImages.Images) {
element.Dispose();
}
_DefaultSplitImages.Images.Clear();
_DefaultSplitImages.Dispose();
_DefaultSplitImages = null;
}
base.Dispose(disposing);
}
|
|
|
|
|
exactly what I was looking for, and wellimplemented
|
|
|
|
|
I'm curious about that drop-down arrow glyph (the one that system uses), is it a character from some secret system font or a bitmap?
Usually I can see it in popup menus (oriented to the right), and it seems scaled properly to match menu font. I'd like to know because I'm thinking about taking a shot at home-made custom split-button control, and glyphs from a font already present in the system are much more convenient to reuse than an extra hand-made resource bitmap (which I then have to scale at high dpi to match it's size with the used font).
Any insights are appreciated.
[Edit]
Nevermind, I found it...
It's the glyph you can get with DrawFrameControl() , look up for DFCS_MENUARROW or DFCS_SCROLLDOWN.
modified 10-Oct-11 15:08pm.
|
|
|
|
|
It's a custom-made bitmap image that is drawn at run-time. You can easily check this by looking at the source code of the article. You can re-use that drawing code to have the same symbol up-scalable without loss of quality.
Synved : Logos / Designs / Websites / Development / FREE Quote / www.synved.com
|
|
|
|
|
|
Your control sometimes requires the user to click the split portion of the button twice in order to get the menu to appear. I've done some debugging and found that the MousePostion isn't giving the correct mouse coordinates every time. The MousePosition is being called in the MouseInSplit method.
Any insights would be helpful, thanks.
|
|
|
|
|
It is very good for me and my currently work. Thanks
|
|
|
|
|
Thanks for this article, it's very userful to me,but I have a question like the subject.Could you give me some advise.
|
|
|
|
|
Good job man. Easy and simple to use. I really wish Microsoft would implement this this in Visual Studio. One can only wish. Anyway, thanks and keep up the good work, just what I was looking for.
|
|
|
|
|
I think codeproject is the best web-Page for a coder or any IT-worker.Because there are many friends to help you when you need. At this place every body like a student.They study a lot of programming skill from many article.
|
|
|
|
|
Split Button was really useful. Thank you.
|
|
|
|
|
I was wondering what event I can put code in to be able to tell what subitem is being clicked?
If I'm populating the control like this:
this.splitButton1.ContextMenuStrip = new ContextMenuStrip();
this.splitButton1.ContextMenuStrip.Items.Add("one");
this.splitButton1.ContextMenuStrip.Items.Add("two");
this.splitButton1.ContextMenuStrip.Items.Add("three");
Is there a way to differentiate when someone clicks "one" or "two?" Also, can you populate the subitems on the control itself somehow (like a Collections property... but I don't see one)?
Thanks.
|
|
|
|
|
The easier (and the best in my opinion) way to populate menu items is to add a context menu (ContextMenuStrip class) to your form/UserControl through the designer and then add/remove/edit items or event handlers (ContextMenuStrip.ItemClicked is the one you need) through the designer itself. In such a way you can visually manage your context menu for the split button without the need to write any line of code (you need to do so only if menu items are dynamically determined, that is, created at run time). Then you simply have to select the ContextMenuStrip property of the SplitButton to your newly created context menu (using the drop down for the ContextMenuStrip property in the properties window). You should have a look at the demo project in the download section in this same article because in there I do exactly the things explained above.
|
|
|
|
|
|
I already knew about the first solution, the problem is: that control doesn't allow you to use custom images for the split button states, it simply draws the triangle by himself and so I wrote this control to accomplish the task.
In regard to the second solution, it has been published yesterday (June 24th, 2007) while mine has been published 10 months ago. In addition my article is also referenced in there as "# A buggy, but open source SplitButton designed by some anonymous guy. Pass.", and that is really funny because it says my control is bugged but it doesn't say what the bugs are and where. Strangely enough, that page has been published yesterday and isn't even indexed in google but you posted this comment at 21:43 which leaves you about 21 hours (probably less) to find the page, analyse the code in there and comment my article. That blog has a google pagerank of 3, which makes it just impossible to find through search engines, I wonder how did you find it so fast.
|
|
|
|
|
I guess you're implying I'm that guy. Truth is, I use that guy's Panel2006 (a corny name, with just as many bugs). He's often mean, but this time I thought that he posted something useful.
My mistake for trying to relay information.
|
|
|
|
|
No, your mistake was to relay information in the wrong way. Never mind, though.
|
|
|
|
|
I tried using the second one (that fixes Microsoft guy's errors), and received an error when I clicked on a button that had no items in it... not sure what happened there, but I'm going back to Microsoft guy's control until it works properly. Thanks for providing links to both.
|
|
|
|
|
I noticed that when you right-click on the split button you still get the default context menu. If you override the context menu property like so:
private ContextMenuStrip _ContextMenu;
public override ContextMenuStrip ContextMenuStrip
{
get
{
return _ContextMenu;
}
set
{
_ContextMenu = value;
}
}
then the context menu will not appear when you right-click the split button.
|
|
|
|
|
Yes you're right. But i don't think that this is the best solution, infact in that way works because the base control uses directly the field of the context menu, and not the property, but you can't know if in the feature it will do so (even if it would be illogical not doing that). The real solution would be renaming also the property, and not only using a different field, or another (worse) solution would be overriding the (right) click event handler.
Using the property you need also to change the OnMouseDown method override, from:
if (_AlwaysDropDown || MouseInSplit())
{
if (Enabled)
{
...
To:
if (_AlwaysDropDown || MouseInSplit())
{
if (Enabled && mevent.Button == MouseButtons.Left)
{
...
Thanks anyway for the report.
|
|
|
|
|
I find your SplitButton very usefull.
One thing though. When I disable the button, the background image stays enabled.
Could you tell me how to fix that?
Thanks,
Golan Bar-Nov.
|
|
|
|
|
You have to select the disabled image in the DisabledImage property of the SplitButton (under "Split Button Images" category), after setting the ImageList property to your ImageList, it's not automatically selected/grayed. I think I'm going to update this article/control in the near future, but I'm too busy at this time.
|
|
|
|
|
Thank you for your quick reply.
The disabled image under the "Split Button Images" refers to the arrow image on a disabled mode. There is another image in the button's base class called BackgroundImage which stays enabled when the control is disabled.
|
|
|
|
|
The control doesn't check the background image, and nor use that property, so is not a control fault if it remains enabled. So if you want to disable it you must do by yourself, and that shouldn't be difficult. Simply create a grayed-scale of your image, this routine should work:
public static Image CreateDisabledImage(Image normalImage)
{
ImageAttributes attributes1 = new ImageAttributes();
attributes1.ClearColorKey();
attributes1.SetColorMatrix(ToolStripRenderer.DisabledImageColorMatrix);
Size size1 = normalImage.Size;
Bitmap bitmap1 = new Bitmap(size1.Width, size1.Height);
Graphics graphics1 = Graphics.FromImage(bitmap1);
graphics1.DrawImage(normalImage, new Rectangle(0, 0, size1.Width, size1.Height), 0, 0, size1.Width, size1.Height, GraphicsUnit.Pixel, attributes1);
graphics1.Dispose();
return bitmap1;
}
|
|
|
|
|
Sorry, this is the complete code to disable an image (example program class):
static class Program
{
static ColorMatrix DisabledImageColorMatrix;
///
/// The main entry point for the application.
///
[STAThread]
static void Main()
{
float[][] singleArrayArray1 = new float[5][];
singleArrayArray1[0] = new float[] { 0.2125f, 0.2125f, 0.2125f, 0f, 0f };
singleArrayArray1[1] = new float[] { 0.2577f, 0.2577f, 0.2577f, 0f, 0f };
singleArrayArray1[2] = new float[] { 0.0361f, 0.0361f, 0.0361f, 0f, 0f };
float[] singleArray1 = new float[5];
singleArray1[3] = 1f;
singleArrayArray1[3] = singleArray1;
singleArrayArray1[4] = new float[] { 0.38f, 0.38f, 0.38f, 0f, 1f };
float[][] singleArrayArray2 = new float[5][];
float[] singleArray2 = new float[5];
singleArray2[0] = 1f;
singleArrayArray2[0] = singleArray2;
float[] singleArray3 = new float[5];
singleArray3[1] = 1f;
singleArrayArray2[1] = singleArray3;
float[] singleArray4 = new float[5];
singleArray4[2] = 1f;
singleArrayArray2[2] = singleArray4;
float[] singleArray5 = new float[5];
singleArray5[3] = 0.7f;
singleArrayArray2[3] = singleArray5;
singleArrayArray2[4] = new float[5];
DisabledImageColorMatrix = MultiplyColorMatrix(singleArrayArray2, singleArrayArray1);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
public static Image CreateDisabledImage(Image normalImage)
{
ImageAttributes attributes1 = new ImageAttributes();
attributes1.ClearColorKey();
attributes1.SetColorMatrix(Program.DisabledImageColorMatrix);
Size size1 = normalImage.Size;
Bitmap bitmap1 = new Bitmap(size1.Width, size1.Height);
Graphics graphics1 = Graphics.FromImage(bitmap1);
graphics1.DrawImage(normalImage, new Rectangle(0, 0, size1.Width, size1.Height), 0, 0, size1.Width, size1.Height, GraphicsUnit.Pixel, attributes1);
graphics1.Dispose();
return bitmap1;
}
static ColorMatrix MultiplyColorMatrix(float[][] matrix1, float[][] matrix2)
{
int num1 = 5;
float[][] singleArrayArray1 = new float[num1][];
for (int num2 = 0; num2 < num1; num2++)
{
singleArrayArray1[num2] = new float[num1];
}
float[] singleArray1 = new float[num1];
for (int num3 = 0; num3 < num1; num3++)
{
for (int num4 = 0; num4 < num1; num4++)
{
singleArray1[num4] = matrix1[num4][num3];
}
for (int num5 = 0; num5 < num1; num5++)
{
float[] singleArray2 = matrix2[num5];
float single1 = 0f;
for (int num6 = 0; num6 < num1; num6++)
{
single1 += singleArray2[num6] * singleArray1[num6];
}
singleArrayArray1[num5][num3] = single1;
}
}
return new ColorMatrix(singleArrayArray1);
}
}
|
|
|
|