Mostrar Form desde otro hilo

26/08/2008 - 22:36 por Ignacio X. Domínguez | Informe spam
Un saludo a toda la comunidad.

Estoy desarrollando una aplicacíón de notificación que coloca un icono en la
bandeja y muestra una ventana de notificación cuando ocurre un evento al
estilo messenger y es la clase principal la que maneja el GUI. Tengo además
otra clase con un hilo que cada cierto tiempo llama a un Web Service. Cuando
este servicio devuelve algunos datos la idea es mostrar la ventana de
notificacion. El problema esta en que cuando se llama el metodo que muestra
la ventana la ventana se crea pero no esta visible. Si hago en cambio doble
click sobre el icono (el evento llama al mismo metodo) la ventana se muestra
perfectamente. El problema creo que esta en que la ventana creada desde el
hilo de notificaciones usa el mismo hilo y no el hilo del thread. Intente
solucionarlo con un delegate y un evento, luego llamando al metodo Invoke,
pero no funciona.

Les muestro el codigo simplificado para ilustrar:

class Notifier : ApplicationContext {
private System.ComponentModel.IContainer components = null;
private NotifyIcon trayIcon;
private ContextMenuStrip trayMenu;

private NotificationChecker nc = null;

[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Notifier());
}

public Notifier() {
this.components = new System.ComponentModel.Container();
this.trayIcon = new NotifyIcon(this.components);
this.trayMenu = new ContextMenuStrip(this.components);
this.trayIcon.ContextMenuStrip = this.trayMenu;
this.trayIcon.DoubleClick += new EventHandler(trayIcon_DoubleClick);
this.trayIcon.Visible = true;
nc = new NotificationChecker();
nc.NotificationsArrived += new
NotificationsArrivedEventHandler(showNotificationForm);
nc.startChecking();
}

private void trayIcon_DoubleClick(object sender, EventArgs e) {
showNotificationForm();
}

private void showNotificationForm() {
frmNotification fAlert = new frmNotification();
fAlert.Show();
}
}


Ahora el codigo del hilo que llama al web service:



public delegate void NotificationsArrivedEventHandler();

class NotificationChecker {
private Thread thread = null;
private bool started = false;
private NotifierWebService unWebService = null;

public event NotificationsArrivedEventHandler NotificationsArrived;

public NotificationChecker() {
started = false;
unWebService = new NotifierWebService();
unWebService.getNotificationsCompleted += new
getNotificationsCompletedEventHandler(notificationsArrived);
}

private void run() {
while(started) {
unWebService.getNotificationsAsync(settings.UserLogin,
settings.UserPassword);
Thread.Sleep(600000);
}
}

private void notificationsArrived(object sender,
getNotificationsCompletedEventArgs e) {
if(e.Error != null) {
return;
}
else if(e.Result.Length > 0) {
if(NotificationsArrived != null)
NotificationsArrived.Invoke();
}
}

public void startChecking() {
if(!started) {
started = true;
thread = new Thread(new ThreadStart(run));
thread.IsBackground = true;
thread.Start();
}
}

public void stopChecking() {
if(started) {
started = false;
thread.Interrupt();
thread.Abort();
thread = null;
}
}
}


Alguien tiene alguna idea? Gracias de antemano

Preguntas similare

Leer las respuestas

#6 Ignacio X. Domínguez
27/08/2008 - 19:59 | Informe spam
Ese metodo notificationsArrived es la funcion de callback para el web
service que esta dentro de la clase que tiene el thread secundario. Luego
NotificationsArrived es el evento que tiene asignado el metodo de la clase
principal y se llama mediante Invoke, pero se ejecuta en el hilo del web
service, no en el principal ni en el secundario.

Efectivamente no tengo problemas al abrir ventanas desde el menu del icono
del tray. Solo tengo problemas con esta ventana que se debe abrir desde otro
hilo.

El ejemplo que me envias funcionaria si mi hilo principal estuviera en un
Form que tiene el metodo Invoke, pero mi hilo principal esta en un
ApplicationContext que no lo tiene.. No quisiera heredar de Form porque
aunque puedo ocultarlo realmente no lo necesito y supongo que consume mas
recursos tener un form oculto que tener el ApplicationContext.


"RFOG" wrote in message news:
Vaya. Yo tengo por ahí varias aplicaciones con un icono en el tray y
ciertamente se me abren las ventanas al hacer clic, o un menú y desde el
menú la ventana correspondiente...

De todos modos, creo que lo que está mal es esto:

