views:

3225

answers:

2

Hi,

I have a situation when I want to change the selected value of a combo box in a windows forms application by using Ctrl-Left ori Ctrl-Right. However if the combobox is the selected control on the form this does not work. The selected value is set to the old value after it is set to the new value. If the combobox is not the selected control then everything works fine. In my application the combo can receive focus because the values can also be changed with the mouse. While what I want can be accomplished by selecting some other control on the form before actually changing the value I don't like this solution to much.

So, there are two questions: Why is the selectedIndexChanged event fired with the old value if the combo box is the selected control on the form and How can avoid this or if I can't what is a good workaround ? Further is some code to illustrate my issue. If you drop a combo box and a richtext box on a form and copy the below code in Form1.cs you can see my problem. You need to make the combo DropDownList Style and add some numbers as items (1, 2, 3 is fine)

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        comboBox1.SelectedIndex = 0;
    }

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        if (keyData == (Keys.Control | Keys.Left))
        {
            DoUpdate();
        }
        return base.ProcessCmdKey(ref msg, keyData);
    }

    private void DoUpdate()
    {
        int index = Convert.ToInt32(comboBox1.SelectedItem);
        index++;
        if (comboBox1.Focused)
            richTextBox1.Select();
        comboBox1.SelectedItem = index.ToString();
    }

    private void SetComboValue(int value)
    {
        comboBox1.SelectedItem = value.ToString();
        richTextBox1.AppendText(string.Format("Set value {0} \r\n", value.ToString()));         
    }

    private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
    {
        richTextBox1.AppendText(string.Format("Selected index changed before {0}\r\n", comboBox1.SelectedItem));
        SetComboValue(Convert.ToInt32(comboBox1.SelectedItem));
        richTextBox1.AppendText(string.Format("Selected index changed after {0}\r\n", comboBox1.SelectedItem));
    }
}
A: 

Why don't you just run the SetComboValue method from your DoUpdate method? If that doesn't work you could always set a flag that states it's set from the DoUpdate method and only run the selected index changed event when directly accessed. Just a thought.

We do something similar when we have a bunch of events tied to comboboxes and lists during our load of the form. We are setting values and don't want the events to fire during load so we have a flag that keeps the events from firing until the form load is complete.

My thought is that you are setting the value but something else is forcing a second selecteditem change event to occur. However it also looks like you could get into a endless loop since you set the selectedindex in the DoUpdate method and the SetComboValue method.

Just change the selected index in one place.

Joshua Cauble
Thank you for your comment Joshua. This will not change the problem because in SetComboValue is this line: comboBox1.SelectedItem = value.ToString(); (actually it was comboBox1.SelectedItem = Convert.ToInt32(value);but I changed that because is incorrect) which will trigger the selectedIndexChanged event one more time. So even if I set the value even if I call a method to set the value I have the same issue. I'm not calling SetComboValue because of design reasons. I think it is better to have that function called by some other methods.
mosu
I try to avoid adding another global variable to act as a flag because I already have some flags to care about and I don't consider this, in my case, a good design.
mosu
+1  A: 

This is caused by the fact that by default, if the combobox has focus, then pressing left and right(without control) will change the value.

You are catching ctrl-left, incrementing the value ... and then passing on the keypress to the base object, which interprets the left keypress and decrements the selected item.

Change your code as follows, to swallow the keypress, and things seem to work as expected:

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        if (keyData == (Keys.Control | Keys.Left))
        {
            DoUpdate();
            return true;
        }
        else
        {
            return base.ProcessCmdKey(ref msg, keyData);
        }
    }
rcknight
You are terrible right :-)I was so focus on the Ctrl-Left think that I forgat that I was pressing the Left and Right keys and these will also act like a cursor on the drop down list. However now I have a question: if I return that true value in ProcessCmdKey will this key combination propagate further to other programs that "listen" for these keys or not ?I have a small application installed on my computer and this is doing something when I press Ctrl-M but also, somehow, it stops propagating this key combination (as a result, Ctrl-M in VS will do nothing in this particular case)
mosu
I'm not sure what the behaviour will be when other apps are listening, but msdn says this: "When overriding the ProcessCmdKey method in a derived class, a control should return true to indicate that it has processed the key. For keys that are not processed by the control, the result of calling the base class's ProcessCmdKey method should be returned." (http://msdn.microsoft.com/en-us/library/system.windows.forms.control.processcmdkey.aspx) ... So I guess it is the correct action to take??
rcknight
I handled the Shift key pressing and it seams not to be a problem returning true. After pressing 5 times the Windows StickyKeys message appears so I think the message is sent further.Thank you very much for you reply.
mosu