MessageBox que se cierra tras X segundos en C#
Con contador incluido. Versión 1.0.3
Hoy os traigo un código que he estado escribiendo esta tarde en C#, que a mí
me ha resultado bastante útil y que permite crear un
MessageBox con un contador.
Las características principales
son las siguientes:
Muestra un MessageBox durante XX segundos (he puesto un máximo de 99
seg)
Muestra un contador (o no) de los segundos que van quedando
Cuando termina el contador, el MessageBox se cierra automáticamente
Pienso que es un código muy interesante que puedes implantar tanto en C# como en VB.NET. En las siguientes líneas te explicaré un poco cómo lo he creado, cómo se usa y algún problemilla que he tenido.
Cómo funciona
El funcionamiento es muy sencillo y a grandes rasgos hace lo siguiente:
- Muestra un MessageBox
- Crea un System.Threading.Timer que servirá para invocar al método "EjecutaCada1Segundo" cada 1 segundo.
- Para encontrar el handle del texto del MessageBox se usan las API FindWindow y FindWindowEx
- El handle es necesario para cambiar el texto con la API SetWindowText.
- Tras terminar el contador, el MessageBox se cierra automáticamente con SendMessage y WM_CLOSE.
Puedes dejar el código más claro usando:
using System.Threading;
Si no quieres usar SetWindowText, puedes hacerlo con SendMessage y el mensaje WM_SETTEXT.
Cómo utilizarlo
Primero tienes que copiar el código de más abajo y pegarlo en tu código fuente (observa que es una nueva clase). Después debes llamar en tu código al MessageBoxTemporal de este modo:
MessageBoxTemporal.Show("Texto", "Titulo", 8, false);
Donde:
- Texto: Es el texto que se mostrará en el cuerpo del MessageBox
- Titulo: Es el título del MessageBox
- 8: Son los segundos que durará el mensaje. No puedes poner más de 99 segundos.
- false: true o false, bool que muestra o no el contador.
False: aparecerá el MessageBox, se cerrará a los X segundos pero no aparecerá ninguna cadena de texto que diga: "Este mensaje se cerrará en X segundos".
True: aparecerá el MessageBox, se cerrará a los X segundos y aparecerá un mensaje con un contador ("Este mensaje se cerrará en X segundos.")
Curiosidades
Debes saber que si modificas el código, no puedes, por defecto, añadir más caracteres de texto en el MessageBox con SetWindowText. Por eso verás que en el código he añadido tres puntos de más (...) para indicarlo por si falla.
En un MessageBox ha sido sencillo encontrar el handle del texto del mismo, ya que solo existe un control "Static", si no tendría que haber seguido con el proceso y con GW_HWNDNEXT.
Se añade automáticamente un texto -que tú puedes modificar-: "Este mensaje se cerrará dentro de X segundos."
Por ejemplo, si pones en tu código esto:
MessageBoxTemporal.Show("Mensaje de prueba para karmany.NET", "Título", 22, true);
La primera imagen que aparecerá será esta:
Y el contador de 22 segundos irá disminuyendo segundo a segundo hasta llegar a 0:
Momento en el que el MessageBox se cerrará automáticamente.
Y creo que no se me olvida nada más...
Código
Si veis algún fallo comentarlo...
public class MessageBoxTemporal { System.Threading.Timer IntervaloTiempo; string TituloMessageBox; string TextoMessageBox; int TiempoMaximo; IntPtr hndLabel = IntPtr.Zero; bool MostrarContador; MessageBoxTemporal(string texto, string titulo, int tiempo, bool contador) { TituloMessageBox = titulo; TiempoMaximo = tiempo; TextoMessageBox = texto; MostrarContador = contador; if (TiempoMaximo > 99) return; //Máximo 99 segundos IntervaloTiempo = new System.Threading.Timer(EjecutaCada1Segundo, null, 1000, 1000); if (contador) { DialogResult ResultadoMensaje = MessageBox.Show(texto + "\r\nEste mensaje se cerrará dentro de " + TiempoMaximo.ToString("00") + " segundos ...", titulo); if (ResultadoMensaje == DialogResult.OK) IntervaloTiempo.Dispose(); } else { DialogResult ResultadoMensaje = MessageBox.Show(texto + "...", titulo); if (ResultadoMensaje == DialogResult.OK) IntervaloTiempo.Dispose(); } } public static void Show(string texto, string titulo, int tiempo, bool contador) { new MessageBoxTemporal(texto, titulo, tiempo, contador); } void EjecutaCada1Segundo(object state) { TiempoMaximo--; if (TiempoMaximo <= 0) { IntPtr hndMBox = FindWindow(null, TituloMessageBox); if (hndMBox != IntPtr.Zero) { SendMessage(hndMBox, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); IntervaloTiempo.Dispose(); } } else if(MostrarContador) { // Ha pasado un intervalo de 1 seg: if (hndLabel != IntPtr.Zero) { SetWindowText(hndLabel, TextoMessageBox + "\r\nEste mensaje se cerrará dentro de " + TiempoMaximo.ToString("00") + " segundos"); } else { IntPtr hndMBox = FindWindow(null, TituloMessageBox); if (hndMBox != IntPtr.Zero) { // Ha encontrado el MessageBox, busca ahora el texto hndLabel = FindWindowEx(hndMBox, IntPtr.Zero, "Static", null); if (hndLabel != IntPtr.Zero) { // Ha encontrado el texto porque el MessageBox // solo tiene un control "Static". SetWindowText(hndLabel, TextoMessageBox + "\r\nEste mensaje se cerrará dentro de " + TiempoMaximo.ToString("00") + " segundos"); } } } } } const int WM_CLOSE = 0x0010; [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)] static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] static extern bool SetWindowText(IntPtr hwnd, string lpString); }
Si el código te resulta útil, no cuesta nada agradecerlo con un mensaje...
Versiones
1.0.1 - 28 de julio de 2013
Primer código.
1.0.2 - 06 de agosto de 2013
Se repara el error por el que al aparecer el MessageBox y pulsar el botón Aceptar, el Timer no era liberado de memoria y no realizaba después correctamente la cuenta atrás.
1.0.3 - 17 de noviembre de 2013
Corregido error (Una instrucción incrustada no puede ser una declaración o una instrucción con etiqueta
) comentado por el usuario David.
"Si existe algún ejemplo para ocultar la X del messagebox favor dejar en un comentario " gracias.
Mil gracias y un sin fin de Likes.
Gracias !
Si puedo sacar algo de tiempo, lo analizaré y adaptaré para VB.NET pero realmente no es una prioridad porque prefiero C#. A ver si algún usuario es capaz de adaptarlo.
Un saludo.
Un saludo
Un saludo
Tengo problemas al convertirlo a vb.net
Public Shared Sub Show(texto As String, titulo As String, tiempo As Integer, contador As Boolean)
New MessageBoxTempo ral(texto, titulo, tiempo, contador)
End Sub
error de sintaxis en new
favor su apoyo gracias por compartir el codigo.
saludos
Lamentablemente no lo puedo publicar en este comentario porque en estos momentos me quedan menos de 636 caracteres, pero lo dejo en mi blog con una referencia a este sitio como fuente.
https://pecoronero667.wordpress.com/
Saludos a todos.
Un saludo
Gracias
Muchas veces al usar editores para escribir, estos modifican el código HTML.
De todos modos, voy a probarlo y luego te cuento...
Error 1 Una instrucción incrustada no puede ser una declaración o una instrucción con etiqueta C:\Users\GNARCIA\Dropbox\Desarrollo\C\MS\Contabilidad\Contabilidad\frmFacturacionCFD.cs 928 17 Contabilidad
esta es la linea:
DialogResult ResultadoMensaj e = MessageBox.Show (texto + "\r\nEste mensaje se cerrará dentro de " +
TiempoMaximo.To String("00") + " segundos ...", titulo);
if (ResultadoMensaj e == DialogResult.OK ) IntervaloTiempo .Dispose();
No sé cómo ha ocurrido pero desaparecieron 2 corchetes: {}
Ya lo he reparado y puedes volver a copiar el código.
Saludos
Esto corrige un problema al pulsar el botón Aceptar del MessageBox.
Un saludo...