private void notificationsArrived(object sender,
getNotificationsCompletedEventArgs e) {
if(e.Error != null) {
return;
}
else if(e.Result.Length > 0) {
if(NotificationsArrived != null)
NotificationsArrived.Invoke();
}
}

¿Estás lanzando el propio evento desde el mismo evento?

Al invoke que yo me refiero es a tener un delegado en el programa
principal que muestre la ventana. Entonces, desde el evento del hilo haces
un invoke de ese delegado, más o menos así:

//Esto está en el hilo principal
public delegate void AbreVentanaDelegate(int param1, int param2);
public void AbreVentana(int param1,int param2)
{
//Mostrar la ventana
}
public AbreVentanaDelegate m_delegado;
public void InvocarAbreVentana(param1,param2)
{
Invoke(m_delegado,param1,param2);
}

//En en constructor de la clase o donde quieras, para que tengas al
"delegado" siempre disponible:
delegado=new MiApp.AbreVentanaDelegate(AbreVentna);

Y luego, desde el hilo, hacer la invocación delegado asignado:

MiApp.InvocarAbreVentana(p1,p2);

Evidentemente el código se puede acortar, pero la idea es esa.


On Wed, 27 Aug 2008 18:07:06 +0200, Ignacio X. Domínguez
wrote:

Si, de hecho mando a imprimir por consola el ManagedThreadId cuando la
ventana se muestra y cuando se crea la ventana en el
frmNotification_Load y se llaman bien, lo que quiere decir que
efectivamente se crea, pero el ManagedThreadId cuando la creo en el
evento doubleclick del trayicon es distinto a cuando se crea sola como
evento del web service, y en el primer caso si se muestra... Y en ambos
casos se la misma funcion la que se llama.. Realmente no se que hacer..



"RFOG" wrote in message news:
Pues qué raro. Si pones un breakpoint en el lugar adecuado, ¿se ejecuta
el código que va a mostrar la ventana?

Una chorrada: ¿No tendrá el Visible a false?

On Wed, 27 Aug 2008 16:06:22 +0200, Ignacio X. Domínguez
wrote:

Ya estoy llamando el metodo a traves de Invoke y la ventana tiene
TopMost >>>> true. El Invoke lo estoy llamando en un delegate. En el hilo principal
no
puedo llamar Invoke porque la clase principal hereda de
ApplicationContext
que no tiene ese metodo. La ventana si se esta creando, pero
simplemente no
se muestra en pantalla aun minimizando todas las ventanas.

Creo que el problema es el thread en el cual se crea la ventana, y ese
es el
problema que no he podido solucionar. Estoy asegurandome que la
ventana se
muestre topmost y fuera de foco mediante llamadas a la API de windows
de la
siguiente manera:

ShowWindow(Handle, SW_SHOWNOACTIVATE);
SetWindowPos(Handle, (IntPtr)HWND_TOPMOST, this.Left, this.Top,
this.Width,
this.Height, SWP_NOACTIVATE);

Funciona perfecto cuando lo llamo desde el hilo de la UI, pero no
cuando
llega la notificación.

Gracias


"RFOG" wrote in message
news:
No he visto tu código en detalle (ya tengo bastante con ver el mío,
je je), pero la forma correcta de tocar código de la UI desde otro
hilo aparte del principal es el de ejecutar ese código desde un
método llamado mediante Invoke.

Luego está el tema de que no te aparece esa ventana. Si lo haces como
arriba seguro que te está apareciendo, lo que ocurre es que al estar
el usuario trabajando con otras, aparece y queda tapada. Para
evitarlo, tienes que poner la propiedad TopMost de la misma a true.

On Tue, 26 Aug 2008 22:36:33 +0200, Ignacio X. Domínguez
wrote:

Un saludo a toda la comunidad.

Estoy desarrollando una aplicacíón de notificación que coloca un
icono en la bandeja y muestra una ventana de notificación cuando
ocurre un evento al estilo messenger y es la clase principal la que
maneja el GUI. Tengo además otra clase con un hilo que cada cierto
tiempo llama a un Web Service. Cuando este servicio devuelve
algunos datos la idea es mostrar la ventana de notificacion. El
problema esta en que cuando se llama el metodo que muestra la
ventana la ventana se crea pero no esta visible. Si hago en cambio
doble click sobre el icono (el evento llama al mismo metodo) la
ventana se muestra perfectamente. El problema creo que esta en que
la ventana creada desde el hilo de notificaciones usa el mismo hilo
y no el hilo del thread. Intente solucionarlo con un delegate y un
evento, luego llamando al metodo Invoke, pero no funciona.

Les muestro el codigo simplificado para ilustrar:

