2008-12-09 33 views
5

Parece que tiene una aplicación WinForms .NET y un ComboBox (configurado en el estilo "DropDown"), y que ComboBox tiene varios elementos que son idénticas, cosas raras suceden. Específicamente, el índice del elemento seleccionado puede cambiar sin activando el evento SelectedIndexChanged..NET WinForms ComboBox, elementos idénticos y el evento SelectedIndexChanged

Por supuesto, esto causa confusión masiva y errores extraños y oscuros, que es lo que he estado jalando últimamente.

Aquí está un ejemplo simple que puede utilizar para ver lo que estoy hablando:

  • Hacer un nuevo proyecto .NET WinForms (yo uso VB.NET, pero no dude en traducir - es bastante simple) .
  • Coloque un ComboBox, un botón y un TextBox (establezca MultiLine = True) en el formulario.
  • utilizar el siguiente código para cargar el ComboBox con 3 artículos idénticos e imprimir algunos mensajes de estado cuando se dispara el evento SelectedIndexChanged, y para ver lo que el índice seleccionado es (a través de un botón):
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged 
     TextBox1.Text = TextBox1.Text & vbNewLine & "ComboBox SelectedIndexChanged event fired." & vbNewLine & _ 
      "SelectedIndex is: " & ComboBox1.SelectedIndex 
    End Sub 

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 
     ComboBox1.Items.Add("John Doe") 
     ComboBox1.Items.Add("John Doe") 
     ComboBox1.Items.Add("John Doe") 

    End Sub 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 
     TextBox1.Text = TextBox1.Text & vbNewLine & _ 
     "Button clicked." & vbNewLine & _ 
     "SelectedIndex is: " & ComboBox1.SelectedIndex 
    End Sub

Ejecute el proyecto y seleccione un elemento del ComboBox (por ejemplo, el del medio). Luego, haga clic en la flecha desplegable de ComboBox, pero NO SELECCIONE NADA. Haga clic en el botón (Button1 por defecto) y vea lo que dice.

A menos que yo he perdido la cabeza, esto es lo que debería ver:

ComboBox SelectedIndexChanged event fired. 
SelectedIndex is: 1 
Button clicked. 
SelectedIndex is: 0

En otras palabras, el índice seleccionado ha cambiado, pero sin el evento SelectedIndexChanged disparar!

Esto solo ocurre cuando los elementos en el ComboBox son idénticos. Si son diferentes, esto no sucede. (Tampoco sucede si el estilo "DropDown" del ComboBox está establecido en "DropDownList")

Sospecho que esto puede ser un error en el framework .NET y no es algo que pueda arreglar, pero en el modo apagado posibilidad de que alguien más tenga alguna idea sobre qué hacer aquí (¡o qué podría estar haciendo mal!), ¡por favor, toque! No puedo explicar este comportamiento o evitarlo (espero que el SelectedIndex siga siendo el mismo a menos que, ¿sabes ?, ¡realmente lo CAMBIAS seleccionando algo más!)

Respuesta

17

El .NET Framework en realidad no lo hace realizar un seguimiento del índice seleccionado de la lista desplegable del cuadro combinado; esto es manejado internamente por la API de Windows. Como consecuencia de esto, .NET depende de la API de Windows para notificarlo cuando el índice seleccionado cambia por medio de un mensaje de notificación enviado al identificador de ventana del cuadro combinado, para que pueda activar el evento SelectedIndexChanged.

Desafortunadamente, resulta que el mensaje de notificación particular que .NET observa (CBN_SELCHANGE para ser exacto) NO cubre todos los escenarios posibles en los que el índice seleccionado podría cambiar. Específicamente, CBN_SELCHANGE solo es enviado por la API de Windows si el usuario hace clic en, o selecciona mediante las teclas de flecha, un elemento en la lista desplegable. Sin embargo, en un cuadro combinado de estilo DropDown, el acto de abrir el cuadro combinado hace que Windows mire el texto en la parte de edición del cuadro combinado, busque en la lista de elementos de una coincidencia y, si se encuentra una coincidencia, automáticamente seleccione el elemento correspondiente (o el primer elemento coincidente, si hay varios elementos coincidentes). Esto puede cambiar el índice seleccionado, pero NO envía un mensaje de notificación CBN_SELCHANGE, por lo que .NET omite el hecho de que cambió y no activa el evento SelectedIndexChanged.

Windows hace todo esto en un cuadro combinado de estilo DropDown porque el usuario NO TIENE que seleccionar algo en la lista desplegable; ellos pueden escribir lo que quieran. Por lo tanto, cada vez que abre el cuadro combinado asume que el usuario puede haber cambiado el texto e intenta volver a sincronizar con lo que está en la lista si puede.

En su caso, cuando abre el cuadro combinado por segunda vez, se vuelve a sincronizar y selecciona la primera coincidencia para el texto en la parte de edición, que es "John Doe" # 0, y cambia el index a 0 sin que .NET tenga en cuenta.

Por lo tanto, básicamente es un error en .NET Framework. Desafortunadamente, no hay una solución perfecta: no puede hacer que Windows no realice la resincronización, y no hay ningún evento que se desactive inmediatamente después de que se produzca la resincronización en la que puede obtener el nuevo índice seleccionado. (El evento DropDown en realidad se dispara justo antes de que se produzca la resincronización, por lo que no verá el nuevo índice). Lo mejor que puedes hacer es manejar el evento DropDownClosed, asumir que el índice puede haber cambiado en ese punto y actuar en consecuencia .

+0

