Click here to Skip to main content
15,037,160 members
Please Sign up or sign in to vote.
3.00/5 (2 votes)
See more:
i made custom combobox that have an integrated button to add new item and it works good in case when DropDownStyle = ComboBoxStyle.DropDownList but there is a problem when the combobox DropDownStyle = ComboBoxStyle.DropDown the text of the combobox is cover the button taht i made. is there away to make space before text in case whene the DropDownStyle for the combobox is set to DropDown ? you can see the problem in image

http://i.stack.imgur.com/g5VZB.png[^]

C#
public class ComboBoxButton3 : ComboBox
{
    public ComboBoxButton3()
    {
        myButton = new Button();

        this.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
        this.DropDownStyle = ComboBoxStyle.DropDownList;
    }

    protected override void OnCreateControl()
    {
        this.myButton.Size = new Size(23, this.ClientSize.Height);
        this.myButton.Location = new Point(0, 0);
        this.myButton.Cursor = Cursors.Default;
        this.Button.BackgroundImage = global::app.Properties.Resources.add1;
        this.Button.BackgroundImageLayout = ImageLayout.Stretch;
        this.Button.FlatStyle = FlatStyle.Flat;
        this.Controls.Add(this.myButton);

        base.OnCreateControl();
    }


    protected override void OnDrawItem(DrawItemEventArgs e)
    {
        if (this != null)
        {
            e.DrawBackground();
            if (e.Index >= 0)
            {
                StringFormat sf = new StringFormat();
                sf.LineAlignment = StringAlignment.Center;
                sf.Alignment = StringAlignment.Center;

                Brush brush = new SolidBrush(this.ForeColor);

                if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
                    brush = SystemBrushes.HighlightText;

                e.Graphics.DrawString(this.Items[e.Index].ToString(), this.Font, brush, e.Bounds, sf);
            }
        }

        base.OnDrawItem(e);
    }

    public Button myButton;
    public Button Button
    {
        get
        {
            return myButton;
        }
        set
        {
            myButton = value;
        }
    }
}


What I have tried:

i found an answer for same question but to add button to textBox and the answer was by adding this code when create textbox but it doesn't work for the combobox

C#
protected override void OnCreateControl()
{
// Send EM_SETMARGINS to prevent text from disappearing underneath the button
SendMessage(this.Handle, 0xd3, (IntPtr)2, (IntPtr)(this.mButton.Width << 16));
base.OnCreateControl();
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
Posted
Updated 11-Feb-16 13:36pm
v2
Comments
Sergey Alexandrovich Kryukov 11-Feb-16 21:07pm
   
To start with, what is "ComboBox"? Which one? Full type name, please.
Let me tell you: you are doing a dirty thing, SendMessage. And by "adding space" you are looking for another dirty thing. UI and code should be designed in a regular reliable matter. We can discuss it when you confirm the type; is it System.Windows.Forms.ComboBox (I'm pretty much sure it is)? Or something else...

What is the purpose of OnDrawItem? You are drawing a string... but where do you draw a cross?

—SA
BillWoodruff 12-Feb-16 0:49am
   
"you are doing a dirty thing, SendMessage. And by "adding space" you are looking for another dirty thing." out of soap, Sergey ? :)
masalahi 12-Feb-16 10:45am
   
Why this is dirty???
Sergey Alexandrovich Kryukov 12-Feb-16 10:49am
   
Because it breaks platform compatibility. All work should be done using pure .NET FCL. Also, the design should be tolerant to all the system settings. If you have a custom rendering of some element, you need to do whole rendering owner-drown or render the whole thing graphically...
—SA

1 solution

Note: an example of how to add a space character (or any other strings) to the front (prefix) or end (suffix) of the ComboBox Items appears at the end, here.

Please understand that these basic Controls provided by WinForms are essentially "little-black-boxes," an "opaque wrapper" of .NET around old COM based Controls. As a group, they are inconsistent, and I would argue that, with rare exceptions, any time a developer spends "hacking" these Controls using the Win API, is wasting time.

There is already a useful, proven, method ("Composition") to create custom Controls: make a UserControl that contains the "atomic" Controls you want to use as an "entity."

So, add a UserControl to your Project, put a ComboBox, and a Button, into the UserControl. Spend some time tweaking the visual appearance of the UserControl.

The code-behind can look this simple:
C#
using System;
using System.Windows.Forms;

namespace YourNameSpace
{
    public partial class UCComboBox : UserControl
    {
        public UCComboBox()
        {
            InitializeComponent();
        }

        // event handler for new item Button click to be "injected" here
        public Action<ComboBox> GetNewItemAction { set; get; }

        // event handler for ComboBox selection to be "injected" here
        public Action<int, object> ComboSelectionAction { set; get; }

        // one possible way an external "client" can set the content (Items)
        // of the internal ComboBox, with an option to either clear
        // the existing Items, or append the new Items to the existing Items
        public void SetComboItems(bool doClearItemsFirst, params string[] items)
        {
            if(doClearItemsFirst) TheCombo.Items.Clear();

            TheCombo.Items.AddRange(items);
        }

        // the Button is clicked
        private void btnAddNewItem_Click(object sender, EventArgs e)
        {
            if (GetNewItemAction != null) GetNewItemAction(TheCombo);
        }

        // selection in the ComboBox
        private void TheCombo_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (ComboSelectionAction != null) ComboSelectionAction(TheCombo.SelectedIndex, TheCombo.SelectedItem);
        }
    }
}
An example of the use of this UserControl:

