2

我有一个文本框,我想将所选项目的数量限制为 MaxSelection。期望的行为是一旦选择了 MaxSelection 项目,任何进一步的选择都将被忽略。(因此这个问题不同于“在 vb.net 的列表框中限制选择”)。

我有一个用于尝试完成此操作的列表框的 SelectedIndexChanged 事件的事件处理程序。如果用户使用 Ctrl 键单击选择第 (MaxSelection+1) 项,则选择恢复为先前的选择。

问题是当用户选择一个项目,然后按住 Shift 键单击列表下方的项目,即 MaxSelection+1 项目在列表下方。在这种情况下,会引发多个 SelectedIndexChanged 事件:一个用于 Shift-click 事件,该事件选择被 Shift-clicked 的项目,一个用于选择原始选择和 Shift-clicked 选择之间的所有项目。这些事件中的第一个允许用户选择按住 Shift 键单击的项目(这在技术上是正确的),然后第二个事件将选择恢复为第一个事件之后的选择(这将是最初选择的项目和 Shift -单击的项目)。所需要的是代码会将选择恢复为第一个事件之前的选择(这只是最初选择的项目)。

有没有办法在 Shift 单击之前保留选择?

谢谢,罗伯

这是 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();
        }

    }
4

3 回答 3

2

一些第 3 方组件具有可取消的事件,例如 BeforeSelectedIndexChanged。

但是在使用MS默认组件时,我认为你的方法基本上就是你所需要的。您还可以将选择存储在已知在更改之前触发的其他事件(例如 MouseDown 或 KeyDown)中。

于 2009-04-14T19:45:01.530 回答
1

感谢 Lucero 的洞察力,即我可以将代码存储在另一个事件中,我能够使用 MouseUp 创建一个解决方案。正如对 Lucero 问题的评论中所述,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);
        }
    }
于 2009-04-15T18:23:38.440 回答
0

我认为这是简单的方法,在示例中限制为 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);
    }
}
于 2013-07-03T11:18:49.330 回答