example - fill datagridview c# with datatable
BindingList<> evento ListChanged (2)
Tengo un BindingList <> de un conjunto de clases en la propiedad DataSource de un BindingSource, que a su vez se establece en la propiedad DataSource de un DataGridView.
1. Entiendo que cualquier adición a la lista desencadenará un evento ListChanged que se propagará a través de BindingSource y luego a DataGridView, que se actualizará para mostrar el cambio. Esto sucederá porque los eventos se han conectado automáticamente. (¿Sí?)
Todo está bien cuando todo el trabajo se realiza en el subproceso de la interfaz de usuario, pero cuando la lista se crea y cambia de un subproceso que no es UI, en última instancia, se produce una excepción cruzada cuando se actualiza la cuadrícula. Puedo entender por qué sucede esto, pero no cómo solucionarlo ...
2. Lo que me cuesta entender, ¿dónde debo interceptar mejor el evento ListChanged para tratar de ordenar cosas en el hilo de la interfaz de usuario? Supongo que necesito una referencia a la secuencia de comandos de interfaz de usuario de alguna manera para ayudar a hacer esto?
He leído muchos artículos / publicaciones sobre esto, pero estoy luchando porque no entiendo completamente los mecanismos en acción aquí.
Nunca cambiaré ningún elemento una vez que esté en la lista, solo los agregaré y, en primer lugar, borraré la lista.
(Estoy usando .NET 2.0)
Esa vista es bastante justa. Debajo de las cubiertas, otros objetos como CurrencyManager y Binding aseguran que los controles se actualicen cuando la fuente de datos subyacente cambie.
Agregar un elemento a un enlace de datos vinculados BindingList desencadena una serie de eventos que terminan tratando de actualizar el DataGridView. Dado que la interfaz de usuario solo se puede actualizar desde el subproceso de interfaz de usuario, debe agregar elementos a BindingList desde el subproceso de la interfaz de usuario a través de Control.Invocar .
Reuní una muestra rápida creando un Formulario con un DataGridView, un BindingSource y un botón.
El botón crea otro hilo que simula obtener un nuevo elemento para incluirlo en BindingList.
La inclusión en sí misma se realiza nuevamente en el hilo de la interfaz de usuario a través de Control.Invocar.
public partial class BindingListChangedForm : Form {
BindingList<Person> people = new BindingList<Person>();
Action<Person> personAdder;
public BindingListChangedForm() {
InitializeComponent();
this.dataGridView1.AutoGenerateColumns = true;
this.bindingSource1.DataSource = this.people;
this.personAdder = this.PersonAdder;
}
private void button1_Click(object sender, EventArgs e) {
Thread t = new Thread(this.GotANewPersononBackgroundThread);
t.Start();
}
// runs on the background thread.
private void GotANewPersononBackgroundThread() {
Person person = new Person { Id = 1, Name = "Foo" };
//Invokes the delegate on the UI thread.
this.Invoke(this.personAdder, person);
}
//Called on the UI thread.
void PersonAdder(Person person) {
this.people.Add(person);
}
}
public class Person {
public int Id { get; set; }
public string Name { get; set; }
}
Puede ampliar BindingList para usar ISynchronizeInvoke (implementado por System.Windows.Forms.Control) para ordenar las invociones de eventos en el subproceso de la interfaz de usuario.
Entonces, todo lo que necesita hacer es usar el nuevo tipo de lista y todo está ordenado.
public partial class Form1 : System.Windows.Forms.Form {
SyncList<object> _List;
public Form1() {
InitializeComponent();
_List = new SyncList<object>(this);
}
}
public class SyncList<T> : System.ComponentModel.BindingList<T> {
private System.ComponentModel.ISynchronizeInvoke _SyncObject;
private System.Action<System.ComponentModel.ListChangedEventArgs> _FireEventAction;
public SyncList() : this(null) {
}
public SyncList(System.ComponentModel.ISynchronizeInvoke syncObject) {
_SyncObject = syncObject;
_FireEventAction = FireEvent;
}
protected override void OnListChanged(System.ComponentModel.ListChangedEventArgs args) {
if(_SyncObject == null) {
FireEvent(args);
}
else {
_SyncObject.Invoke(_FireEventAction, new object[] {args});
}
}
private void FireEvent(System.ComponentModel.ListChangedEventArgs args) {
base.OnListChanged(args);
}
}