Share via


Quick Gotcha with SelectionChanged on ComboBoxes

Been a while since I've been working on the soon-to-be-not-so-uber-secret private project, but now that I'm back on task I've come across a nice little coding gotcha to share with my loyal reader(s).

Situation: You have a ComboBox on your application that changes some major setting, and you'd like to pop up a MessageBox to the user to let her know she's doing something big and to verify the action whenever she changes the SelectedItem of the ComboBox.  In fact, if she decides against it, you undo the change.

(Bad) Solution:

EssentialDataGrouping edg = (EssentialDataGrouping)this.DataContext;
Universe old = edg.CurrentUniverse;
Universe temp = this.UniverseComboBox.SelectedItem as Universe;
MessageBoxResult result = MessageBox.Show("Change current universe?  Changes will be saved to this universe.", temp.ToString(), MessageBoxButton.OKCancel);
switch (result)
{
    case MessageBoxResult.OK:
        edg.SaveUniverse();
        edg.CurrentUniverse = temp;
        this.currentUniverse = temp.ID;
        break;
    case MessageBoxResult.Cancel:
        this.changingUnivese = false;
        this.UniverseComboBox.SelectedItem = old;
        break;
    default:
        break;
}

Problem: The SelectionChanged even fires when you first load the window, so the user gets that confirmation box before the app starts.  Also, if she chooses to cancel her change, the event gets fired again, since you are actually changing the SelectedItem...

(Better) Solution:

Your warning code needs to be put in a separate private method, whose input is a bool that basically decides whether or not the logic should be run.  What you really want to do is run this logic whenever the user is trying to change the selection of the combobox, and now run the logic when you change the selection behind the scenes or when the event is fired by setup. 

This can be done by creating a global bool variable that is set to false by default.  The only way the user should be changing the universe is with a mouse click.... so why not handle those events? (Note: if you want the user to be able to use the keyboard as well, you can turn on the global flag when the ComboBox has Keyboard focus and off when it loses focus.)  PreviewMouseLeftButtonDown and MouseLeftButtonUp are the pair you want... set the global to true on PreviewMouseLeftButtonDown and false on MouseLeftButtonUp.  You want PreviewMLBDown rather than MLBDown b/c you want to make sure to turn on the flag before the click.

In the case where you change the selection yourself (when the user chooses to cancel her action), you need only set the global bool to false.  Note that at the end of the method, if you are allowing for keyboard changes, you should check for keyboard focus and change the global flag to true if focused (otherwise you'd cancel and then not check again if the user doesn't shift keyboard focus).