class Notifier : ApplicationContext {
private System.ComponentModel.IContainer components = null;
private NotifyIcon trayIcon;
private ContextMenuStrip trayMenu;

private NotificationChecker nc = null;

[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Notifier());
}

public Notifier() {
this.components = new System.ComponentModel.Container();
this.trayIcon = new NotifyIcon(this.components);
this.trayMenu = new ContextMenuStrip(this.components);
this.trayIcon.ContextMenuStrip = this.trayMenu;
this.trayIcon.DoubleClick += new
EventHandler(trayIcon_DoubleClick);
this.trayIcon.Visible = true;
nc = new NotificationChecker();
nc.NotificationsArrived += new
NotificationsArrivedEventHandler(showNotificationForm);
nc.startChecking();
}

private void trayIcon_DoubleClick(object sender, EventArgs e) {
showNotificationForm();
}

private void showNotificationForm() {
frmNotification fAlert = new frmNotification();
fAlert.Show();
}
}


Ahora el codigo del hilo que llama al web service:



public delegate void NotificationsArrivedEventHandler();

class NotificationChecker {
private Thread thread = null;
private bool started = false;
private NotifierWebService unWebService = null;

public event NotificationsArrivedEventHandler NotificationsArrived;

public NotificationChecker() {
started = false;
unWebService = new NotifierWebService();
unWebService.getNotificationsCompleted += new
getNotificationsCompletedEventHandler(notificationsArrived);
}

private void run() {
while(started) {
unWebService.getNotificationsAsync(settings.UserLogin,
settings.UserPassword);
Thread.Sleep(600000);
}
}

private void notificationsArrived(object sender,
getNotificationsCompletedEventArgs e) {
if(e.Error != null) {
return;
}
else if(e.Result.Length > 0) {
if(NotificationsArrived != null)
NotificationsArrived.Invoke();
}
}

public void startChecking() {
if(!started) {
started = true;
thread = new Thread(new ThreadStart(run));
thread.IsBackground = true;
thread.Start();
}
}

public void stopChecking() {
if(started) {
started = false;
thread.Interrupt();
thread.Abort();
thread = null;
}
}
}


Alguien tiene alguna idea? Gracias de antemano








==>>>>> Mi blog sobre programación: http://geeks.ms/blogs/rfog
Momentos Leves: http://momentosleves.blogspot.com/
Cosas mías: http://rfog.blogsome.com/
Libros, ciencia ficción y programación
>>>>> Un pintor es un hombre que pinta lo que vende. Un artista, en cambio,
es un hombre que vende lo que pinta.








==>>> Mi blog sobre programación: http://geeks.ms/blogs/rfog
Momentos Leves: http://momentosleves.blogspot.com/
Cosas mías: http://rfog.blogsome.com/
Libros, ciencia ficción y programación
>>> Un pintor es un hombre que pinta lo que vende. Un artista, en cambio,
es un hombre que vende lo que pinta.








Microsoft Visual C++ MVP
==> Mi blog sobre programación: http://geeks.ms/blogs/rfog
Momentos Leves: http://momentosleves.blogspot.com/
Cosas mías: http://rfog.blogsome.com/
Libros, ciencia ficción y programación
> Un pintor es un hombre que pinta lo que vende. Un artista, en cambio, es
un hombre que vende lo que pinta.
Respuesta Responder a este mensaje
#7 RFOG
27/08/2008 - 20:25 | Informe spam
Pues entonces ni idea... salvo que los servicios no pueden interactuar con
la UI... Es decir, desde un servicio como tal no puedes abrir una ventana.


"Ignacio X. Domínguez" wrote in message
news:
Ese metodo notificationsArrived es la funcion de callback para el web
service que esta dentro de la clase que tiene el thread secundario. Luego
NotificationsArrived es el evento que tiene asignado el metodo de la clase
principal y se llama mediante Invoke, pero se ejecuta en el hilo del web
service, no en el principal ni en el secundario.

Efectivamente no tengo problemas al abrir ventanas desde el menu del icono
del tray. Solo tengo problemas con esta ventana que se debe abrir desde
otro hilo.

El ejemplo que me envias funcionaria si mi hilo principal estuviera en un
Form que tiene el metodo Invoke, pero mi hilo principal esta en un
ApplicationContext que no lo tiene.. No quisiera heredar de Form porque
aunque puedo ocultarlo realmente no lo necesito y supongo que consume mas
recursos tener un form oculto que tener el ApplicationContext.


