Multi value ListBox automatically selects undesire

2019-08-05 18:56发布

问题:

I have an object that has some attributes from the list selected - let's say a Promotion that can have 0 to X communication channels. To display/edit this information I am using a listbox with option SelectionMode==MultiExtended.

But in some cases it is behaving strangely

  1. I have Promotion with 2 communication channels selected (first and last out of three channels),

  2. I click on a second channel (that previously was the only unselected channel) and know it shows, that 1st and 2nd channels are selected (I placed a check at the beginning of the listbox SelectedIndexChanged event - and it shows that SelectedItems.Count==2, although I clicked on a single item not holding Ctrl or Shift keys) and in this case SelectedIndexChanged event is triggered twice in all other cases it is triggered just once

  3. This happens only after the first time I open this dialogform, if I manually select 1st and 3rd item of Channels, and then click on the 2nd item - then it works properly

Screencast of a problem in action

http://screencast.com/t/lVs0e9oau

This is how I load list of all possible channels into listbox

foreach (var ct in Promotion_operations.Configuration.PromoCommunicationTypes)
{
    KeyValuePair<string, PromotionCommunicationType> nct = 
        new KeyValuePair<string, PromotionCommunicationType>(ct.Name, ct);
    communications.Add(nct);
}
PromotionCommunicationList.DataSource = communications; //Promotion_operations.Configuration.PromoCommunicationTypes;
PromotionCommunicationList.DisplayMember = "Key";
PromotionCommunicationList.ValueMember = "Value";

This is how I load selecteditems based on Promotion's data

private void LoadSelectedCommunicationsList(ListBox lstbox, List<PromotionCommunication> communications)
{
    lstbox.SelectedItems.Clear();
    foreach (var ct in communications)
    {
        for (int j = 0; j < lstbox.Items.Count; j++)
        {                    
            if (ct.CommunicationType.Id == ((KeyValuePair<string, PromotionCommunicationType>)lstbox.Items[j]).Value.Id)
            {
                lstbox.SelectedItems.Add(lstbox.Items[j]);
            }
        }
    }
}

What could be the cause of this behaviour?

that clicking on one previously unselected list selects both - newly selected item and first item of the list?

回答1:

Your PromotionCommunicationList and HistoryCommunicationList are sharing the same reference to your list of objects as DataSource. That said, they have the same BindingContext and share the same CurrencyManager. CurrencyManager is remembering selected items of your ListBox control and that's where your conflict is created because he's saving selected items of both of your ListBoxes. You already found the solution for your problem because new CurrencyManager is created when you set "different" list (the copy of your original one) as DataSource. Another possible solution would be the creation of new BindingContext for one of your ListBox controls.
You can try this out:

PromotionCommunicationList.DataSource = communications;
(..)
HistoryCommunicationList.BindingContext = new BindingContext(); // Add this
HistoryCommunicationList.DataSource = communications;

It should solve your problem. For more information about BindingContext check this link on MSDN.



回答2:

I found the cause of the problem, though I don't really understand why it caused such a behaviour (if someone will answer that question, I will accept it as an answer to this question)

I had 2 listbox-es in my form and both of them where using the same collection as a Datasource, BUT!!! SelectedItems was selected using code (acctually it seems that in winforms it is not possible to databind listbox's selecteditems)

INITIALLY My code was:

PromotionCommunicationList.DataSource = communications;
(..)
HistoryCommunicationList.DataSource = communications;

Corrected version is:

PromotionCommunicationList.DataSource = communications.ToList();
(..)
HistoryCommunicationList.DataSource = communications.ToList();

I know that ToList() makes a copy, but I don't understand what's wrong with having the same collection as DataSource for list items of 2 listbox-es? Why does this have an impact on SelectedItems collection?