Если в ListBox выбрано более X элементов, вернитесь к предыдущему выбору - PullRequest
2 голосов
/ 14 апреля 2009

У меня есть текстовое поле, в котором я хочу ограничить количество выбранных элементов до MaxSelection. Желаемое поведение состоит в том, что после выбора элементов MaxSelection любые дальнейшие выборы игнорируются. (Таким образом, этот вопрос отличается от " предельных значений в списке в vb.net ").

У меня есть обработчик события SelectedIndexChanged для списка, который пытается это сделать. Если пользователь использует Ctrl-щелчок, чтобы выбрать (MaxSelection + 1) -й элемент, выбор возвращается к предыдущему.

Проблема заключается в том, что пользователь выбирает элемент, а затем нажимает Shift и щелкает элемент вниз по списку, то есть MaxSelection + 1 элемент дальше по списку. В этом случае возникает более одного события SelectedIndexChanged: одно для Shift-click, которое выбирает элемент, который был нажат Shift, и одно для выбора всех элементов между исходным выбором и выбором Shift-clicked. Первое из этих событий позволяет пользователю выбрать элемент, нажатый по нажатию клавиши Shift (что технически правильно), затем второе событие возвращает выбор к выбору, как это было после первого события (которое будет первоначально выбранным элементом и кнопкой Shift). щелкнул пункт). Желательно, чтобы код возвращал выбор к выбору до первого события (которое является только первоначально выбранным элементом).

Есть ли способ сохранить выделение до нажатия Shift?

Спасибо, Rob

Вот обработчик события SelectedIndexChanged:

    void ChildSelectionChanged(object sender, EventArgs e)
    {
        ListBox listBox = sender as ListBox;

        //If the number of selected items is greater than the number the user is allowed to select
        if ((this.MaxSelection != null) && (listBox.SelectedItems.Count > this.MaxSelection))
        {
            //Prevent this method from running while reverting the selection
            listBox.SelectedIndexChanged -= ChildSelectionChanged;

            //Revert the selection to the previous selection
            try
            {
                for (int index = 0; index < listBox.Items.Count; index++)
                {
                    if (listBox.SelectedIndices.Contains(index) && !this.previousSelection.Contains(index))
                    {
                        listBox.SetSelected(index, false);
                    }
                }
            }
            finally
            {
                //Re-enable this method as an event handler for the selection change event
                listBox.SelectedIndexChanged += ChildSelectionChanged;
            }
        }
        else
        {
            //Store the current selection
            this.previousSelection.Clear();
            foreach (int selectedIndex in listBox.SelectedIndices)
            {
                this.previousSelection.Add(selectedIndex);
            }

            //Let any interested code know the selection has changed.
            //(We do not do this in the case where the selection would put
            //the selected count above max since we revert the selection;
            //there is no net effect in that case.)
            RaiseSelectionChangedEvent();
        }

    }

Ответы [ 3 ]

2 голосов
/ 14 апреля 2009

Некоторые сторонние компоненты имеют отменяемые события, такие как BeforeSelectedIndexChanged.

Но при использовании компонента MS по умолчанию, я думаю, что ваш подход в основном то, что вам нужно. Вы также можете сохранить выборку в других событиях (таких как MouseDown или KeyDown), которые, как известно, сработали до изменения.

1 голос
/ 15 апреля 2009