"RFOG" wrote in message news:
Vaya. Yo tengo por ahí varias aplicaciones con un icono en el tray y
ciertamente se me abren las ventanas al hacer clic, o un menú y desde el
menú la ventana correspondiente...

De todos modos, creo que lo que está mal es esto:

private void notificationsArrived(object sender,
getNotificationsCompletedEventArgs e) {
if(e.Error != null) {
return;
}
else if(e.Result.Length > 0) {
if(NotificationsArrived != null)
NotificationsArrived.Invoke();
}
}

¿Estás lanzando el propio evento desde el mismo evento?

Al invoke que yo me refiero es a tener un delegado en el programa
principal que muestre la ventana. Entonces, desde el evento del hilo
haces un invoke de ese delegado, más o menos así:

//Esto está en el hilo principal
public delegate void AbreVentanaDelegate(int param1, int param2);
public void AbreVentana(int param1,int param2)
{
//Mostrar la ventana
}
public AbreVentanaDelegate m_delegado;
public void InvocarAbreVentana(param1,param2)
{
Invoke(m_delegado,param1,param2);
}

//En en constructor de la clase o donde quieras, para que tengas al
"delegado" siempre disponible:
delegado=new MiApp.AbreVentanaDelegate(AbreVentna);

Y luego, desde el hilo, hacer la invocación delegado asignado:

MiApp.InvocarAbreVentana(p1,p2);

Evidentemente el código se puede acortar, pero la idea es esa.


On Wed, 27 Aug 2008 18:07:06 +0200, Ignacio X. Domínguez
wrote:

Si, de hecho mando a imprimir por consola el ManagedThreadId cuando la
ventana se muestra y cuando se crea la ventana en el
frmNotification_Load y se llaman bien, lo que quiere decir que
efectivamente se crea, pero el ManagedThreadId cuando la creo en el
evento doubleclick del trayicon es distinto a cuando se crea sola como
evento del web service, y en el primer caso si se muestra... Y en ambos
casos se la misma funcion la que se llama.. Realmente no se que hacer..



"RFOG" wrote in message news:
Pues qué raro. Si pones un breakpoint en el lugar adecuado, ¿se ejecuta
el código que va a mostrar la ventana?

Una chorrada: ¿No tendrá el Visible a false?

On Wed, 27 Aug 2008 16:06:22 +0200, Ignacio X. Domínguez
wrote:

Ya estoy llamando el metodo a traves de Invoke y la ventana tiene
TopMost >>>>> true. El Invoke lo estoy llamando en un delegate. En el hilo principal
no
puedo llamar Invoke porque la clase principal hereda de
ApplicationContext
que no tiene ese metodo. La ventana si se esta creando, pero
simplemente no
se muestra en pantalla aun minimizando todas las ventanas.

Creo que el problema es el thread en el cual se crea la ventana, y ese
es el
problema que no he podido solucionar. Estoy asegurandome que la
ventana se
muestre topmost y fuera de foco mediante llamadas a la API de windows
de la
siguiente manera:

ShowWindow(Handle, SW_SHOWNOACTIVATE);
SetWindowPos(Handle, (IntPtr)HWND_TOPMOST, this.Left, this.Top,
this.Width,
this.Height, SWP_NOACTIVATE);

Funciona perfecto cuando lo llamo desde el hilo de la UI, pero no
cuando
llega la notificación.

Gracias


"RFOG" wrote in message
news:
No he visto tu código en detalle (ya tengo bastante con ver el mío,
je je), pero la forma correcta de tocar código de la UI desde otro
hilo aparte del principal es el de ejecutar ese código desde un
método llamado mediante Invoke.

Luego está el tema de que no te aparece esa ventana. Si lo haces como
arriba seguro que te está apareciendo, lo que ocurre es que al estar
el usuario trabajando con otras, aparece y queda tapada. Para
evitarlo, tienes que poner la propiedad TopMost de la misma a true.

On Tue, 26 Aug 2008 22:36:33 +0200, Ignacio X. Domínguez
wrote:

Un saludo a toda la comunidad.

Estoy desarrollando una aplicacíón de notificación que coloca un
icono en la bandeja y muestra una ventana de notificación cuando
ocurre un evento al estilo messenger y es la clase principal la que
maneja el GUI. Tengo además otra clase con un hilo que cada cierto
tiempo llama a un Web Service. Cuando este servicio devuelve
algunos datos la idea es mostrar la ventana de notificacion. El
problema esta en que cuando se llama el metodo que muestra la
ventana la ventana se crea pero no esta visible. Si hago en cambio
doble click sobre el icono (el evento llama al mismo metodo) la
ventana se muestra perfectamente. El problema creo que esta en que
la ventana creada desde el hilo de notificaciones usa el mismo hilo
y no el hilo del thread. Intente solucionarlo con un delegate y un
evento, luego llamando al metodo Invoke, pero no funciona.

