2008-09-17 21 views
5

¿Hay alguna manera de forzar un control de vista de lista para tratar todos los clics como si se hicieran con la tecla Control?Listview Multiple Selection

Necesito replicar la funcionalidad de usar la tecla de control (seleccionar un conjunto de elementos y desactivar su estado de selección) para permitirle al usuario seleccionar fácilmente varios elementos al mismo tiempo.

Gracias de antemano.

Respuesta

4

No es el comportamiento estándar del control ListView, incluso cuando MultiSelect está establecido en verdadero.

Si desea crear su propio control personalizado que tendría que hacer lo siguiente:

  1. derivar un control desde ListView
  2. agregar un controlador para el evento "seleccionado".
  3. En "OnSelected", mantenga su propia lista de elementos seleccionados.
  4. Si el elemento recién seleccionado no está en su lista, agréguelo. Si lo es, eliminarlo.
  5. En el código, seleccione todos los elementos en su lista.

Debe ser lo suficientemente simple como para implementar y sentir como multiselección sin utilizar la tecla de control!

+0

He estado trabajando en esta línea yo mismo, pero quería comprobar y ver si había algo más fácil antes de seguir adelante con la solución. Gracias por su respuesta. – Evan

+0

Asegúrese de que la lista de elementos seleccionados almacena los "contenidos" y no el índice. Usar el índice es más fácil, pero significa que debe mantener ListBox.ItemsCollection y su lista sincronizados. ¡Con suerte, su objeto o cuerda es lo suficientemente único como para mantener esta relación! –

-2

El comportamiento de Ctrl + Click es el que implementa el navegador y tiene poco que ver con el control .NET real. El resultado que intenta obtener puede adquirirse con una gran cantidad de JavaScript adicional; la forma más sencilla sería crear un control JavaScript predeterminado que funcione de esta manera, en lugar de intentar hackear la vista de lista. ¿Sería esto deseable? En ese caso, podría investigarlo y contactarte con una solución.

+0

¡Nada que ver con el navegador, está usando C# y WinForms! –

+0

Gracias de todos modos, Javascript no se puede usar en el entorno de winforms. – Evan

0

Desplácese por ListviewItemCollection y puede establecer la propiedad Seleccionado para elementos individuales en verdadero. Creo que esto emulará la función de "selección múltiple" que está intentando reproducir. (Además, como se menciona en el comentario anterior, asegúrese de que la propiedad MultiSelect de lisetview esté establecida en verdadero.)

2

También le conviene considerar el uso de Checkboxes en la vista de lista. Es una forma obvia de comunicar el concepto de selección múltiple a su usuario promedio que puede no saber acerca de Ctrl + clic.

Desde la página de MSDN:

propiedad las casillas de verificación ofrece una manera de seleccionar varios elementos en el control ListView sin utilizar la tecla CTRL. Dependiendo de su aplicación, el uso de casillas de verificación para seleccionar elementos en lugar del método de selección múltiple estándar puede ser más fácil para el usuario. Incluso si la propiedad MultiSelect del control ListView está configurada como falsa, aún puede mostrar casillas de verificación y proporcionar múltiples capacidades de selección al usuario. Esta función puede ser útil si no desea que se seleccionen varios elementos y aún así desea permitir que el usuario elija varios elementos de la lista para realizar una operación dentro de su aplicación.

2

Aquí está la solución completa que utilicé para resolver este problema usando WndProc. Básicamente, hace una prueba de acierto cuando se hace clic en el mouse ... luego, si MutliSelect está activado, automáticamente activará/desactivará el elemento [.Selected] y no se preocupará por mantener otras listas o por jugar con la funcionalidad de ListView.

No he probado esto en todos los escenarios, ... funcionó para mí. YMMV.

public class MultiSelectNoCTRLKeyListView : ListView { 
    public MultiSelectNoCTRLKeyListView() { 

    } 

    public const int WM_LBUTTONDOWN = 0x0201; 
    protected override void WndProc(ref Message m) { 
    switch (m.Msg) { 
     case WM_LBUTTONDOWN: 
     if (!this.MultiSelect) 
      break; 

     int x = (m.LParam.ToInt32() & 0xffff); 
     int y = (m.LParam.ToInt32() >> 16) & 0xffff; 

     var hitTest = this.HitTest(x, y); 
     if (hitTest != null && hitTest.Item != null) 
      hitTest.Item.Selected = !hitTest.Item.Selected; 

     return; 
    } 

    base.WndProc(ref m); 
    } 
} 
2

Aquí hay una solución completa que es una modificación de la solución proporcionada por Matthew M. anteriormente.

Ofrece una mejora, así como un poco de funcionalidad adicional.

Mejora: - al hacer clic con el botón izquierdo en el control se enfoca el control. - botón derecho del ratón comportamiento es consistente (selección simple)

funcionalidad Agregado: - el control tiene una propiedad (MultiSelectionLimit) que le permite poner un límite en el número de elementos se pueden seleccionar a la vez.

