33

C# & .Net 2.0 question (WinForms)

I have set of items in ComboBox and non of them selected. I would like to show a string on combo "Please select item" in that situation.

Current implementation is just added empty item with such text on index 0 and remove it when user select one of following items. Unfortunately empty item is shown in dropdown list as well. How to avoid this situation or in other way - is there any way to show custom text on ComboBox when no item is selected?

Answers below work when ComboBoxStyle is set to DropDown (ComboBox is editable). Is there possibility to do this when ComboBoxStyle is set to DropDownList?

johnnyRose
  • 7,310
  • 17
  • 40
  • 61
jotbek
  • 1,479
  • 3
  • 14
  • 22
  • so....when the user select the item in combobox you dont want show the custom text in the items ... – rockyashkumar Nov 09 '11 at 11:14
  • Take a look at http://stackoverflow.com/questions/1426050/how-to-display-default-text-select-team-in-combo-box-on-pageload-in-wpf – PaulB Nov 09 '11 at 11:24
  • 1
    I'm searching for solution that works with WinForms not WPF. To be strictly I edited first post and underlined this. Thanks. – jotbek Nov 09 '11 at 11:49

17 Answers17

19

Use the insert method of the combobox to insert the "Please select item" in to 0 index,

comboBox1.Items.Insert(0, "Please select any value");

and add all the items to the combobox after the first index. In the form load set

comboBox1.SelectedIndex = 0;

EDIT:

In form load write the text in to the comboBox1.Text by hardcoding

comboBox1.Text = "Please, select any value";

and in the TextChanged event of the comboBox1 write the following code

 private void comboBox1_TextChanged(object sender, EventArgs e)
        {
            if (comboBox1.SelectedIndex < 0)
            {
                comboBox1.Text = "Please, select any value";
            }
            else
            {
                comboBox1.Text = comboBox1.SelectedText;
            }
        }
  • 8
    But that would mean the user can select this. I guess the user wants that this is shown as an hint and not a selectable answer. – RvdK Nov 09 '11 at 11:18
  • 1
    Yes, it is my current implementation, but this item is shown in dropdown list and user can still choose it. I would like to avoid such situation and show text only on combobox but not in dropdown list. – jotbek Nov 09 '11 at 11:19
  • @skk: For some reason it does not work. Items are already loaded maybe *ToString()* method has priority not *Text* property. – jotbek Nov 09 '11 at 12:02
  • @jotbek, no if you write the code in textchanged event definitely it will work for you. Can you post the code which is modified now? – Sai Kalyan Kumar Akshinthala Nov 09 '11 at 12:08
  • 1
    @skk: Correct me if i'm wrong but this code will cause infinite loop because every change of text will fire event that text change and this code will run again? – jotbek Nov 09 '11 at 12:17
  • Ya, but any how we are setting the Selectedtext also in the else case right. Then where's your problem? – Sai Kalyan Kumar Akshinthala Nov 09 '11 at 12:26
  • @skk: I don't know why but it is impossible to change value of Text property. When set property when SelectedIndex == -1 it just comes back to empty value. It works that during program execution as well as when pause program and manually try to set value in debug. Same thing happens when set property in Properties explorer in VS2010 in design mode - value just vanishes. Maybe I need to set some other property in order to allow me set value of Text? (I'm using .Net 2.0) – jotbek Nov 09 '11 at 12:43
  • @skk: so i checked it and it appears that it works only when combobox is editable, that means that *ComboBoxStyle* is set to *DropDown* not *DropDownList*. – jotbek Nov 09 '11 at 13:18
  • @jotbek, actually it will work in DropDown style also but we can change only the selected index, but in the case of DropDownList we can manually change the text at runtime. – Sai Kalyan Kumar Akshinthala Nov 09 '11 at 13:27
  • @skk: as in my previous comment, changing Text property works only when DropDown of ComboBoxStyle is set. I could not achieve changing Text property during runtime when DropDownList was set. Unfortunately I use .Net 2.0 and ComboBox does not have IsEditable property that would make TextBox of ComboBox read-only. This property exists only in .Net > 2.0 – jotbek Nov 09 '11 at 13:37
  • Here you can find solution: If you have .Net > 2.0 linkg [1] and If you have .Net == 2.0 (search for pavlo_ua answer) link [2]. [1]: http://kentb.blogspot.com/2008/11/using-comboboxtext-as-hint.html [2]: http://social.msdn.microsoft.com/Forums/en/winformsdesigner/thread/0004035e-4f1d-4b16-bd97-6123c209fe2f – jotbek Nov 09 '11 at 14:19