Les muestro el codigo simplificado para ilustrar:

class Notifier : ApplicationContext {
private System.ComponentModel.IContainer components = null;
private NotifyIcon trayIcon;
private ContextMenuStrip trayMenu;

private NotificationChecker nc = null;

[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Notifier());
}

public Notifier() {
this.components = new System.ComponentModel.Container();
this.trayIcon = new NotifyIcon(this.components);
this.trayMenu = new ContextMenuStrip(this.components);
this.trayIcon.ContextMenuStrip = this.trayMenu;
this.trayIcon.DoubleClick += new
EventHandler(trayIcon_DoubleClick);
this.trayIcon.Visible = true;
nc = new NotificationChecker();
nc.NotificationsArrived += new
NotificationsArrivedEventHandler(showNotificationForm);
nc.startChecking();
}

private void trayIcon_DoubleClick(object sender, EventArgs e) {
showNotificationForm();
}

private void showNotificationForm() {
frmNotification fAlert = new frmNotification();
fAlert.Show();
}
}


Ahora el codigo del hilo que llama al web service:



public delegate void NotificationsArrivedEventHandler();

class NotificationChecker {
private Thread thread = null;
private bool started = false;
private NotifierWebService unWebService = null;

public event NotificationsArrivedEventHandler NotificationsArrived;

public NotificationChecker() {
started = false;
unWebService = new NotifierWebService();
unWebService.getNotificationsCompleted += new
getNotificationsCompletedEventHandler(notificationsArrived);
}

private void run() {
while(started) {
unWebService.getNotificationsAsync(settings.UserLogin,
settings.UserPassword);
Thread.Sleep(600000);
}
}

private void notificationsArrived(object sender,
getNotificationsCompletedEventArgs e) {
if(e.Error != null) {
return;
}
else if(e.Result.Length > 0) {
if(NotificationsArrived != null)
NotificationsArrived.Invoke();
}
}

public void startChecking() {
if(!started) {
started = true;
thread = new Thread(new ThreadStart(run));
thread.IsBackground = true;
thread.Start();
}
}

public void stopChecking() {
if(started) {
started = false;
thread.Interrupt();
thread.Abort();
thread = null;
}
}
}


Alguien tiene alguna idea? Gracias de antemano








==>>>>>> Mi blog sobre programación: http://geeks.ms/blogs/rfog
Momentos Leves: http://momentosleves.blogspot.com/
Cosas mías: http://rfog.blogsome.com/
Libros, ciencia ficción y programación
>>>>>> Un pintor es un hombre que pinta lo que vende. Un artista, en cambio,
es un hombre que vende lo que pinta.








==>>>> Mi blog sobre programación: http://geeks.ms/blogs/rfog
Momentos Leves: http://momentosleves.blogspot.com/
Cosas mías: http://rfog.blogsome.com/
Libros, ciencia ficción y programación
>>>> Un pintor es un hombre que pinta lo que vende. Un artista, en cambio,
es un hombre que vende lo que pinta.








Microsoft Visual C++ MVP
==>> Mi blog sobre programación: http://geeks.ms/blogs/rfog
Momentos Leves: http://momentosleves.blogspot.com/
Cosas mías: http://rfog.blogsome.com/
Libros, ciencia ficción y programación
>> Un pintor es un hombre que pinta lo que vende. Un artista, en cambio, es
un hombre que vende lo que pinta.






Microsoft Visual C++ MVP
==Mi blog sobre programación: http://geeks.ms/blogs/rfog
Momentos Leves: http://momentosleves.blogspot.com/
Cosas mías: http://rfog.blogsome.com/
Libros, ciencia ficción y programación
Detrás de cada hombre con éxito hay una mujer que lo trata de inútil.
Respuesta Responder a este mensaje
#8 Alejandro Mezcua
28/08/2008 - 16:01 | Informe spam
A ver, sin probar el código creo que el problema que estás teniendo es que
estás confundiendo el Invoke que quieres usar. En tu caso estás llamando al
método Invoke del evento NotificationsArrived (un delegado), en tu función
notificationsArrived, y esto lo que hace es llamar al evento, pero en el
mismo thread en el que está.

Luego ese evento carga la función showNotificationForm pero esta se
ejecutará en un thread secundario con lo que no mostrará el form
correctamente. Es en la función showNotificationForm donde tienes que
comprobar si vienes de un thread secundario y usar Control.Invoke para que
el thread del UI invoque el método que necesitas.