Благодаря пониманию Лусеро, что я могу поместить код для сохранения выбора в другое событие, я смог создать решение с помощью MouseUp. Как указано в комментариях к вопросу Люсеро, MouseDown срабатывает после события SelectedValueChange, поэтому вместо этого я должен использовать MouseUp. Вот код:

    /// <summary>
    /// Handle the ListBox's SelectedValueChanged event, revert the selection if there are too many selected
    /// </summary>
    /// <param name="sender">the sending object</param>
    /// <param name="e">the event args</param>
    void ChildSelectionChanged(object sender, EventArgs e)
    {
        ListBox listBox = sender as ListBox;

        //If the number of selected items is greater than the number the user is allowed to select
        if ((this.MaxSelection != null) && (listBox.SelectedItems.Count > this.MaxSelection))
        {
            //Prevent this method from running while reverting the selection
            listBox.SelectedIndexChanged -= ChildSelectionChanged;

            //Revert the selection to the previously stored selection
            try
            {
                for (int index = 0; index < listBox.Items.Count; index++)
                {
                    if (listBox.SelectedIndices.Contains(index) && !this.previousSelection.Contains(index))
                    {
                        listBox.SetSelected(index, false);
                    }
                }
            }
            catch (ArgumentOutOfRangeException ex)
            {
            }
            catch (InvalidOperationException ex)
            {
            }
            finally
            {
                //Re-enable this method as an event handler for the selection change event
                listBox.SelectedIndexChanged += ChildSelectionChanged;
            }
        }
        else
        {
            RaiseSelectionChangedEvent();
        }
    }

    /// <summary>
    /// Handle the ListBox's MouseUp event, store the selection state.
    /// </summary>
    /// <param name="sender">the sending object</param>
    /// <param name="e">the event args</param>
    /// <remarks>This method saves the state of selection of the list box into a class member.
    /// This is used by the SelectedValueChanged handler such that when the user selects more 
    /// items than they are allowed to, it will revert the selection to the state saved here 
    /// in this MouseUp handler, which is the state of the selection at the end of the previous
    /// mouse click.  
    /// We have to use the MouseUp event since:
    /// a) the SelectedValueChanged event is called multiple times when a Shift-click is made;
    /// the first time it fires the item that was Shift-clicked is selected, the next time it
    /// fires, the rest of the items intended by the Shift-click are selected.  Thus using the
    /// SelectedValueChanged handler to store the selection state would fail in the following
    /// scenario:
    ///   i)   the user is allowed to select 2 items max
    ///   ii)  the user clicks Line1
    ///   iii) the SelectedValueChanged fires, the max has not been exceeded, selection stored
    ///        let's call it Selection_A which contains Line1
    ///   iii) the user Shift-clicks and item 2 lines down from the first selection called Line3
    ///   iv)  the SelectedValueChanged fires, the selection shows that only Line1 and Line3 are
    ///        selected, hence the max has not been exceeded, selection stored let's call it 
    ///        Selection_B which contains Line1, Line3
    ///   v)   the SelectedValueChanged fires again, this time Line1, Line2, and Line3 are selected,
    ///        hence the max has been exceeded so we revert to the previously stored selection
    ///        which is Selection_B, what we wanted was to revert to Selection_A
    /// b) the MouseDown event fires after the first SelectedValueChanged event, hence saving the 
    /// state in MouseDown also stores the state at the wrong time.</remarks>
    private void valuesListBox_MouseUp(object sender, MouseEventArgs e)
    {
        if (this.MaxSelection == null)
        {
            return;
        }

        ListBox listBox = sender as ListBox;

        //Store the current selection
        this.previousSelection.Clear();
        foreach (int selectedIndex in listBox.SelectedIndices)
        {
            this.previousSelection.Add(selectedIndex);
        }
    }
0 голосов
/ 03 июля 2013

Я думаю, что это простой способ, в примере ограничение составляет 6 пунктов.

string[] lbitems;
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    ListBox listBox = (ListBox)sender;
    if (listBox.SelectedItems.Count == 7)
    {
        for (int i = 0; i < listBox.SelectedItems.Count; i++)
        {
            bool trovato = false;
            for (int j = 0; j < lbitems.Length; j++)
            {
                if (listBox.SelectedItems[i] == lbitems[j])
                {
                    trovato = true;
                    break;
                }
            }

            if (trovato == false)
            {
                listBox.SelectedItems.Remove(listBox.SelectedItems[i]);
                break;
            }
        }
    }
    else
    {
        lbitems = new string[listBox.SelectedItems.Count];
        listBox.SelectedItems.CopyTo(lbitems, 0);
    }
}
...