net index committed changed change asp .net vb.net winforms

.net - committed - selectedindexchanged combobox c# asp net



.NET WinForms ComboBox, elementos idénticos y el evento SelectedIndexChanged (4)

Parece que cuando tienes una aplicación WinForms .NET y un ComboBox (configurado en el estilo "DropDown"), y ese ComboBox tiene varios elementos idénticos, suceden cosas extrañas. Específicamente, el índice del elemento seleccionado puede cambiar sin activar el evento SelectedIndexChanged.

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

Aquí hay un ejemplo simple que puede usar para ver de lo que estoy hablando:

  • Cree un nuevo proyecto .NET WinForms (yo uso VB.NET, pero siéntase libre de traducir, es bastante simple).
  • Coloque un ComboBox, un botón y un TextBox (establezca MultiLine = True) en el formulario.
  • Utilice el siguiente código para cargar el ComboBox con 3 elementos idénticos e imprimir algunos mensajes de estado cuando se active el evento SelectedIndexChanged, y para ver cuál es el índice seleccionado actualmente (mediante 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 haya 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 que el evento SelectedIndexChanged se active.

Esto solo ocurre cuando los elementos en el ComboBox son idénticos. Si son diferentes, esto no sucede. (Tampoco sucede si el estilo "DropDown" de 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 caso de que alguien más tenga alguna idea sobre qué hacer aquí (o lo que 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!)


.NET Framework en realidad no realiza 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 utilizando 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 CBN_SELCHANGE el hecho de que cambió y no CBN_SELCHANGE 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, vuelve a sincronizar y selecciona la primera coincidencia para el texto en la parte de edición, que es "Juan Pérez" # 0, y cambia el índice seleccionado a 0 sin .NET siendo consciente.

Por lo tanto, básicamente es un error en .NET Framework. Desafortunadamente, no hay una solución perfecta: no puede lograr 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 ocurra 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 .


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 en el que puedo pensar que 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 múltiples 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 he pensado en lo que tiene sentido hacer lo que está haciendo, en cuyo caso puede dejar de lado mis comentarios.


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 debería mostrar:

C:/ A B A

Perfectamente bueno 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 está convirtiendo utilizando la propiedad DisplayMember, y el control está buscando 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; } } } }


Se produce un problema similar sin tener elementos idénticos si ingresa 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 intenta 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 diálogo, el texto ha cambiado sin previo aviso. Este problema no ocurre si el texto no coincide con ningún elemento.