Un saludo,

Alejandro Mezcua
MVP Device Application Development
http://www.byteabyte.net/

"Ignacio X. Domínguez" wrote in message
news:%
Un saludo a toda la comunidad.

Estoy desarrollando una aplicacíón de notificación que coloca un icono en
la bandeja y muestra una ventana de notificación cuando ocurre un evento
al estilo messenger y es la clase principal la que maneja el GUI. Tengo
además otra clase con un hilo que cada cierto tiempo llama a un Web
Service. Cuando este servicio devuelve algunos datos la idea es mostrar la
ventana de notificacion. El problema esta en que cuando se llama el metodo
que muestra la ventana la ventana se crea pero no esta visible. Si hago en
cambio doble click sobre el icono (el evento llama al mismo metodo) la
ventana se muestra perfectamente. El problema creo que esta en que la
ventana creada desde el hilo de notificaciones usa el mismo hilo y no el
hilo del thread. Intente solucionarlo con un delegate y un evento, luego
llamando al metodo Invoke, pero no funciona.

Les muestro el codigo simplificado para ilustrar:

class Notifier : ApplicationContext {
private System.ComponentModel.IContainer components = null;
private NotifyIcon trayIcon;
private ContextMenuStrip trayMenu;

private NotificationChecker nc = null;

[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Notifier());
}

public Notifier() {
this.components = new System.ComponentModel.Container();
this.trayIcon = new NotifyIcon(this.components);
this.trayMenu = new ContextMenuStrip(this.components);
this.trayIcon.ContextMenuStrip = this.trayMenu;
this.trayIcon.DoubleClick += new
EventHandler(trayIcon_DoubleClick);
this.trayIcon.Visible = true;
nc = new NotificationChecker();
nc.NotificationsArrived += new
NotificationsArrivedEventHandler(showNotificationForm);
nc.startChecking();
}

private void trayIcon_DoubleClick(object sender, EventArgs e) {
showNotificationForm();
}

private void showNotificationForm() {
frmNotification fAlert = new frmNotification();
fAlert.Show();
}
}


Ahora el codigo del hilo que llama al web service:



public delegate void NotificationsArrivedEventHandler();

class NotificationChecker {
private Thread thread = null;
private bool started = false;
private NotifierWebService unWebService = null;

public event NotificationsArrivedEventHandler NotificationsArrived;

public NotificationChecker() {
started = false;
unWebService = new NotifierWebService();
unWebService.getNotificationsCompleted += new
getNotificationsCompletedEventHandler(notificationsArrived);
}

private void run() {
while(started) {
unWebService.getNotificationsAsync(settings.UserLogin,
settings.UserPassword);
Thread.Sleep(600000);
}
}

private void notificationsArrived(object sender,
getNotificationsCompletedEventArgs e) {
if(e.Error != null) {
return;
}
else if(e.Result.Length > 0) {
if(NotificationsArrived != null)
NotificationsArrived.Invoke();
}
}

public void startChecking() {
if(!started) {
started = true;
thread = new Thread(new ThreadStart(run));
thread.IsBackground = true;
thread.Start();
}
}

public void stopChecking() {
if(started) {
started = false;
thread.Interrupt();
thread.Abort();
thread = null;
}
}
}


Alguien tiene alguna idea? Gracias de antemano



Respuesta Responder a este mensaje
#9 Ignacio X. Domínguez
28/08/2008 - 22:37 | Informe spam
Efectivamente era esto lo que estaba pasando. Lo que hice fue crear un
Control c = new Control(); en el thread principal y llamar al invoke de este
cuando su InvokeRequired era true, todo esto dentro de showNotificationForm.

Para referencia de cualquier persona que tenga este problema, es necesario
llamar a c.CreateControl() antes de llamar al Invoke porque es necesario que
el control ya haya creado su Handle.

Por cierto, lei que es recomendable usar BeginInvoke en lugar de Invoke como
una mejor práctica para evitar los deadlocks. Supongo que el BeginInvoke es
asincrono y el Invoke no. Es cierto eso?

Muchisimas gracias a todos los del foro.


"Alejandro Mezcua" wrote in message
news:
A ver, sin probar el código creo que el problema que estás teniendo es que
estás confundiendo el Invoke que quieres usar. En tu caso estás llamando
al
método Invoke del evento NotificationsArrived (un delegado), en tu función
notificationsArrived, y esto lo que hace es llamar al evento, pero en el
mismo thread en el que está.