a. assuming you've either drag-dropped an instance of the UserControl from the ToolBox, or created one in code: it's named 'UCComboBox1:
C#
// in Form Load EventHandler:
private void Form1_Load(object sender, EventArgs e)
{
    UCComboBox1.SetComboItems(
        true,
        "one", "two", "three", "four",
        "five", "six", "seven", "eight");
    
    UCComboBox1.ComboSelectionAction = ComboSelectionAction;
    
    UCComboBox1.GetNewItemAction = GetNewItemAction;
}

// event that receives notification from UserControl ComboBox
// when a selection is made
private void ComboSelectionAction(int ndx, object item)
{
    Console.WriteLine("selected index: {0} selected item: {1}", ndx, item);
}

// event that receives notification from UserControl Button
// when Button is clicked
private void GetNewItemAction(ComboBox comboBox)
{
    comboBox.Items.Add("nine");
}
But, how do you do add spaces (or anything else) to the Items in the ComboBox ?

Well, you could "automate" that pretty easily:
C#
// requires Linq
// in the UserControl
public void SetComboItems(bool doClearItemsFirst, string prefix, string suffix, params string[] items)
{
    if(doClearItemsFirst) TheCombo.Items.Clear();

    TheCombo.Items.AddRange(AdjustItems(prefix, suffix, items));
}

public string[] AdjustItems(string prefix, string suffix, string[] items)
{
   return items.Select(itm => prefix + itm + suffix).ToArray();
}
To use this:
C#
// in the client
UCComboBox1.SetComboItems(
    true,  // clear items
    "{",   // prefix
    "}",   // suffix
    "one", "two", "three", "four",   // items
    "five", "six", "seven", "eight");
Notes:

0. in case it's not clear to you: 'Action and 'Func are just a convenient way to use the Delegate Type. These facilities were added in .NET 3.5.

1. the choice to pass a reference to the ComboBox itself to the external client/user when the Button is clicked is based on the idea that the client/user may wish to do some editing/adjustment to the ComboBox Items beyond just adding a new Item. if adding a new Item is all that's required, you could use a 'Func that returned some 'object or 'string to add to the ComboBox, rather than an 'Action, and avoid "exposing" the ComboBox outside its host Class.
   
v2
Comments
aarif moh shaikh 12-Feb-16 2:05am
   
+5
BillWoodruff 12-Feb-16 2:35am
   
Glad you found this answer useful. Once you "get" how you can use 'UserControls to "compose" complex, custom, Controls ... and, optionally, creating UserControls that have other UserControls inside them ... I think you will find this a valuable resource.

The "price" of doing complex UserControls is, perhaps, you have to spend some time tweaking the visual appearance, perhaps adjusting 'Dock, or 'Anchor properties, or 'Padding, 'Margin, etc.

And, of course, the approach shown here ... for the ComboBox ... is not compatible with binding: using DataSource/DisplayMember/ValueMember ... although you could re-use the code shown here that adds a prefix/suffix string, and apply to the whatever you are going to bind to.

cheers, Bill

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900