2009-04-14 35 views
2

我有一個文本框,我想將所選項目的數量限制爲MaxSelection。期望的行爲是一旦MaxSelection項目被選中,任何進一步的選擇都將被忽略。 (因此這個問題不同於「limit selections in a listbox in vb.net」)。如果在列表框中選擇了多於X個項目,請回到上一個選擇

我有一個嘗試完成此操作的列表框的SelectedIndexChanged事件的事件處理程序。如果用戶使用Ctrl單擊選擇(MaxSelection + 1)項目,則選擇將恢復爲上一個選擇。

問題是當用戶選擇一個項目,然後按住Shift並單擊MaxSelection +列表中下一個項目。在這種情況下,將引發多個SelectedIndexChanged事件:一個用於按住Shift鍵點按選擇按住Shift鍵單擊的項目,另一個用於選擇原始選擇項和Shift按下的選擇項之間的所有項目。這些事件中的第一個允許用戶選擇按住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(); 
     } 

    } 

回答

2

一些第三方組件有取消的事件,如BeforeSelectedIndexChanged。

但是,當使用MS默認組件時,我認爲你的方法基本上是你需要的。您還可以將選擇存儲在已知會在更改前觸發的其他事件(如MouseDown或KeyDown)中。

+0

不幸的是,我發現的MouseDown和的KeyDown是在SelectedValueChanged事件後開火。然而,你已經啓發了一個使用MouseUp的解決方案,我將很快發佈。非常感謝。 – 2009-04-15 15:39:01

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); 
     } 
    } 
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); 
    } 
} 
相關問題