2008-09-14 9 views
43

Estoy haciendo un ejemplo para alguien que aún no se ha dado cuenta de que los controles como ListBox no tienen que contener cadenas; había estado almacenando cadenas formateadas y saltando a través de aros de análisis complicados para recuperar los datos del ListBox y me gustaría mostrarle que hay una mejor manera.¿Cómo hago que un ListBox actualice el texto de su elemento?

Me di cuenta de que si tengo un objeto almacenado en el ListBox, entonces actualizo un valor que afecta a ToString, el ListBox no se actualiza. Intenté llamar al Refresh y Update en el control, pero ninguno de los dos funciona. Aquí está el código del ejemplo que estoy usando, se le requiere para arrastrar un cuadro de lista y un botón en el formulario:

Public Class Form1 

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs) 
     MyBase.OnLoad(e) 

     For i As Integer = 1 To 3 
      Dim tempInfo As New NumberInfo() 
      tempInfo.Count = i 
      tempInfo.Number = i * 100 
      ListBox1.Items.Add(tempInfo) 
     Next 
    End Sub 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 
     For Each objItem As Object In ListBox1.Items 
      Dim info As NumberInfo = DirectCast(objItem, NumberInfo) 
      info.Count += 1 
     Next 
    End Sub 
End Class 

Public Class NumberInfo 

    Public Count As Integer 
    Public Number As Integer 

    Public Overrides Function ToString() As String 
     Return String.Format("{0}, {1}", Count, Number) 
    End Function 
End Class

pensé que tal vez el problema estaba usando campos y trató de implementar INotifyPropertyChanged, pero esto no tuvo efecto. (La razón por la que estoy usando campos es porque es un ejemplo y no tengo ganas de agregar algunas docenas de líneas que no tienen nada que ver con el tema que estoy demostrando)

Honestamente, nunca he intentado actualizar artículos en su lugar como este antes; en el pasado, siempre he estado agregando/eliminando elementos, no editándolos. Entonces nunca me di cuenta de que no sé cómo hacer que esto funcione.

Entonces, ¿qué es lo que me falta?

Respuesta

24

BindingList se encarga de actualizar los enlaces por sí mismo.

using System; 
using System.ComponentModel; 
using System.Windows.Forms; 

namespace TestBindingList 
{ 
    public class Employee 
    { 
     public string Name { get; set; } 
     public int Id { get; set; } 
    } 

    public partial class Form1 : Form 
    { 
     private BindingList<Employee> _employees; 

     private ListBox lstEmployees; 
     private TextBox txtId; 
     private TextBox txtName; 
     private Button btnRemove; 

     public Form1() 
     { 
      InitializeComponent(); 

      FlowLayoutPanel layout = new FlowLayoutPanel(); 
      layout.Dock = DockStyle.Fill; 
      Controls.Add(layout); 

      lstEmployees = new ListBox(); 
      layout.Controls.Add(lstEmployees); 

      txtId = new TextBox(); 
      layout.Controls.Add(txtId); 

      txtName = new TextBox(); 
      layout.Controls.Add(txtName); 

      btnRemove = new Button(); 
      btnRemove.Click += btnRemove_Click; 
      btnRemove.Text = "Remove"; 
      layout.Controls.Add(btnRemove); 

      Load+=new EventHandler(Form1_Load); 
     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 
      _employees = new BindingList<Employee>(); 
      for (int i = 0; i < 10; i++) 
      { 
       _employees.Add(new Employee() { Id = i, Name = "Employee " + i.ToString() }); 
      } 

      lstEmployees.DisplayMember = "Name"; 
      lstEmployees.DataSource = _employees; 

      txtId.DataBindings.Add("Text", _employees, "Id"); 
      txtName.DataBindings.Add("Text", _employees, "Name"); 
     } 

     private void btnRemove_Click(object sender, EventArgs e) 
     { 
      Employee selectedEmployee = (Employee)lstEmployees.SelectedItem; 
      if (selectedEmployee != null) 
      { 
       _employees.Remove(selectedEmployee); 
      } 
     } 
    } 
} 
+0

Esto es en realidad menos trabajo que la respuesta actualmente aceptada. ¡Magnífico! Edité tu publicación para incluir un ejemplo. – OwenP

+1

En realidad podrías mejorarlo más, creo. Puede aplicar enlaces padre e hijo a controles, lo que significa que podría prescindir del controlador de eventos _SelectedIndexChanged. Sin embargo, olvidé el código exacto ..... :( – Quibblesome

+0

He actualizado el ejemplo, quitando el controlador de eventos SelectedIndexChanged y reemplazando con 2 nuevas líneas en el controlador de carga. :) – Quibblesome

9

Utilice la propiedad de origen de datos y un objeto BindingSource entre el origen de datos y la propiedad del origen de datos del cuadro de lista. Luego actualiza eso.

actualización ejemplo agregado.

así:

