我有一个文本框,我想将所选项目的数量限制为MaxSelection。期望的行为是,一旦选择了MaxSelection项,则忽略任何进一步的选择。 (因此这个问题与“limit selections in a listbox in vb.net”不同。
我为列表框的SelectedIndexChanged事件设置了一个事件处理程序,试图完成此操作。如果用户使用Ctrl-单击选择(MaxSelection + 1)项,则选择将恢复为之前的选择。
问题是当用户选择一个项目,然后按住Shift键并单击列表中的项目,该项目是列表中的MaxSelection + 1项目。在这种情况下,会引发多个SelectedIndexChanged事件:一个用于Shift-单击,用于选择按住Shift键单击的项目,另一个用于选择原始选择和按住Shift键单击的选择之间的所有项目。这些事件中的第一个允许用户选择Shift-clicked项目(这在技术上是正确的),然后第二个事件将选择恢复为第一个事件之后的选择(这将是最初选择的项目和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();
}
}
答案 0 :(得分:2)
某些第三方组件具有可取消的事件,例如BeforeSelectedIndexChanged。
但是在使用MS默认组件时,我认为您的方法基本上就是您所需要的。您还可以将选择存储在已知在更改之前触发的其他事件(例如MouseDown或KeyDown)中。
答案 1 :(得分: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);
}
}
答案 2 :(得分: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);
}
}