9

Credit must be given to IronRazerz in a response to TextBox watermark (CueBanner) which goes away when user types in single line TextBox (not for RichTextBox).

You will need to declare the following in your class:

private const int CB_SETCUEBANNER = 0x1703;

[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]string lParam);

Then you can use it with something like:

SendMessage(this.comboBox1.Handle, CB_SETCUEBANNER, 0, "Please select an item...");

This is assuming the Combo Box's DropDownStyle is set to DropDownList, as is the original poster's question.

This should result in something like the following:

Placeholder text for combo box drop down list

Kevin
  • 172
  • 1
  • 6
5

I can't see any native .NET way to do it but if you want to get your hands dirty with the underlying Win32 controls...

You should be able to send it the CB_GETCOMBOBOXINFO message with a COMBOBOXINFO structure which will contain the internal edit control's handle. You can then send the edit control the EM_SETCUEBANNER message with a pointer to the string. (Note that this requires at least XP and visual styles to be enabled.

Deanna
  • 23,876
  • 7
  • 71
  • 156
  • Yes I agree, here you have solution for .Net 2.0: [link]http://social.msdn.microsoft.com/Forums/en/winformsdesigner/thread/0004035e-4f1d-4b16-bd97-6123c209fe2f search for pavlo_ua solution – jotbek Nov 09 '11 at 15:27
4

Here's how I do it. It might not be the best method, and offers least control; however, it's simple and quick and I thought it might be a good idea to share it so that more options are available for others.

<ComboBox SelectedIndex="0">
    <ComboBoxItem Visibility="Collapsed">Please select one...</ComboBoxItem>
    <ComboBoxItem>1</ComboBoxItem>
    <ComboBoxItem>2</ComboBoxItem>
    <ComboBoxItem>3</ComboBoxItem>
    <ComboBoxItem>4</ComboBoxItem>
</ComboBox>

The idea behind this is that the initial selection is index 0, which is collapsed, so it's not available under selection for the user once they select something else. The downside is that you have to remember that if you're checking for a selected index, remember that index 0 means there was no selection made.

B.K.
  • 9,982
  • 10
  • 73
  • 105
4
    private void comboBox1_TextChanged(object sender, EventArgs e)
    {
        if (comboBox1.Text == "")
            comboBox1.Text = "Select one of the answers"; 
    }

should do the trick at startup this line is present, when selected an item on combobox, this items text will appear. when deleling the text this text will appear again

Moonlight
  • 708
  • 1
  • 7
  • 18
  • 3
    For some reason it just don't work. This solution is one that everyone suggest here, but when I set Text property it does not keep passed value. Maybe ToString method of items in collection have priority over Text property. – jotbek Nov 09 '11 at 12:07
  • it works for dropdown and simple style, DropdownList is not working. – Moonlight Nov 09 '11 at 14:11
  • I would rather use string.Empty instead of "" for creating an empty text box. – HelpNeeder Nov 29 '11 at 19:00
3

I used a quick work around so I could keep the DropDownList style.

class DummyComboBoxItem
{
    public string DisplayName
    {
        get
        {
            return "Make a selection ...";
        }
    }
}
public partial class mainForm : Form
{
    private DummyComboBoxItem placeholder = new DummyComboBoxItem();
    public mainForm()
    {
        InitializeComponent();

        myComboBox.DisplayMember = "DisplayName";            
        myComboBox.Items.Add(placeholder);
        foreach(object o in Objects)
        {
            myComboBox.Items.Add(o);
        }
        myComboBox.SelectedItem = placeholder;
    }

    private void myComboBox_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (myComboBox.SelectedItem == null) return;
        if (myComboBox.SelectedItem == placeholder) return;            
        /*
            do your stuff
        */
        myComboBox.Items.Add(placeholder);
        myComboBox.SelectedItem = placeholder;
    }

    private void myComboBox_DropDown(object sender, EventArgs e)
    {
        myComboBox.Items.Remove(placeholder);
    }

    private void myComboBox_Leave(object sender, EventArgs e)
    {
        //this covers user aborting the selection (by clicking away or choosing the system null drop down option)
        //The control may not immedietly change, but if the user clicks anywhere else it will reset
        if(myComboBox.SelectedItem != placeholder)
        {
            if(!myComboBox.Items.Contains(placeholder)) myComboBox.Items.Add(placeholder);
            myComboBox.SelectedItem = placeholder;
        }            
    }       
}