Public Class Form1 

    Private datasource As New List(Of NumberInfo) 
    Private bindingSource As New BindingSource 

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs) 
     MyBase.OnLoad(e) 

     For i As Integer = 1 To 3 
      Dim tempInfo As New NumberInfo() 
      tempInfo.Count = i 
      tempInfo.Number = i * 100 
      datasource.Add(tempInfo) 
     Next 
     bindingSource.DataSource = datasource 
     ListBox1.DataSource = bindingSource 
    End Sub 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 
     For Each objItem As Object In datasource 
      Dim info As NumberInfo = DirectCast(objItem, NumberInfo) 
      info.Count += 1 
     Next 
     bindingSource.ResetBindings(False) 
    End Sub 
End Class 

Public Class NumberInfo 

    Public Count As Integer 
    Public Number As Integer 

    Public Overrides Function ToString() As String 
     Return String.Format("{0}, {1}", Count, Number) 
    End Function 
End Class 
+0

excelente. Por alguna razón, el enlace de datos en WinForms nunca salta a la vista como una solución sin importar cuánto la use en WPF. – OwenP

+0

Heh solía ser aún más divertido que esto. Algo así como: ((CurrencyManager) this.BindingContext [ListBox1]). Refresh(); Obteniendo un objeto "oculto" fuera del BindingContext y luego transfiriéndolo a un administrador de divisas. Aunque este es el C# como nunca lo hice en VB.NET. – Quibblesome

+0

Esta es una buena respuesta, pero en última instancia, la sugerencia de geno de utilizar BindingList lleva a menos trabajo. – OwenP

-1

No sé mucho acerca de vb.net pero en C# se debe utilizar fuente de datos y luego se unen mediante una llamada listbox.bind() haría el truco.

+2

Eso es para las internets. Me parece que esta persona está interesada en WinForm. – Quibblesome

1

Si deriva de ListBox existe el método RefreshItem protected al que puede llamar. Solo vuelve a exponer este método en tu propio tipo.

public class ListBox2 : ListBox { 
    public void RefreshItem2(int index) { 
     RefreshItem(index); 
    } 
} 

A continuación, cambie el archivo de diseñador para usar su propio tipo (en este caso, ListBox2).

32

Uso esta clase cuando necesito tener un cuadro de lista que se actualice.

Actualice el objeto en la lista y luego llame a cualquiera de los métodos incluidos, dependiendo de si tiene el índice disponible o no. Si está actualizando un objeto que está contenido en la lista, pero no tiene el índice, deberá llamar a RefreshItems y actualizar todos los elementos.

public class RefreshingListBox : ListBox 
{ 
    public new void RefreshItem(int index) 
    { 
     base.RefreshItem(index); 
    } 

    public new void RefreshItems() 
    { 
     base.RefreshItems(); 
    } 
} 
+0

Nota: 'RefreshItem' solo funciona si la propiedad' DisplayMember' está configurada. –

0

Es un poco poco profesional, pero funciona. Acabo de eliminar y agregué el artículo (también lo seleccioné de nuevo). La lista se ordenó de acuerdo con la propiedad "Mostrar y cambiar", así que, una vez más, estaba bien para mí. El efecto secundario es que se genera un evento adicional (índice cambiado).

if (objLstTypes.SelectedItem != null) 
{ 
PublisherTypeDescriptor objType = (PublisherTypeDescriptor)objLstTypes.SelectedItem; 
objLstTypes.Items.Remove(objType); 
objLstTypes.Items.Add(objType); 
objLstTypes.SelectedItem = objType; 
} 
+0

¿por qué votar? – nawfal

+0

¡Esto siempre colocará el elemento seleccionado al final de ListBox! –

-3

Si objLstTypes es su nombre ListBox Uso objLstTypes.Items.Refrescar(); Espero que esto funcione ...

+0

No hay método de actualización. –

29
lstBox.Items[lstBox.SelectedIndex] = lstBox.SelectedItem; 
+5

¡esta es una respuesta válida de hecho! – nawfal

+3

¡Éste tiene mi voto! Simple, se puede hacer sin agregar nuevas clases o párrafos de código. Esto era todo lo que necesitaba. – Rob

+1

Fantástico: simple y hace el trabajo, ¡gracias! –

16
typeof(ListBox).InvokeMember("RefreshItems", 
    BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, 
    null, myListBox, new object[] { }); 
+0

Lo encontré útil: se puede usar para hacer que el cuadro de lista se actualice de forma dinámica –

0

Si se utiliza un método de dibujar como:

private void listBox1_DrawItem(object sender, DrawItemEventArgs e) 
{ 
    e.DrawBackground(); 
    e.DrawFocusRectangle(); 

    Sensor toBeDrawn = (listBox1.Items[e.Index] as Sensor); 
    e.Graphics.FillRectangle(new SolidBrush(toBeDrawn.ItemColor), e.Bounds); 
    e.Graphics.DrawString(toBeDrawn.sensorName, new Font(FontFamily.GenericSansSerif, 14, FontStyle.Bold), new SolidBrush(Color.White),e.Bounds); 
} 

sensor es mi clase.

Así que si cambio de la clase Color algún lugar, sólo tiene que actualizar como:

int temp = listBoxName.SelectedIndex; 
listBoxName.SelectedIndex = -1; 
listBoxName.SelectedIndex = temp; 

Y el Color actualizará, sólo otra solución :)

Cuestiones relacionadas