Luego ese evento carga la función showNotificationForm pero esta se
ejecutará en un thread secundario con lo que no mostrará el form
correctamente. Es en la función showNotificationForm donde tienes que
comprobar si vienes de un thread secundario y usar Control.Invoke para que
el thread del UI invoque el método que necesitas.

Un saludo,

Alejandro Mezcua
MVP Device Application Development
http://www.byteabyte.net/

"Ignacio X. Domínguez" wrote in message
news:%
Un saludo a toda la comunidad.

Estoy desarrollando una aplicacíón de notificación que coloca un icono en
la bandeja y muestra una ventana de notificación cuando ocurre un evento
al estilo messenger y es la clase principal la que maneja el GUI. Tengo
además otra clase con un hilo que cada cierto tiempo llama a un Web
Service. Cuando este servicio devuelve algunos datos la idea es mostrar
la ventana de notificacion. El problema esta en que cuando se llama el
metodo que muestra la ventana la ventana se crea pero no esta visible. Si
hago en cambio doble click sobre el icono (el evento llama al mismo
metodo) la ventana se muestra perfectamente. El problema creo que esta en
que la ventana creada desde el hilo de notificaciones usa el mismo hilo y
no el hilo del thread. Intente solucionarlo con un delegate y un evento,
luego llamando al metodo Invoke, pero no funciona.

Les muestro el codigo simplificado para ilustrar:

class Notifier : ApplicationContext {
private System.ComponentModel.IContainer components = null;
private NotifyIcon trayIcon;
private ContextMenuStrip trayMenu;

private NotificationChecker nc = null;

[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Notifier());
}

public Notifier() {
this.components = new System.ComponentModel.Container();
this.trayIcon = new NotifyIcon(this.components);
this.trayMenu = new ContextMenuStrip(this.components);
this.trayIcon.ContextMenuStrip = this.trayMenu;
this.trayIcon.DoubleClick += new
EventHandler(trayIcon_DoubleClick);
this.trayIcon.Visible = true;
nc = new NotificationChecker();
nc.NotificationsArrived += new
NotificationsArrivedEventHandler(showNotificationForm);
nc.startChecking();
}

private void trayIcon_DoubleClick(object sender, EventArgs e) {
showNotificationForm();
}

private void showNotificationForm() {
frmNotification fAlert = new frmNotification();
fAlert.Show();
}
}


Ahora el codigo del hilo que llama al web service:



public delegate void NotificationsArrivedEventHandler();

class NotificationChecker {
private Thread thread = null;
private bool started = false;
private NotifierWebService unWebService = null;

public event NotificationsArrivedEventHandler NotificationsArrived;

public NotificationChecker() {
started = false;
unWebService = new NotifierWebService();
unWebService.getNotificationsCompleted += new
getNotificationsCompletedEventHandler(notificationsArrived);
}

private void run() {
while(started) {
unWebService.getNotificationsAsync(settings.UserLogin,
settings.UserPassword);
Thread.Sleep(600000);
}
}

private void notificationsArrived(object sender,
getNotificationsCompletedEventArgs e) {
if(e.Error != null) {
return;
}
else if(e.Result.Length > 0) {
if(NotificationsArrived != null)
NotificationsArrived.Invoke();
}
}

public void startChecking() {
if(!started) {
started = true;
thread = new Thread(new ThreadStart(run));
thread.IsBackground = true;
thread.Start();
}
}

public void stopChecking() {
if(started) {
started = false;
thread.Interrupt();
thread.Abort();
thread = null;
}
}
}


Alguien tiene alguna idea? Gracias de antemano






Respuesta Responder a este mensaje
#10 Alejandro Mezcua
29/08/2008 - 10:30 | Informe spam
Si usas BeginInvoke el sistema usa un thread del ThreadPool y te notifica
con un delegado. Dependiendo del caso te puede resultar interesante usarlo
pero tendrás que evaluarlo en cada caso (recuerda que el ThreadPool está
limitado a 25 threads por omisión).

Por otro lado, viendo tu código, no es necesario que crees ninguna instancia
de un control nuevo ya que tienes por ejemplo el ContextMenuStrip, que es un
control, y podrías usar el Invoke de ese mismo (this.trayMenu.Invoke).

Un saludo,

Alejandro Mezcua
MVP Device Application Development
http://www.byteabyte.net/

"Ignacio X. Domínguez" wrote in message
news:%
Efectivamente era esto lo que estaba pasando. Lo que hice fue crear un
Control c = new Control(); en el thread principal y llamar al invoke de
este cuando su InvokeRequired era true, todo esto dentro de
showNotificationForm.