If you use databinding you'll have to create a dummy version of the type you're bound to - just make sure you remove it before any persistence logic.

Jesse Adam
  • 415
  • 3
  • 14
2

Make the Dropdownstyle property of combo box to Dropdown and set the combo box text to "Select" as below

            combobox.DataSource = dsIn.Tables[0];
            combobox.DisplayMember = "Name";
            combobox.ValueMember = "Value";
            combobox.Text = "--Select--";
prema
  • 427
  • 4
  • 16
1

One line after form InitializeComponent();

cbo_MyDropBox.Text = "Select a server...";

You only need it once right? All you need to do if a pick is mandatory is check if the box index != -1. Could anyone elaborate why the other answers jump through hoops to get this going?

The only thing I'm missing here is having just this initial text grayed out. If you really want that just use a label in front and turn it off once the index is changed.

Madmenyo
  • 8,389
  • 7
  • 52
  • 99
1

If none of the previous solution are working for you, why not add some validation on combobox something like,

    var orginalindex = 0;

    private void comboBox1_SelectedItemChanged(object sender, EventArgs e)
    {
        if (comboBox1.SelectedIndex == 0)
        {
            comboBox1.Text = "Select one of the answers";
            comboBox1.SelectedIndex = comboBox1.SelectedIndex;
        }
        else
        {
            orginalindex = comboBox1.SelectedIndex;
        }
    }
Ash Burlaczenko
  • 24,778
  • 15
  • 68
  • 99
1

Here you can find solution created by pavlo_ua: If you have .Net > 2.0 and If you have .Net == 2.0 (search for pavlo_ua answer)

Cheers, jbk

edit: So to have clear answer not just link

You can set Text of combobox when its style is set as DropDown (and it is editable). When you have .Net version < 3.0 there is no IsReadonly property so we need to use win api to set textbox of combobox as readonly:

private bool m_readOnly = false;
private const int EM_SETREADONLY = 0x00CF;

internal delegate bool EnumChildWindowsCallBack( IntPtr hwnd, IntPtr lParam );

