2008-10-14 45 views
17

Tengo un cuadro combinado en una aplicación de WinForms en la que se puede seleccionar un elemento, pero no es obligatorio. Por lo tanto, necesito un primer elemento 'Vacío' para indicar que no se ha establecido ningún valor.Cómo insertar el campo 'Vacío' en ComboBox vinculado a DataTable

El cuadro combinado está enlazado a un ser DataTable de regresar de un procedimiento almacenado (Ofrezco disculpas por la notación húngara en mis controles de interfaz de usuario: p):

DataTable hierarchies = _database.GetAvailableHierarchies(cmbDataDefinition.SelectedValue.ToString()).Copy();//Calls SP 
cmbHierarchies.DataSource = hierarchies; 
cmbHierarchies.ValueMember = "guid"; 
cmbHierarchies.DisplayMember = "ObjectLogicalName"; 

¿Cómo puedo insertar un elemento, vacío?

Tengo acceso para cambiar el SP, pero realmente preferiría no 'contaminarlo' con la lógica de UI.

Actualización: Era la DataTable.NewRow() que había borrado, gracias. Les he modificado a todos (las 3 respuestas hasta ahora de todos modos). Estoy tratando de obtener el patrón Iterator funcionando antes de decidir una 'respuesta'

Actualización: Creo que esta edición me pone en la tierra del Wiki de la comunidad, he decidido no especificar una sola respuesta, ya que todos tienen mérito en el contexto de sus dominios. Gracias por su aporte colectivo.

Respuesta

23

hay dos cosas que puede hacer:

  1. Añadir una fila vacía a la DataTable que es regresó del procedimiento almacenado.

    DataRow emptyRow = hierarchies.NewRow(); 
    emptyRow["guid"] = ""; 
    emptyRow["ObjectLogicalName"] = ""; 
    hierarchies.Rows.Add(emptyRow); 
    

    Crea un DataView y ordénelo utilizando la columna ObjectLogicalName. Esto hará que la fila recién agregada sea la primera fila en DataView.

    DataView newView =   
        new DataView(hierarchies,  // source table 
        "",        // filter 
        "ObjectLogicalName",   // sort by column 
        DataViewRowState.CurrentRows); // rows with state to display 
    

    A continuación, establezca el DataView como DataSource del ComboBox.

  2. Si realmente no desea agregar una nueva fila como se mencionó anteriormente.Puede permitir que el usuario establezca el valor ComboBox en nulo simplemente manejando el evento de pulsación de tecla "Eliminar". Cuando un usuario presiona la tecla Eliminar, configure el SelectedIndex en -1. También debe establecer ComboBox.DropDownStyle en DropDownList. Como esto evitará que el usuario edite los valores en el ComboBox.

+0

Un poco tarde en el juego, pero la primera opción funcionó bien para mí. Lo único que hice diferente fue usar InsertAt, de modo que mi opción vacía ("Ninguno") aparece como el primer elemento en el cuadro combinado: hierarchies.Rows.InsertAt (emptyRow, 0); –

+0

Olvidé mencionar ... No usé DataView y lo até directamente a una tabla devuelta por un método, por lo que ya estaba ordenada correctamente. InsertAt es útil en este caso. –

3

insertar una fila en blanco en su tabla de datos, y comprobar si en la validación/actualizar/crear

+0

Mejor sería "seleccionar" una fila en blanco y UNION que con su consulta real. Eso dejaría intacta tu base de datos. – BoltBait

+0

@ [BoltBait]: salvo que ya dijo que no quería cambiar su procedimiento almacenado –

+4

Prefiero mantener los requisitos de IU fuera del DB. –

8

lo general crea un iterador para este tipo de cosas Se evita la contaminación de sus datos, y funciona bien con los datos de unión:

DataTable hierarchies = _database.GetAvailableHierarchies(cmbDataDefinition.SelectedValue.ToString()).Copy();//Calls SP 
cmbHierarchies.DataSource = GetDisplayTable(hierarchies); 
cmbHierarchies.ValueMember = "guid"; 
cmbHierarchies.DisplayMember = "ObjectLogicalName"; 