Para referencia de cualquier persona que tenga este problema, es necesario
llamar a c.CreateControl() antes de llamar al Invoke porque es necesario
que el control ya haya creado su Handle.

Por cierto, lei que es recomendable usar BeginInvoke en lugar de Invoke
como una mejor práctica para evitar los deadlocks. Supongo que el
BeginInvoke es asincrono y el Invoke no. Es cierto eso?

Muchisimas gracias a todos los del foro.


"Alejandro Mezcua" wrote in message
news:
A ver, sin probar el código creo que el problema que estás teniendo es que
estás confundiendo el Invoke que quieres usar. En tu caso estás llamando
al
método Invoke del evento NotificationsArrived (un delegado), en tu
función
notificationsArrived, y esto lo que hace es llamar al evento, pero en el
mismo thread en el que está.

Luego ese evento carga la función showNotificationForm pero esta se
ejecutará en un thread secundario con lo que no mostrará el form
correctamente. Es en la función showNotificationForm donde tienes que
comprobar si vienes de un thread secundario y usar Control.Invoke para
que
el thread del UI invoque el método que necesitas.

Un saludo,

Alejandro Mezcua
MVP Device Application Development
http://www.byteabyte.net/

"Ignacio X. Domínguez" wrote in message
news:%
Un saludo a toda la comunidad.

Estoy desarrollando una aplicacíón de notificación que coloca un icono
en la bandeja y muestra una ventana de notificación cuando ocurre un
evento al estilo messenger y es la clase principal la que maneja el GUI.
Tengo además otra clase con un hilo que cada cierto tiempo llama a un
Web Service. Cuando este servicio devuelve algunos datos la idea es
mostrar la ventana de notificacion. El problema esta en que cuando se
llama el metodo que muestra la ventana la ventana se crea pero no esta
visible. Si hago en cambio doble click sobre el icono (el evento llama
al mismo metodo) la ventana se muestra perfectamente. El problema creo
que esta en que la ventana creada desde el hilo de notificaciones usa el
mismo hilo y no el hilo del thread. Intente solucionarlo con un delegate
y un evento, luego llamando al metodo Invoke, pero no funciona.

Les muestro el codigo simplificado para ilustrar:

class Notifier : ApplicationContext {
private System.ComponentModel.IContainer components = null;
private NotifyIcon trayIcon;
private ContextMenuStrip trayMenu;

private NotificationChecker nc = null;

[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Notifier());
}

public Notifier() {
this.components = new System.ComponentModel.Container();
this.trayIcon = new NotifyIcon(this.components);
this.trayMenu = new ContextMenuStrip(this.components);
this.trayIcon.ContextMenuStrip = this.trayMenu;
this.trayIcon.DoubleClick += new
EventHandler(trayIcon_DoubleClick);
this.trayIcon.Visible = true;
nc = new NotificationChecker();
nc.NotificationsArrived += new
NotificationsArrivedEventHandler(showNotificationForm);
nc.startChecking();
}

private void trayIcon_DoubleClick(object sender, EventArgs e) {
showNotificationForm();
}

private void showNotificationForm() {
frmNotification fAlert = new frmNotification();
fAlert.Show();
}
}


Ahora el codigo del hilo que llama al web service:



public delegate void NotificationsArrivedEventHandler();

class NotificationChecker {
private Thread thread = null;
private bool started = false;
private NotifierWebService unWebService = null;

public event NotificationsArrivedEventHandler NotificationsArrived;

public NotificationChecker() {
started = false;
unWebService = new NotifierWebService();
unWebService.getNotificationsCompleted += new
getNotificationsCompletedEventHandler(notificationsArrived);
}

private void run() {
while(started) {
unWebService.getNotificationsAsync(settings.UserLogin,
settings.UserPassword);
Thread.Sleep(600000);
}
}

private void notificationsArrived(object sender,
getNotificationsCompletedEventArgs e) {
if(e.Error != null) {
return;
}
else if(e.Result.Length > 0) {
if(NotificationsArrived != null)
NotificationsArrived.Invoke();
}
}

public void startChecking() {
if(!started) {
started = true;
thread = new Thread(new ThreadStart(run));
thread.IsBackground = true;
thread.Start();
}
}

public void stopChecking() {
if(started) {
started = false;
thread.Interrupt();
thread.Abort();
thread = null;
}
}
}


Alguien tiene alguna idea? Gracias de antemano









Respuesta Responder a este mensaje
Ads by Google
Help Hacer una preguntaSiguiente AnteriorRespuesta Tengo una respuesta
Search Busqueda sugerida