System.Windows.Threading.Dispatcher y WinForms?
multithreading begininvoke (7)
A veces, un componente de temporizador es útil y fácil de configurar en WinForms, simplemente establece su intervalo y luego habilítalo, luego asegúrate de que lo primero que haces en su controlador de eventos Tick es deshabilitarse.
Creo que Timer ejecuta el código en su propio hilo, por lo que puede necesitar hacer un BeginInvoke (llamado al objeto WinForm [this]) para ejecutar su Acción.
private WebBrowserDocumentCompletedEventHandler handler; //need to make it a class field for the handler below (anonymous delegates seem to capture state at point of definition, so they can''t capture their own reference)
private string imageFilename;
private bool exit;
public void CaptureScreenshot(Uri address = null, string imageFilename = null, int msecDelay = 0, bool exit = false)
{
handler = (s, e) =>
{
webBrowser.DocumentCompleted -= handler; //must do first
this.imageFilename = imageFilename;
this.exit = exit;
timerScreenshot.Interval = (msecDelay > 0)? msecDelay : 1;
timerScreenshot.Enabled = true;
};
webBrowser.DocumentCompleted += handler;
Go(address); //if address == null, will use URL from UI
}
private void timerScreenshot_Tick(object sender, EventArgs e)
{
timerScreenshot.Enabled = false; //must do first
BeginInvoke((Action)(() => //Invoke at UI thread
{ //run in UI thread
BringToFront();
Bitmap bitmap = webBrowser.GetScreenshot();
if (imageFilename == null)
imageFilename = bitmap.ShowSaveFileDialog();
if (imageFilename != null)
{
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(imageFilename))); //create any parent directories needed
bitmap.Save(imageFilename);
}
bitmap.Dispose(); //release bitmap resources
if (exit)
Close(); //this should close the app, since this is the main form
}), null);
}
puede ver lo anterior en acción en la herramienta WebCapture ( http://gallery.clipflair.net/WebCapture , código fuente en: http://ClipFlair.codeplex.com , consulte la carpeta Herramientas / WebCapture) que capta capturas de pantalla de sitios web. Por cierto, si desea llamar al ejecutable desde la línea de comandos, asegúrese de ir a Propiedades del proyecto y en la pestaña Seguridad, desactivar la seguridad ClickOnce (de lo contrario, no podrá acceder a la línea de comandos)
¿Un System.Windows.Threading.Dispatcher
funciona en el subproceso UI de una aplicación WinForms
?
¿Si es así por qué? Viene de WindowsBase.dll que parece ser un componente de WPF
.
Si no, ¿cómo puedo invocar las unidades de trabajo de nuevo en el hilo de la interfaz de usuario? He encontrado Control.BeginInvoke()
, pero parece torpe crear un control solo para hacer referencia al hilo de origen.
Dispatcher es un componente de WPF, no un componente de WinForms.
Si desea despachar elementos de trabajo en el hilo de la interfaz de usuario, entonces deberá usar Control.BeginInvoke como ya ha encontrado o reaccionar a ResetEvents / WaitObjects en los hilos.
Por lo general, invocar elementos de trabajo en el hilo de la interfaz de usuario es algo malo a menos que sea una pieza de trabajo UI (es decir, actualizar el contenido de un control o algo así) en cuyo caso el control.BeginInvoke () sería suficiente.
Eche un vistazo a los backgrounder y vea si se ajustan a sus necesidades.
Proporcioné un ejemplo del uso de System.Windows.Threading.Dispatcher
en Windows Form en mi respuesta a la pregunta "Programación paralela usando TPL en WinForms" desde la respuesta anterior a su pregunta :
Si está seguro de estar en el hilo de UI (por ejemplo, en un botón. Haga clic en el controlador), Dispatcher.CurrentDispatcher le proporciona el despachador de subprocesos de interfaz de usuario que puede utilizar para enviar desde el hilo de fondo al hilo de la interfaz de usuario como de costumbre.
es engañoso o confuso o carece del contexto de uso concreto:
-
button.Click
controlador debutton.Click
no garantiza que esté en el hilo de UI; - si no está en el hilo de la interfaz de usuario, aún es posible usar el disparcher de la interfaz de usuario del formulario WinForms
Uno puede obtener el despachador del hilo de la interfaz de usuario de WinForm:
Dispatcher dispatcherUI = Dispatcher.CurrentDispatcher;
en cualquiera de los botones haga clic en el controlador de eventos o en cualquier otro lugar (en el constructor de formularios)
Y luego usarlo para ejecutar en la interfaz de usuario de otros hilos, ver más detalles en el ejemplo a continuación en mi respuesta :
private void button1_Click(object sender, EventArgs e)
{
Dispatcher dispUI = Dispatcher.CurrentDispatcher;
for (int i = 2; i < 20; i++)
{
int j = i;
var t = Task.Factory.StartNew
(() =>
{
var result = SumRootN(j);
dispUI.BeginInvoke
(new Action
(() => richTextBox1.Text += "root " + j.ToString()
+ " " + result.ToString() + Environment.NewLine
)
, null
);
}
);
}
Puede usar Dispatcher
incluso en una aplicación WinForms.
Si está seguro de estar en un subproceso de interfaz de usuario (por ejemplo, en un botón. Haga clic en el controlador), Dispatcher.CurrentDispatcher
le proporciona el despachador de subprocesos de interfaz de usuario que luego puede usar para enviar desde subprocesos de fondo al subproceso de interfaz de usuario como de costumbre.
Tuve un problema similar con la clase de dependencia de Oracle que se ejecuta en su propio subproceso dentro de Winforms,
Cuando el evento OnChange se desencadenó de Oracle Dependency, quería mostrar los cambios en DataGridView simplemente configurando DataSource en eventargs.Details (que es esencialmente una DataTable), y arroja: System.InvalidOperationException no fue manejado por el código de usuario Message = Cross-thread operation no válido: controle ''dataGridView1'' al que se accede desde un hilo que no sea el hilo en el que se creó.
El usuario de Brian Peiris ([email protected]), un colega mío, me mostró de esta manera:
void dep_OnChange(object sender, OracleNotificationEventArgs arg)
{
Console.WriteLine("Notification received");
int infoSum = int.Parse(arg.Details.Compute("Sum(Info)", "Info is not null").ToString());
InfoSum x = (InfoSum)infoSum;
foreach (DataRow dr in arg.Details.Rows)
{
Console.WriteLine(string.Format("Operation(InfoSum)= {0}", Enum.GetName(typeof(InfoSum), x)));
Console.WriteLine(string.Format("ontable={0} Rowid={1},info={2}", dr.Field<string>("ResourceName"), dr.Field<string>("rowid"), dr.Field<Int32>("info")));
}
// Following will throw cross-thread
// dataGridView1.DataSource = arg.Details;
// instead of line above use the following
dataGridView1.BeginInvoke((Action)(()=>dataGridView1.DataSource = arg.Details));
IsNotified = true;
}
}
Use hilo de trabajo en segundo plano ya que es consciente de la existencia de mensaje de UI. Este artículo de MSDN aunque principalmente sobre WPF indica que el BWT es consciente de la UI incluso para formularios de Windows.