Thursday, November 06, 2008

Using ComboBox.Text as a Hint

At work I was after a quick-and-dirty way to get a ComboBox to show some hint text like this:

image

When the user chooses an item, I want to kick off a task but leave the ComboBox exactly as it was (including the hint text). I know there are nicer ways to achieve this by customizing the ComboBox template or by using a different control entirely, but I was integrating this with a third-party ribbon control that precluded any fancy stuff.

The ComboBox control has a Text property that MSDN explains as follows:

When the IsEditable property is true, setting this property places initial text entered in the text box.

My ComboBox is editable to ensure the Text property is displayed, but is read-only to ensure that the user cannot type any text themselves. What MSDN fails to explain is what constitutes “initial”. Is it a one-off thing? Does “initial” mean when there is no selected item?

My first attempt at resetting the ComboBox after a selection was made looked something like this:

_comboBox.SelectionChanged += delegate
{
    if (_comboBox.SelectedIndex == -1)
    {
        return;
    }

    _comboBox.SelectedIndex = -1;
    _comboBox.Text = "Select an Item";
};

Alas, setting the Text property had no effect. The ComboBox would just appear blank after this code executed. Even if I completely reset the ComboBox bindings and properties, it still refused to display the value of the Text property.

I was beginning to think the MSDN documentation was accurate, if a little lacking. It did seem as if the Text property would only be used once prior to the user making a selection. Thereafter, it did not seem to take effect.

On a hunch, I decided to try setting the Text property in a separate message:

_comboBox.SelectionChanged += delegate
{
    if (_comboBox.SelectedIndex == -1)
    {
        return;
    }

    _comboBox.SelectedIndex = -1;
    Dispatcher.BeginInvoke(new ThreadStart(delegate
    {
        _comboBox.Text = "Select an Item";
    }));
};

That did the trick. The ComboBox now correctly displays the Text property after the user selects an item. This seems like something of a bug to me, but I haven’t really investigated further.

You may wonder whether it’s necessary for us to set the SelectedIndex to -1 anymore. It probably isn’t because your hint text is unlikely to match selectable items in the ComboBox. However, if there happens to be a selectable item with the same value as the Text property, setting the Text property will select that item and affect SelectedIndex accordingly. That may or may not be a problem in your scenario. Better to be safe than sorry, I figure…

3 comments:

Matteo Valdina said...

Thanks for this tip. I hope that with new .NET framework this tip still work.

Anonymous said...

Thank you, I ran into exactly that problem trying to do the same thing with a combo. This was exactly the answer I was looking for. Lucky me.
This is not a bug btw, you can't access directly the UI thread at runtime. I work with 3.5 and it should work with every version.
Cheers

Anonymous said...

WOW.

This helped me much.
I did not want to do anything more but set Text to the combobox originally bound to a viewmodel property with Text="{Binding SelectedStrategy}" but for special reasons I sometimes wanted to show :something else: in TextBox.

So I disconnected the XAML/defined binding, setup viewmodel PropertyChange handler in code behind and set Text there.
But it mysteriously did not work.

It works just after I have used your hint... 3 years after it had been posted ;)