Después de mi primera publicación, me di cuenta de un pequeño problema con el código. Al borrar selecciones múltiples, el evento ItemSelectionChanged se invoca varias veces. No pude encontrar ninguna manera de evitar esto con la herencia actual, así que en su lugar adopté una solución donde la propiedad bool SelectionsBeingCleared será verdadera hasta que todos los elementos seleccionados hayan sido deseleccionados. De esta forma, una simple llamada a esa propiedad permitirá evitar los efectos de actualización hasta que se hayan borrado todas las selecciones múltiples.

public class ListViewMultiSelect : ListView 
{ 
    public const int WM_LBUTTONDOWN = 0x0201; 
    public const int WM_RBUTTONDOWN = 0x0204; 

    private bool _selectionsBeingCleared; 
    /// <summary> 
    /// Returns a boolean indicating if multiple items are being deselected. 
    /// </summary> 
    /// <remarks> This value can be used to avoid updating through events before all deselections have been carried out.</remarks> 
    public bool SelectionsBeingCleared 
    { 
     get 
     { 
      return this._selectionsBeingCleared; 
     } 
     private set 
     { 
      this._selectionsBeingCleared = value; 
     } 
    } 
    private int _multiSelectionLimit; 
    /// <summary> 
    /// The limit to how many items that can be selected simultaneously. Set value to zero for unlimited selections. 
    /// </summary> 
    public int MultiSelectionLimit 
    { 
     get 
     { 
      return this._multiSelectionLimit; 
     } 
     set 
     { 
      this._multiSelectionLimit = Math.Max(value, 0); 
     } 
    } 

    public ListViewMultiSelect() 
    { 
     this.ItemSelectionChanged += this.multiSelectionListView_ItemSelectionChanged; 
    } 

    public ListViewMultiSelect(int selectionsLimit) 
     : this() 
    { 
     this.MultiSelectionLimit = selectionsLimit; 
    } 

    private void multiSelectionListView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) 
    { 
     if (e.IsSelected) 
     { 
      if (this.MultiSelectionLimit > 0 && this.SelectedItems.Count > this.MultiSelectionLimit) 
      { 
       this._selectionsBeingCleared = true; 

       List<ListViewItem> itemsToDeselect = this.SelectedItems.Cast<ListViewItem>().Except(new ListViewItem[] { e.Item }).ToList(); 

       foreach (ListViewItem item in itemsToDeselect.Skip(1)) { item.Selected = false; } 

       this._selectionsBeingCleared = false; 

       itemsToDeselect[0].Selected = false; 
      } 

     } 

    } 

    protected override void WndProc(ref Message m) 
    { 
     switch (m.Msg) 
     { 
      case WM_LBUTTONDOWN: 
       if (this.SelectedItems.Count == 0 || !this.MultiSelect) { break; } 

       if (this.MultiSelectionLimit > 0 && this.SelectedItems.Count > this.MultiSelectionLimit) { this.ClearSelections(); } 

       int x = (m.LParam.ToInt32() & 0xffff); 
       int y = (m.LParam.ToInt32() >> 16) & 0xffff; 

       ListViewHitTestInfo hitTest = this.HitTest(x, y); 

       if (hitTest != null && hitTest.Item != null) { hitTest.Item.Selected = !hitTest.Item.Selected; } 

       this.Focus(); 

       return; 
      case WM_RBUTTONDOWN: 
       if (this.SelectedItems.Count > 0) { this.ClearSelections(); } 
       break; 
     } 

     base.WndProc(ref m); 
    } 

    private void ClearSelections() 
    { 
     this._selectionsBeingCleared = true; 

     SelectedListViewItemCollection itemsToDeselect = this.SelectedItems; 

     foreach (ListViewItem item in itemsToDeselect.Cast<ListViewItem>().Skip(1)) { item.Selected = false; } 

     this._selectionsBeingCleared = false; 

     this.SelectedItems.Clear(); 
    } 
} 
0

Por si alguien más ha buscado y encontrado este artículo, la solución aceptada ya no es válida. (de hecho, no estoy seguro de que alguna vez lo haya sido). Para hacer lo que desea (seleccionar múltiples sin una tecla modificadora) simplemente configure el tipo de selección de vista de lista para que sea múltiple, en lugar de extendido. Múltiple selecciona un elemento después de otro cuando se hace clic, y extendido requiere que se presione primero la tecla modificadora.

+0

Esto no proporciona una respuesta a la pregunta. Una vez que tenga suficiente [reputación] (http://stackoverflow.com/help/whats-reputation) podrá [comentar cualquier publicación] (http://stackoverflow.com/help/privileges/comment); en su lugar, [brinde respuestas que no requieran aclaración del autor de la pregunta] (http://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can- i-do-instead). - [De la opinión] (/ reseña/mensajes de baja calidad/13911675) –

+0

No estoy seguro de cómo no proporciona una respuesta a la pregunta. La pregunta era "¿Hay alguna manera de forzar un control de lista de vistas para tratar todos los clics como si se hubieran realizado a través de la tecla Control?" y la respuesta que di fue "establecer que la propiedad de multiselección sea múltiple, no extendida". Cuál es una respuesta válida, correcta y probada a la pregunta original. Oh, bueno, esa es la última vez que trato de ayudar a otros que vienen a través de una vieja pregunta con una solución anterior. – kamikazi