... 

private IEnumerable GetDisplayTable(DataTable tbl) 
{ 
    yield return new { ObjectLogicalName = string.Empty, guid = Guid.Empty }; 

    foreach (DataRow row in tbl.Rows) 
     yield return new { ObjectLogicalName = row["ObjectLogicalName"].ToString(), guid = (Guid)row["guid"] }; 
} 

exención de responsabilidad: No he compilado el código, pero han utilizado este patrón muchas veces.

Nota: He estado en WPF y ASP.Net terreno durante los últimos años. Aparentemente, el cuadro combinado de Winforms quiere un IList, no un IEnumerable. Una operación más costosa sería crear una lista. Este código es realmente la corriente de la concisión y realmente, realmente no han compilado:

DataTable hierarchies = _database.GetAvailableHierarchies(cmbDataDefinition.SelectedValue.ToString()).Copy(); 
List<KeyValuePair<string, Guid>> list = new List<KeyValuePair<string, Guid>>(hierarchies.Rows.Cast<DataRow>().Select(row => new KeyValuePair<string, Guid>(row["Name"].ToString(), (Guid)row["Guid"]))); 
list.Insert(0, new KeyValuePair<string,Guid>(string.Empty, Guid.Empty)); 
cmbHierarchies.DataSource = list; 
cmbHierarchies.ValueMember = "Value"; 
cmbHierarchies.DisplayMember = "Key"; 
+0

Recibo un error de "DataBinding complejo acepta como origen de datos un IList o un IListSource" y no puedo encontrar una conversión entre IEnumerable e IList (estoy en WinForms si está acostumbrado a hacerlo en WPF) – johnc

+0

I han estado en ASP.Net land por un tiempo, pero recuerden usar este patrón en WPF. No he trabajado con WinForms durante aproximadamente 3 años. Si esto quiere una lista, sugeriría una estrategia similar, simplemente cree una lista en lugar de usar un iterador. –

+0

ASP.NET, tiene sentido. Me gusta el patrón aunque – johnc

0

Er, ¿no puede simplemente agregar un elemento predeterminado al ComboBox después del enlace de datos?

+0

El establecimiento de un valor predeterminado (a través de la propiedad Texto) en el cuadro combinado se anula cuando el origen de datos está vinculado a la tabla de datos. –

+0

A continuación, agregue el valor a DataTable. – flipdoubt

0

Me gustaría vincular los datos y luego insertar un elemento en blanco en la posición 0 mediante el método ComboxBox.Items.Insert. Similar a lo que sugirió flipdoubt, pero agrega el elemento a la parte superior.

+2

Una vez que el origen de datos del cuadro combinado se une a la tabla de datos, se produce un error al intentar insertar. Específicamente: la recopilación de elementos no se puede modificar cuando se establece la propiedad Datasource. –

13
cmbHierarchies.SelectedIndex = -1; 
+4

Eso mostrará un artículo vacío hasta que haga una selección, no insertará un artículo vacío – johnc

+0

¡Genial! muchas gracias. –

1

escribí este método basado en las sugerencias aquí por Jason Jackson:

private IEnumerable<KeyValuePair<object,object>> GetDisplayTable(DataTable dataTable, DataColumn ValueMember, string sep,params DataColumn[] DisplayMembers) 
{ 
    yield return new KeyValuePair<object,object>("<ALL>",null); 

    if (DisplayMembers.Length < 1) 
     throw new ArgumentException("At least 1 DisplayMember column is required"); 

    foreach (DataRow r in dataTable.Rows) 
    { 
     StringBuilder sbDisplayMember = new StringBuilder(); 
     foreach(DataColumn col in DisplayMembers) 
     { 
      if (sbDisplayMember.Length > 0) sbDisplayMember.Append(sep); 
      sbDisplayMember.Append(r[col]); 
     } 
     yield return new KeyValuePair<object, object>(sbDisplayMember.ToString(), r[ValueMember]); 
    } 
} 

Uso:

