Para explicar este problema, pongo todo lo que necesita en una pequeña aplicación de muestra que con suerte explica el problema. Realmente traté de introducir todo lo menos posible, pero en mi aplicación real, estos diferentes actores no se conocen entre sí y tampoco deberían hacerlo. Entonces, una respuesta simple como "tomar la variable unas líneas arriba y llamar a Invoke on it" no funcionaría.Excepciones BindingSource y Cross-Thread
Comencemos con el código y luego un poco más de explicación. Al principio hay una clase simple que implementa INotifyPropertyChanged:
public class MyData : INotifyPropertyChanged
{
private string _MyText;
public MyData()
{
_MyText = "Initial";
}
public string MyText
{
get { return _MyText; }
set
{
_MyText = value;
PropertyChanged(this, new PropertyChangedEventArgs("MyText"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Así que nada de especial. Y aquí el código de ejemplo, que simplemente se pueden poner en cualquier proyecto de aplicación de consola vacía:
static void Main(string[] args)
{
// Initialize the data and bindingSource
var myData = new MyData();
var bindingSource = new BindingSource();
bindingSource.DataSource = myData;
// Initialize the form and the controls of it ...
var form = new Form();
// ... the TextBox including data bind to it
var textBox = new TextBox();
textBox.DataBindings.Add("Text", bindingSource, "MyText");
textBox.DataBindings.DefaultDataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
textBox.Dock = DockStyle.Top;
form.Controls.Add(textBox);
// ... the button and what happens on a click
var button = new Button();
button.Text = "Click me";
button.Dock = DockStyle.Top;
form.Controls.Add(button);
button.Click += (_, __) =>
{
// Create another thread that does something with the data object
var worker = new BackgroundWorker();
worker.RunWorkerCompleted += (___, ____) => button.Enabled = true;
worker.DoWork += (___, _____) =>
{
for (int i = 0; i < 10; i++)
{
// This leads to a cross-thread exception
// but all i'm doing is simply act on a property in
// my data and i can't see here that any gui is involved.
myData.MyText = "Try " + i;
}
};
button.Enabled = false;
worker.RunWorkerAsync();
};
form.ShowDialog();
}
Si desea ejecutar este código se podrían obtener una excepción de hilo cruzado al tratar de cambiar la propiedad MyText
. Esto viene, porque el objeto MyData
llama al PropertyChanged
, que quedará atrapado por el BindindSource
. Esto entonces, de acuerdo con Binding
, intenta actualizar la propiedad Text
del TextBox
. Lo que claramente conduce a la excepción.
Mi mayor problema aquí viene del hecho de que el objeto MyData
no sabe nada acerca de una interfaz gráfica de usuario (la causa es un simple objeto de datos). Además, el hilo de trabajo no sabe nada sobre una GUI. Simplemente actúa sobre un conjunto de objetos de datos y los manipula.
En mi humilde opinión, creo que el BindingSource
debe comprobar en qué hilo está viviendo el objeto receptor y hacer un apropiado Invoke()
para obtener el valor de ellos. Desafortunadamente esto no está incorporado (¿o estoy equivocado?), Entonces mi pregunta es:
Cómo se puede resolver esta excepción entre hilos si el objeto de datos o el hilo de trabajo no saben nada sobre una fuente de enlace que está escuchando para sus eventos para insertar los datos en una interfaz gráfica de usuario.
Pero el problema es que no sé el hilo de interfaz de usuario dentro de la clase 'MyData'. Entonces, ¿cómo invocar el hilo de UI si no tengo acceso a ningún control que esté actualmente en el formulario para invocar 'Invoke()' en él? – Oliver
@Oliver: oye, ¿has resuelto este problema? también estoy atrapado con algo como esto? –
@mahesh: Agregue una respuesta propia a esta pregunta, sobre cómo he resuelto esto. No es perfecto, pero es mejor que nada. – Oliver