[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

[ DllImport( "user32.dll" ) ]
internal static extern int EnumChildWindows( IntPtr hWndParent, EnumChildWindowsCallBack lpEnumFunc, IntPtr lParam );


private bool EnumChildWindowsCallBackFunction(IntPtr hWnd, IntPtr lparam)
{
      if( hWnd != IntPtr.Zero )
       {
              IntPtr readonlyValue = ( m_readOnly ) ? new IntPtr( 1 ) : IntPtr.Zero;
             SendMessage( hWnd, EM_SETREADONLY, readonlyValue, IntPtr.Zero );
             comboBox1.Invalidate();
             return true;
       }
       return false;
}

private void MakeComboBoxReadOnly( bool readOnly )
{
    m_readOnly = readOnly;
    EnumChildWindowsCallBack callBack = new EnumChildWindowsCallBack(this.EnumChildWindowsCallBackFunction );
    EnumChildWindows( comboBox1.Handle, callBack, IntPtr.Zero );
}
jotbek
  • 1,479
  • 3
  • 14
  • 22
  • Whilst this may theoretically answer the question, [it would be preferable](http://meta.stackexchange.com/q/8259) to include the essential parts of the answer here, and provide the link for reference. – codingbadger Nov 10 '11 at 20:20
  • you can use e.Handled = true in keypress event of combobox to prevent user from changing text in dropdown mode – Arash Jul 21 '12 at 19:46
0

if ComboBoxStyle is set to DropDownList then the easiest way to make sure the user selects an item is to set SelectedIndex = -1, which will be empty

Irshad
  • 3,071
  • 5
  • 30
  • 51
Harry
  • 1,765
  • 1
  • 12
  • 12
0

I realize this is an old thread, but just wanted to let others who might search for an answer to this question know, in the current version of Visual Studio (2015), there is a property called "Placeholder Text" that does what jotbek originally asked about. Use the Properties box, under "Common" properties.

wheezer
  • 135
  • 1
  • 2
  • 9
  • 2
    That sounds good but I'm not seeing that property under `ComboBox` on https://msdn.microsoft.com/en-us/library/system.windows.forms.combobox(v=vs.110).aspx – user276648 Mar 30 '17 at 02:28
0

Unfortunately none of the above worked for me, so instead I added a label on top of the comboxbox that says "Please select". I used the following code to show and hide it:

  1. When I initialise my combobox, if there is no selected value I bring it to the front and set the text:

    PleaseSelectValueLabel.BringToFront();
    PleaseSelectValueLabel.Text = Constants.AssessmentValuePrompt;
    
  2. If there is a value selected I send it to the back:

    PleaseSelectValueLabel.SendToBack();
    
  3. I then use the following events to move the label to the front or back depending on whether the user has selected a value:

    private void PleaseSelectValueLabel_Click(object sender, EventArgs e)
    {
        PleaseSelectValueLabel.SendToBack();
        AssessmentValue.Focus();
    }
    
    private void AssessmentValue_Click(object sender, EventArgs e)
    {
        PleaseSelectValueLabel.SendToBack();
    }
    
    //if the user hasnt selected an item, make the please select label visible again
    private void AssessmentValue_Leave(object sender, EventArgs e)
    {
        if (AssessmentValue.SelectedIndex < 0)
        {
            PleaseSelectValueLabel.BringToFront();
        }
    }
    
majjam
  • 1,286
  • 2
  • 15
  • 32
0

I was hoping to find a solution to this as well. I see that this is an older post, but hoping my approach might simplify this problem for someone else.

I was using a combobox with a drop down style of DropDownList, but this should work with other styles. In my case I wanted the text to read "Select Source" and I wanted the other options to be "File" and "Folder"

comboBox1.Items.AddRange(new string[] {"Select Source", "File", "Folder" });
comboBox1.Text = "Select Source";

You can select the 0 index instead if you like. I then removed the "Select Source" item when the index is changed as it no longer matters if that text is visible.

comboBox1.SelectedIndexChanged += new EventHandler(comboBox1_IndexChanged);

private void comboBox1_IndexChanged(object sender, EventArgs e)
    {
        comboBox1.Items.Remove("Select Source");
        if (comboBox1.SelectedIndex != -1)
        {
            if (comboBox1.SelectedIndex == 0) // File
            {
                // Do things
            }
            else if (comboBox1.SelectedIndex == 1) // Folder
            {
                // Do things
            }
        }
    }

Thanks

NAPTlME
  • 1
  • 1
0

I could not get @Andrei Karcheuski 's approach to work but he inspired me to this approach: (I added the Localizable Property so the Hint can be translated through .resx files for each dialog you use it on)

 public partial class HintComboBox : ComboBox
{
    string hint;
    Font greyFont;

    [Localizable(true)]
    public string Hint
    {
        get { return hint; }
        set { hint = value; Invalidate(); }
    }

    public HintComboBox()
    {
        InitializeComponent();
    }

    protected override void OnCreateControl()
    {
        base.OnCreateControl();

        if (string.IsNullOrEmpty(Text))
        {
            this.ForeColor = SystemColors.GrayText;
            Text = Hint;
        }
        else
        {
            this.ForeColor = Color.Black;
        }
    }

    private void HintComboBox_SelectedIndexChanged(object sender, EventArgs e)
    {
        if( string.IsNullOrEmpty(Text) )
        {
            this.ForeColor = SystemColors.GrayText;
            Text = Hint;
        }
        else
        {
            this.ForeColor = Color.Black;
        }
    }
wecky
  • 754
  • 9
  • 17
0

Add this class to your solution. You will see ComboBoxPlus in your toolbox after rebuild the solution. It works well with DropDownList for DropDownStyle of comboBox. Enjoy it.

class ComboBoxPlus : System.Windows.Forms.ComboBox
    {
        private const int CB_SETCUEBANNER = 0x1703;

        [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
        private static extern int SendMessage(System.IntPtr hWnd, int msg, int wParam, [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]string lParam);

        private string placeholder = string.Empty;
        public string Placeholder
        {
            get { return placeholder; }
            set
            {
                placeholder = value;
                SendMessage(Handle, CB_SETCUEBANNER, 0, Placeholder);
            }
        }
    }
-3

Why not do it XAML?

<ComboBox x:Name="myComboBoxMenu" PlaceholderText="Hello"/>
Rizwan Asif
  • 125
  • 1
  • 8
  • 1
    this is invalid in XAML – Regis Portalez May 13 '16 at 10:56
  • 1
    I understand your point, XAML is better etc etc. But when a question is asked in a specific technology, probably someone is supporting legacy code and cannot switch or is anyway determined to use that technology. Telling someone to use xaml instead of winforms is like telling someone to use Java instead of C# (or any other pair for that matter). It could be better, help more etc. But not that simple and adds no value as an answer – John Demetriou Oct 26 '18 at 05:57