¡Muchas gracias por esa respuesta! Eso fue * increíblemente * informativo! (¡También es bueno saber que no estoy loco y que hay una explicación legítima para este comportamiento!) – Keithius

+0

Acepto que fue una gran respuesta, así que voté la respuesta – MikeScott8

1

La respuesta de Eric fue muy minuciosa, pero me sorprendió ver que no terminaba con "... pero en realidad, debería preguntarse por qué está completando un cuadro combinado con elementos duplicados". Sin lugar a dudas, se ha permitido que exista el error de .Net framework porque cuando utilizas el control como se pretendía, para permitir que el usuario elija un elemento de una lista, no te topas con este error.

¿Cómo va el usuario a diferenciar entre las entradas idénticas? ¿Por qué elegirían uno sobre otro? ¿Hay un significado diferente para los diferentes artículos? Si es así, tener entradas duplicadas es ambiguo, lo que siempre es un mal diseño de usabilidad. De lo contrario, no deberías tener elementos duplicados.

El único escenario que puedo pensar donde esto podría tener sentido es cuando tiene una lista grande que consta de varios grupos de elementos relacionados, donde uno o más elementos encajan lógicamente en más de un grupo por lo que desea mostrarlo en ambas secciones.

Supongo que su diseño no tuvo en cuenta el hecho de que puede haber varias entradas idénticas y que esta omisión tendrá otras repercusiones de usabilidad que son más importantes que este problema. Por supuesto, entiendo que puede estar haciendo algo en lo que no pensé que tiene sentido hacer lo que está haciendo, en cuyo caso puede dejar de lado mis comentarios.

+0

Considere por un momento que la mayoría de los usuarios NO son programadores, y tienen no hay idea de que 2 elementos en una lista sean iguales podría ser un problema, y ​​comprenderá cómo puede surgir este problema. Además: los usuarios pueden ingresar la misma cosa dos veces por accidente, pero no lo notan al principio, lo que lleva a esta situación. – Keithius

1

Hay casos en los que tener elementos duplicados en la lista no solo es válido, sino también deseable. Considere el cuadro combinado OpenFileDialog que ve en Visual Studio cuando presiona el botón Abrir archivo. Esto muestra un cuadro combinado con elementos como "Mi PC", "Escritorio", "Mis documentos", etc. Para los nombres de las carpetas, solo el nombre corto está en la lista. La ruta completa no se muestra. Por lo tanto, es muy posible que una carpeta tenga el mismo nombre (corto) que uno de sus descendientes.

así que imagina la siguiente estructura de carpetas:

C:\ 
C:\A 
C:\A\B 
C:\A\B\A 

una estructura perfectamente válida. En mi implementación, establecí la propiedad DataSource en una lista de enlaces de objetos. El ValueMember del objeto es el nombre completo del archivo y DisplayMember es el nombre corto del archivo. El cuadro combinado debe mostrar:

C:\ 
    A 
     B 
      A 

perfectamente un buen diseño de interfaz de usuario. La sangría sugiere la anidación de las carpetas.

Pero cuando establezco SelectedValue del cuadro combinado en "C: \ A \ B \ A", se selecciona el elemento incorrecto. El elemento que debe seleccionarse es el último (4º elemento) de la lista, pero en su lugar se selecciona el 2º elemento (índice 1).Y establecer SelectedIndex = 3 no se comporta como se esperaba. Nuevamente, se selecciona el segundo elemento, no el último.

Lo que parece estar sucediendo aquí es que al establecer SelectedValue o SelectedIndex, el valor se convierte mediante la propiedad DisplayMember, y el control busca de principio a fin una coincidencia. Debería estar buscando usando la propiedad ValueMember. El código de muestra está debajo. Apreciado si alguien puede confirmar que esto es un error, o algo que he hecho mal.

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Text; 
using System.Windows.Forms; 

namespace ComboBoxTest 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
     InitializeComponent(); 
     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 
     if (DesignMode) 
      return; 

     BindingList<CBItem> items = new BindingList<CBItem>(); 
     items.Add(new CBItem("A", @"C:\A")); 
     items.Add(new CBItem("B", @"C:\A\B")); 
     items.Add(new CBItem("A", @"C:\A\B\A")); 

     comboBox.DisplayMember = "DisplayValue"; 
     comboBox.ValueMember = "RealValue"; 
     comboBox.DataSource = items; 

     comboBox.SelectedValue = @"C:\A\B\A"; 
     } 
    } 

    class CBItem 
    { 
     public CBItem(string displayValue, string realValue) 
     { 
     _displayValue = displayValue; 
     _realValue = realValue; 
     } 

     private readonly string _displayValue, _realValue; 

     public string DisplayValue { get { return _displayValue; } } 
     public string RealValue { get { return _realValue; } } 
    } 
} 
0

Se produce un problema similar sin tener elementos idénticos si introduce texto libre, que no coincide exactamente con los primeros caracteres. Si el usuario no abre el menú desplegable, no se produce la resincronización y el índice seleccionado es -1 como se esperaba. (No seleccionar uno de los elementos es lo que el usuario tiene la intención de hacer) Ahora el usuario cierra el cuadro de diálogo y lo vuelve a abrir. Usted, como programador, restaura el cuadro combinado con el texto que el usuario ingresó y el texto se completa automáticamente con la coincidencia parcial del elemento sin activar el evento. Si el usuario cierra el cuadro de diálogo, el texto ha cambiado sin previo aviso. Este problema no ocurre si el texto no coincide con ningún elemento.

Cuestiones relacionadas