when tutorial method how example event delegate declarar create c# delegates invoke handle runtime-error

tutorial - Error de compilación de C#: "Invoke o BeginInvoke no pueden invocarse en un control hasta que se haya creado el identificador de ventana".



how to invoke a delegate method c# (9)

Encontré que InvokeRequired no es confiable, así que simplemente uso

if (!this.IsHandleCreated) { this.CreateHandle(); }

Acabo de publicar una pregunta sobre cómo hacer que un delegado actualice un cuadro de texto en otro formulario. Justo cuando pensaba que tenía la respuesta usando Invoke ... esto sucede. Aquí está mi código:

Código de formulario principal:

using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.IO; using System.Data.OleDb; using System.Collections.Specialized; using System.Text; using System.Threading; delegate void logAdd(string message); namespace LCR_ShepherdStaffupdater_1._0 { public partial class Main : Form { public Main() { InitializeComponent(); } public void add(string message) { this.Log.Items.Add(message); } public void logAdd(string message) { /////////////////////////// COMPILER ERROR BELOW /////////// this.Invoke(new logAdd(add), new object[] { message }); // Compile error occurs here }////////////////////////////// COMPILER ERROR ABOVE /////////// private void exitProgramToolStripMenuItem_Click(object sender, EventArgs e) { Application.Exit(); } private void aboutToolStripMenuItem1_Click(object sender, EventArgs e) { Form aboutBox = new AboutBox1(); aboutBox.ShowDialog(); } private void settingsToolStripMenuItem_Click(object sender, EventArgs e) { } private void settingsToolStripMenuItem1_Click(object sender, EventArgs e) { settingsForm.settings.ShowDialog(); } private void synchronize_Click(object sender, EventArgs e) { string message = "Here my message is"; // changed this ErrorLogging.updateLog(message); // changed this } } public class settingsForm { public static Form settings = new Settings(); } }

Código de clase de registro:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LCR_ShepherdStaffupdater_1._0 { public class Logging { static Main mainClass = new Main(); static logAdd logAddDelegate; public static void updateLog(string message) { logAddDelegate = mainClass.logAdd; logAddDelegate(message); } } }

  • Error de compilación:

    InvalidOperationException no se ha controlado: Invoke o BeginInvoke no se pueden invocar en un control hasta que se haya creado el identificador de ventana.

Ya traté de crear un identificador en el elemento de registro ... pero eso no funcionó. El problema es que no tengo NADA de lo que estoy haciendo y he buscado en Google de forma exhaustiva solo para encontrar respuestas vagas.

Dígame cómo crear el identificador antes de invocar a este delegado. Mientras lo hace, deme algunas formas en que puedo hacer que este código sea más simple. Por ejemplo, no quiero dos funciones de Agregar ... Tuve que hacer eso porque no había forma de encontrar un elemento para invocar desde la clase de Registro. ¿Hay una mejor manera de lograr lo que tengo que hacer?

¡¡¡Gracias!!!

EDITAR:

Mi proyecto es bastante grande, pero estos son los únicos elementos que causan este problema específico.

El registro es mi RichTextBox1 (Log.Items.Add (mensaje)) Lo renombré a Log para que sea más fácil volver a escribir.

Estoy llamando a updateLog (mensaje) desde un formulario diferente ... déjame actualizar eso aquí (aunque no hace ninguna diferencia dónde llamo updateLog (mensaje) aún me da este error)

Ustedes van a tener que hacer las cosas más simples para mí ... y dar ejemplos. No entiendo MITAD de todo lo que ustedes están diciendo aquí ... No tengo ni idea de cómo trabajar con Invocación de métodos y Controles. También investigué la mierda ...

SEGUNDA EDICION

Creo que he localizado el problema, pero no sé cómo solucionarlo.

En mi clase de registro utilizo este código para crear mainClass:

estática Main mainClass = new Main ();

Estoy creando una réplica de planos totalmente nueva para Main (), incluido Log (el richtextbox que estoy tratando de actualizar)

Cuando llamo a updateLog (mensaje) creo que estoy tratando de actualizar el registro (richtextbox) en la segunda entidad de Main () también conocida como mainClass. Por supuesto, hacerlo me arrojará esta excepción porque ni siquiera he visto esa réplica del Main actual que estoy usando.

Esto es lo que estoy buscando, gracias a una de las personas que dieron una respuesta:

Main mainClass = Application.OpenForms.OfType<Main>().First(); logAddDelegate = mainClass.logAdd; logAddDelegate(message);

Necesito crear mainClass no con el operador new () porque no quiero crear un nuevo modelo del formulario. Quiero poder editar el formulario actual.

El código anterior no funciona, no puedo encontrar la Aplicación. ¿Es eso incluso la sintaxis de C #?

Si puedo hacer que funcione el código anterior, creo que puedo resolver mi problema y finalmente descansar este problema después de un par de HORAS de búsqueda de respuestas.

EDICION FINAL:

Lo descubrí gracias a uno de los usuarios a continuación. Aquí está mi código actualizado:

Código de formulario principal:

using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.IO; using System.Data.OleDb; using System.Collections.Specialized; using System.Text; using System.Threading; delegate void logAdd(string message); namespace LCR_ShepherdStaffupdater_1._0 { public partial class Main : Form { private static Main mainFormForLogging; public static Main MainFormForLogging { get { return mainFormForLogging; } } public Main() { InitializeComponent(); if (mainFormForLogging == null) { mainFormForLogging = this; } } public void add(string message) { this.Log.Items.Add(message); } public void logAdd(string message) { this.Log.BeginInvoke(new logAdd(add), new object[] { message }); } private void exitProgramToolStripMenuItem_Click(object sender, EventArgs e) { Application.Exit(); } private void aboutToolStripMenuItem1_Click(object sender, EventArgs e) { Form aboutBox = new AboutBox1(); aboutBox.ShowDialog(); } private void settingsToolStripMenuItem_Click(object sender, EventArgs e) { } private void settingsToolStripMenuItem1_Click(object sender, EventArgs e) { settingsForm.settings.ShowDialog(); } private void synchronize_Click(object sender, EventArgs e) { add("test"); Logging.updateLog("testthisone"); //DatabaseHandling.createDataSet(); } } public class settingsForm { public static Form settings = new Settings(); } }

Código de clase de registro:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LCR_ShepherdStaffupdater_1._0 { public class Logging { static Main mainClass = Main.MainFormForLogging; static logAdd logAddDelegate; public static void updateLog(string message) { logAddDelegate = mainClass.logAdd; logAddDelegate(message); } } }


logAddDelegate (mensaje);

Creo que llama esto antes de que se haya producido el evento Form_Load. Repare su código para esperar a que se cargue el formulario antes de llamar a logAddDelegate (...) es decir antes de llamar a Logging.updateLog ()


¿Es este tu código exacto? Usted está llamando this.Log.Items.Add(message); en su método de agregar (cadena), pero su clase de registro se llama Registro, no Log. ¿Tienes otro formulario llamado Log quizás? Si ese formulario no se creó cuando llamas al método add, obtendrás esta excepción.


Bien, voy a comenzar de nuevo.

Para comprender lo que está sucediendo, necesita comprender cómo .NET y Windows se relacionan entre sí. .NET se ejecuta en Windows y envuelve muchos de los conceptos nativos de Win32, como una ventana, una vista de lista, un cuadro de edición (el nombre de Win32 para un cuadro de texto estándar). Esto significa que puede tener una instancia .NET válida de un TextBox o un Formulario, pero aún no tiene la versión subyacente de Windows de ese elemento (EditBox o Window). Cuando HandleCreated es verdadero, se crea la versión de Windows del elemento.

Su problema está ocurriendo porque algo está llevando al método logAdd a llamar antes de que se haya creado la ventana del formulario. Esto significa que en algún lugar durante su inicio después de que se haya instanciado la instancia de Formulario, pero antes de que se haya creado el identificador de Ventana, algo está intentando llamar a logAdd. Si agrega un punto de interrupción a logAdd, debería poder ver qué está haciendo esa llamada. Lo que encontrará es que la llamada se realiza en la instancia principal que crea en la clase de registrador y NO en la instancia principal que se está ejecutando. Como la instancia del registrador nunca se muestra, el identificador de la ventana no se crea, y entonces obtiene su error.

La forma general en que se ejecuta una aplicación es llamar a Application.Run (nuevo Main ()) en su método de inicio, que generalmente está en la clase Program y se llama Main. Necesita que su registrador apunte a esta instancia de main.

Hay varias formas de obtener la instancia del formulario, cada una con sus propias advertencias, pero para simplificar, podría exponer la instancia fuera de la clase principal. Por ejemplo:

public partial class Main : Form { private static Main mainFormForLogging; public static Main MainFormForLogging { get { return mainFormForLogging; } } public Main() { InitializeComponent(); if (mainFormForLogging == null) { mainFormForLogging = this; } } protected void Dispose(bool disposing) { if (disposing) { if (this == mainFormForLogging) { mainFormForLogging = null; } } base.Dispose(disposing); } }


Cuando obtiene este error, casi siempre significa que ha intentado actuar sobre un control o formulario antes de que realmente se haya creado.

En WinForms, los elementos GUI tienen dos vidas semi-independientes: como clases en la memoria y como entidades en el sistema operativo. Como tal, es posible hacer referencia a un control en .net que aún no se ha creado. El "identificador que se está creando" se refiere a tener un número asignado al control por el sistema operativo para permitir que los programas manipulen sus propiedades.

En este caso, la mayoría de los errores se pueden eliminar estableciendo un indicador al final del evento de carga del formulario y solo intentando manipular los controles del formulario después de que se haya establecido ese indicador.


Es un error de tiempo de ejecución, no un error del compilador.

Se debe mostrar su Formulario "Principal" (por lo tanto, se debe crear un identificador de ventana) antes de poder realizar llamadas a BeginInvoke o Invoke en él.

Lo que suelo hacer en estas situaciones es dejarlo en el Formulario para determinar si necesita usar una llamada a BeginInvoke o Invoke. Puede probarlo con una llamada a InvokeRequired (ver MSDN).

Entonces, para empezar, me desharía de la llamada logAddDelegate en el método updateLog de la clase Loggin. Simplemente haga una llamada directa al formulario para agregar un registro. Al igual que:

public partial class Main : Form { public Main() { InitializeComponent(); } private delegate void AddNewLogMessageEventHandler(string message); public void AddLogMessage(string message) { object[] args = new object[1]; args[0] = message; if (InvokeRequired) BeginInvoke(new AddNewLogMessageEventHandler(AddLog), args); else Invoke(new AddNewLogMessageEventHandler(AddLog), args); } private void AddLog(string message) { this.Log.Items.Add(message); } }

}

Como puede ver, el Formulario en sí es el encargado de determinar si necesita llamar al método de forma asíncrona o no.

Sin embargo, esto aún no solucionará el error de tiempo de ejecución, ya que está realizando una llamada al formulario antes de que se muestre. Puede verificar si el Identificador del formulario es nulo o no, y eso al menos le permitirá verificar si está tratando con un Formulario válido.


Ese error tiende a suceder si invoca en una ventana que aún no se ha ''mostrado''. ¿Estás seguro de que no estás creando una segunda instancia de la clase principal con tu código en la clase Logging (específicamente, la primera línea)? Puede ser que el formulario principal al que llama no es el formulario principal que está buscando. Si desea verificar, agregue una llamada a "MainClass.Show ()" justo dentro de su llamada de registro. Si aparece una segunda copia del formulario principal, entonces el problema es que su clase de registro no hace referencia a la "instancia" correcta de su formulario.

Piense en la clase como un ''blueprint''. Cada instancia de la clase (creada con la palabra ''nuevo'') es otro objeto creado a partir del plano. El hecho de que dos objetos (en este caso, sus dos formas principales) compartan el mismo modelo, no significa que pueda usarlos de manera intercambiable. En este caso, ya tiene un formulario principal y desea ''reutilizarlo''. Puedes probar:

MainClass myMainForm = Application.OpenForms.OfType<MainClass>().First(); logAddDelegate = myMainForm.logAdd; logAddDelegate(message);

dentro de su función de registro en lugar de lo que tiene actualmente. La diferencia es que la llamada a Application.OpenForms.OfType () Primero irá a su aplicación, y recuperará el formulario principal ACTUAL que está viendo (técnicamente, recuperará la primera instancia del mismo) y hará su invocación en ese forma, directamente.

Espero que esto ayude.


He resuelto esto en el pasado usando el siguiente método:

private void invokeOnFormThread(MethodInvoker method) { if (IsHandleCreated) Invoke(new EventHandler(delegate { method(); })); else method(); }

Llame a invokeOnFormThread lugar de Invoke. Solo usará el hilo del formulario si ya se ha creado un mango, de lo contrario utilizará el hilo de la persona que llama.


Esto es para ayudar en caso de que cualquier otra persona quede atrapada con esto. Mi problema: VB.net: "No se puede llamar a Invoke o BeginInvoke en un control hasta que se haya creado el identificador de ventana." Cerré un formulario que tiene un controlador para el evento que llamo para actualizar al delegado, sin quitar el controlador para el evento.

Lo que hice: cuando cerré el formulario, eliminé todos los controladores y los asigné cuando abrí el formulario. Solucionó el problema.