2009-04-14 5 views
2

선택한 항목의 수를 MaxSelection으로 제한하려는 텍스트 상자가 있습니다. 원하는 동작은 MaxSelection 항목을 선택하면 더 큰 선택 항목이 무시된다는 것입니다. (따라서이 질문은 "limit selections in a listbox in vb.net"과 다릅니다).ListBox에서 X 개 이상의 항목을 선택한 경우 이전 선택으로 되돌립니다.

이 작업을 시도하는 목록 상자의 SelectedIndexChanged 이벤트에 대한 이벤트 처리기가 있습니다. 사용자가 Ctrl + 클릭을 사용하여 (MaxSelection + 1) 번째 항목을 선택하면 이전 선택 항목으로 되돌아갑니다.

사용자가 항목을 선택한 다음 목록에서 MaxSelection + 1 항목 아래의 항목을 Shift 키를 누른 채로 클릭 할 때 문제가 발생합니다. 이 경우 하나 이상의 SelectedIndexChanged 이벤트가 발생합니다. 하나는 Shift 키를 누른 상태에서 클릭 한 항목을 선택하는 항목이고, 다른 하나는 원래 항목과 Shift 키를 누른 상태로 선택한 항목 사이에있는 항목을 모두 선택하는 항목입니다. 이러한 이벤트 중 첫 번째 이벤트는 사용자가 Shift 키를 누른 항목 (기술적으로는 정확함)을 선택할 수있게 한 다음 두 번째 이벤트는 첫 번째 이벤트 이후의 선택 항목으로 되돌립니다 (원래 선택한 항목과 Shift 클릭 된 항목). 원하는 것은 코드가 첫 번째 이벤트 (원래 선택한 항목 만) 이전에 선택 항목을 선택 항목으로 되돌릴 수 있다는 것입니다.

Shift 키를 누르기 전에 선택 사항을 유지할 수있는 방법이 있습니까? 일부 제 3 자 구성 요소는 BeforeSelectedIndexChanged 같은 취소 할 이벤트가

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

:

감사합니다, 롭

는 여기의 SelectedIndexChanged 이벤트 핸들러입니다.

하지만 MS 기본 구성 요소를 사용하면 기본적으로 사용자의 접근 방식이 필요하다고 생각합니다. 변경하기 전에 트리거 된 것으로 알려진 다른 이벤트 (예 : MouseDown 또는 KeyDown)에 선택 영역을 저장할 수도 있습니다.

+0

불행히도 SelectedValueChanged 이벤트 후에 MouseDown 및 KeyDown이 실행되고 있음을 발견했습니다. 그러나 곧 내가 게시 할 MouseUp을 사용하는 솔루션에 영감을 얻었습니다. 정말 고마워. –

+2

나는 그것을 테스트하지는 않았지만 전에 해고해야한다고 생각했다. 죄송합니다. 해결 방법을 찾았다는 소식을 듣게되어 기쁩니다. 그러나 "최악"의 경우에는 ListBox에서 상속 된 자체 컨트롤을 만들고 WndProc 메서드를 재정 의하여 Windows 메시지 처리에 연결하십시오. – Lucero

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); 
    } 
} 
관련 문제