bindingSource1.DataSource = GetDisplayTable(
      /*DataTable*/typedDataTable, 
      /*ValueMember*/typedDataTable.IDColumn, 
      /*DisplayColumn Seperator*/" - ", 
      /*List of Display Columns*/ 
      typedDataTable.DB_CODEColumn, 
      typedDataTable.DB_NAMEColumn); 

comboBox1.DataSource = bindingSource1; 
comboBox1.DisplayMember = "Key"; 
comboBox1.ValueMember = "Value"; 

//another example without multiple display data columns: 
bindingSource2.DataSource = GetDisplayTable(
      /*DataTable*/typedDataTable, 
      /*ValueMember*/typedDataTable.IDColumn, 
      /*DisplayColumn Seperator*/null, 
      /*List of Display Columns*/ 
       typedDataTable.DESCColumn); 

más abajo, donde se consume el valor seleccionado:

if (comboBox1.SelectedValue != null) 
    // Do Something with SelectedValue 
else 
    // All was selected (all is my 'empty') 

Esto permitirá muestra varias columnas concatenadas en ComboBox, mientras mantiene el miembro Value en el identificador único + usa el bloque iterador con BindingSource, BindingSource puede ser excesivo para su situación.

Comisiones y sugerencias son bienvenidas.

2

he encontrado otra solución:

Justo después de crear la tabla de datos (antes de usar relleno), añadir nueva fila y utilizar AcceptChanges método para la mesa. El nuevo registro obtendría RowState = Sin cambios, y no se agregaría a la base de datos, pero sería visible en su tabla de datos y en el cuadro combinado.

DataTable dt = new DataTable(); 
    dt.Rows.Add(); 
    dt.AcceptChanges(); 
    ... 
    dt.Fill("your query"); 
0

Tuve un desafío similar. Como parte del evento de carga de formulario fijo el SelectedIndex del control a -1

es decir

private void Form1_Load(object sender, EventArgs e) 
{   
    this.TableAdapter.Fill(this.dsListOfCampaigns.EvolveCampaignTargetListMasterInfo); 
    this.comboCampaignID.SelectedIndex = -1; 
} 

Efectivamente, el cuadro combinado se rellena y se selecciona el primer elemento. Entonces el artículo no está seleccionado. Puede no ser una solución viable para todos los casos.

0

this.recieptDateTimePicker.SelectedIndex = -1; this.dateCompoBox.SelectedIndex = -1;

0

he encontrado de esta manera:

DataTable hierarchies = new DataTable(); 

    cmbHierarchies.BeginUpdate(); 
    cmbHierarchies.ValueMember = this.Value; 
    cmbHierarchies.DisplayMember = this.Display; 
    hierarchies = DataView.ToTable(); 
    cmbHierarchies.DataSource = table; 
    cmbHierarchies.EndUpdate(); 

    //Add empty row 
    DataRow row = table.NewRow(); 
    table.Rows.InsertAt(row, 0); 
    cmbHierarchies.SelectedIndex = 0; 
0

En lugar de añadir una nueva fila a la tabla de datos, simplemente enlazar los datos a la lista desplegable y, al cargar lote de SelectedIndex a -1. Esto hará que la selección sea nula hasta que el usuario seleccione un elemento.

Recorté esto de uno de mis proyectos actuales.

 Attorney_List_CB.DataSource = DA_Attorney_List.BS.DataSource; 
     Attorney_List_CB.DisplayMember = "Attorney Name"; 
     Attorney_List_CB.SelectedIndex = -1;  

Con el fin de borrar la selección por lo general insertar un botón que establece el SelectedIndex de nuevo a -1.

private void Clear_Selection_BTN_Click(object sender, EventArgs e) 
    { 
     Attorney_List_CB.SelectedIndex = -1; // Clears user selection 
    } 

Por último, una vez que validar los datos en mi forma, si el SelectedIndex de cualquier cuadro combinado es -1, entonces se omite, o I generará algún tipo de valor predeterminado tal como "N/A" o lo que sea Necesito bajo las circunstancias.

Cuestiones relacionadas