Tengo una aplicación de Windows Forms VS2010 C # donde muestro un cuadro de mensaje para mostrar un mensaje.
Tengo un botón correcto, pero si se alejan, quiero que se agote el tiempo de espera y cerrar el cuadro de mensaje después de digamos 5 segundos, cerrar automáticamente el cuadro de mensaje.
Hay MessageBox personalizados (heredados de Form) u otros Formularios de reportero, pero sería interesante que no fuera necesario un Formulario.
¿Alguna sugerencia o muestra al respecto?
Actualizado:
Para WPF,
cerrar automáticamente el cuadro de mensaje en C #
Cuadro de mensaje personalizado (usando la herencia de formulario)
http://www.codeproject.com/Articles/17253/A-Custom-Message-Box
http://www.codeproject.com/Articles/327212/Custom-Message-Box-in-VC
http://tutplusplus.blogspot.com.es/2010/07/c-tutorial-create-your-own-custom.html
Cuadro de mensaje desplazable
Un cuadro de mensaje desplazable en C #
Reportero de excepciones
/programming/49224/good-crash-reporting-library-in-c-sharp
http://www.codeproject.com/Articles/6895/A-Reusable-Flexible-Error-Reporting-Framework
Solución:
Tal vez creo que las siguientes respuestas son una buena solución, sin usar un formulario.
https://stackoverflow.com/a/14522902/206730
https://stackoverflow.com/a/14522952/206730
fuente
MessageBox
Respuestas:
Pruebe el siguiente enfoque:
AutoClosingMessageBox.Show("Text", "Caption", 1000);
Donde la
AutoClosingMessageBox
clase se implementó de la siguiente manera:public class AutoClosingMessageBox { System.Threading.Timer _timeoutTimer; string _caption; AutoClosingMessageBox(string text, string caption, int timeout) { _caption = caption; _timeoutTimer = new System.Threading.Timer(OnTimerElapsed, null, timeout, System.Threading.Timeout.Infinite); using(_timeoutTimer) MessageBox.Show(text, caption); } public static void Show(string text, string caption, int timeout) { new AutoClosingMessageBox(text, caption, timeout); } void OnTimerElapsed(object state) { IntPtr mbWnd = FindWindow("#32770", _caption); // lpClassName is #32770 for MessageBox if(mbWnd != IntPtr.Zero) SendMessage(mbWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); _timeoutTimer.Dispose(); } 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); }
Actualización: si desea obtener el valor de retorno del cuadro de mensaje subyacente cuando el usuario selecciona algo antes del tiempo de espera, puede usar la siguiente versión de este código:
var userResult = AutoClosingMessageBox.Show("Yes or No?", "Caption", 1000, MessageBoxButtons.YesNo); if(userResult == System.Windows.Forms.DialogResult.Yes) { // do something } ... public class AutoClosingMessageBox { System.Threading.Timer _timeoutTimer; string _caption; DialogResult _result; DialogResult _timerResult; AutoClosingMessageBox(string text, string caption, int timeout, MessageBoxButtons buttons = MessageBoxButtons.OK, DialogResult timerResult = DialogResult.None) { _caption = caption; _timeoutTimer = new System.Threading.Timer(OnTimerElapsed, null, timeout, System.Threading.Timeout.Infinite); _timerResult = timerResult; using(_timeoutTimer) _result = MessageBox.Show(text, caption, buttons); } public static DialogResult Show(string text, string caption, int timeout, MessageBoxButtons buttons = MessageBoxButtons.OK, DialogResult timerResult = DialogResult.None) { return new AutoClosingMessageBox(text, caption, timeout, buttons, timerResult)._result; } void OnTimerElapsed(object state) { IntPtr mbWnd = FindWindow("#32770", _caption); // lpClassName is #32770 for MessageBox if(mbWnd != IntPtr.Zero) SendMessage(mbWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); _timeoutTimer.Dispose(); _result = _timerResult; } 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); }
Otra actualización más
Revisé el caso de @ Jack con
YesNo
botones y descubrí que el enfoque con el envío delWM_CLOSE
mensaje no funciona en absoluto.Proporcionaré una solución en el contexto de la biblioteca AutoclosingMessageBox separada . Esta biblioteca contiene un enfoque rediseñado y, creo, puede ser útil para alguien.
También está disponible a través del paquete NuGet :
Notas de la versión (v1.0.0.2):
- Nueva API Show (IWin32Owner) para admitir los escenarios más populares (en el contexto del n . ° 1 );
- API de New Factory () para proporcionar un control total sobre la visualización de MessageBox;
fuente
#32770
valor como nombre de claseSystem.Windows.Forms.MessageBoxButtons.YesNo
YesNo
botones, tienes toda la razón, no funciona. Proporcionaré una solución en el contexto de la biblioteca AutoclosingMessageBox separada . Contiene el enfoque rediseñado y creo que puede ser útil. ¡Gracias!Una solución que funciona en WinForms:
var w = new Form() { Size = new Size(0, 0) }; Task.Delay(TimeSpan.FromSeconds(10)) .ContinueWith((t) => w.Close(), TaskScheduler.FromCurrentSynchronizationContext()); MessageBox.Show(w, message, caption);
Basado en el efecto de que cerrar el formulario que posee el cuadro de mensaje también cerrará el cuadro.
Los controles de Windows Forms tienen el requisito de que se debe acceder a ellos en el mismo subproceso que los creó. El uso
TaskScheduler.FromCurrentSynchronizationContext()
garantizará eso, asumiendo que el código de ejemplo anterior se ejecuta en el hilo de la interfaz de usuario o en un hilo creado por el usuario. El ejemplo no funcionará correctamente si el código se ejecuta en un hilo de un grupo de hilos (por ejemplo, una devolución de llamada del temporizador) o un grupo de tareas (por ejemplo, en una tarea creada conTaskFactory.StartNew
oTask.Run
con parámetros predeterminados).fuente
Task
yTaskScheduler
son del espacio de nombresSystem.Threading.Tasks
en mscorlib.dll, por lo que no se necesitan referencias de ensamblado adicionales.w.SentToBack()
justo antesMessageBox.Show()
, el cuadro de diálogo todavía se mostraba en la parte superior del formulario principal. Probado en .NET 4.5 y 4.7.1.AppActivate!
Si no le importa enturbiar un poco sus referencias, puede incluir
Microsoft.Visualbasic,
y utilizar esta forma muy breve.Mostrar el cuadro de mensaje
(new System.Threading.Thread(CloseIt)).Start(); MessageBox.Show("HI");
Función CloseIt:
public void CloseIt() { System.Threading.Thread.Sleep(2000); Microsoft.VisualBasic.Interaction.AppActivate( System.Diagnostics.Process.GetCurrentProcess().Id); System.Windows.Forms.SendKeys.SendWait(" "); }
¡Ahora ve a lavarte las manos!
fuente
El método System.Windows.MessageBox.Show () tiene una sobrecarga que toma una ventana propietaria como primer parámetro. Si creamos una ventana de propietario invisible que luego cerramos después de un tiempo específico, su cuadro de mensaje secundario también se cerraría.
Hasta aquí todo bien. Pero, ¿cómo cerramos una ventana si el hilo de la interfaz de usuario está bloqueado por el cuadro de mensaje y no se puede acceder a los controles de la interfaz de usuario desde un hilo de trabajo? La respuesta es: enviando un mensaje de Windows WM_CLOSE al identificador de la ventana del propietario:
Window CreateAutoCloseWindow(TimeSpan timeout) { Window window = new Window() { WindowStyle = WindowStyle.None, WindowState = System.Windows.WindowState.Maximized, Background = System.Windows.Media.Brushes.Transparent, AllowsTransparency = true, ShowInTaskbar = false, ShowActivated = true, Topmost = true }; window.Show(); IntPtr handle = new WindowInteropHelper(window).Handle; Task.Delay((int)timeout.TotalMilliseconds).ContinueWith( t => NativeMethods.SendMessage(handle, 0x10 /*WM_CLOSE*/, IntPtr.Zero, IntPtr.Zero)); return window; }
Y aquí está la importación para el método API de Windows SendMessage:
static class NativeMethods { [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); }
fuente
System.Windows
ySystem.Windows.Forms
eso me tomó un tiempo averiguarlo. Necesitará la siguiente:System
,System.Runtime.InteropServices
,System.Threading.Tasks
,System.Windows
,System.Windows.Interop
,System.Windows.Media
Puedes probar esto:
[DllImport("user32.dll", EntryPoint="FindWindow", SetLastError = true)] static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName); [DllImport("user32.Dll")] static extern int PostMessage(IntPtr hWnd, UInt32 msg, int wParam, int lParam); private const UInt32 WM_CLOSE = 0x0010; public void ShowAutoClosingMessageBox(string message, string caption) { var timer = new System.Timers.Timer(5000) { AutoReset = false }; timer.Elapsed += delegate { IntPtr hWnd = FindWindowByCaption(IntPtr.Zero, caption); if (hWnd.ToInt32() != 0) PostMessage(hWnd, WM_CLOSE, 0, 0); }; timer.Enabled = true; MessageBox.Show(message, caption); }
fuente
RogerB en CodeProject tiene una de las soluciones más hábiles para esta respuesta, y lo hizo en '04, y todavía está funcionando
Básicamente, vaya aquí a su proyecto y descargue el archivo CS . En caso que enlazan muere nunca, tengo una copia de seguridad lo esencial aquí. Agregue el archivo CS a su proyecto o copie / pegue el código en algún lugar si prefiere hacerlo.
Entonces, todo lo que tendrías que hacer es cambiar
DialogResult result = MessageBox.Show("Text","Title", MessageBoxButtons.CHOICE)
a
DialogResult result = MessageBoxEx.Show("Text","Title", MessageBoxButtons.CHOICE, timer_ms)
Y estás listo para irte.
fuente
Hay un proyecto de codeproject disponible AQUÍ que proporciona esta funcionalidad.
Siguiendo muchos hilos aquí en SO y otros tableros, esto no se puede hacer con el MessageBox normal.
Editar:
Tengo una idea que es un poco ehmmm, sí ...
Use un temporizador y comience cuando aparezca el cuadro de mensaje. Si su cuadro de mensajes solo escucha el botón OK (solo 1 posibilidad), use el evento OnTick para emular un ESC-Presione
SendKeys.Send("{ESC}");
y luego detenga el temporizador.fuente
El código de DMitryG "obtiene el valor de retorno del subyacente
MessageBox
" tiene un error, por lo que timerResult nunca se devuelve correctamente (laMessageBox.Show
llamada regresa DESPUÉS de que seOnTimerElapsed
complete). Mi solución está a continuación:public class TimedMessageBox { System.Threading.Timer _timeoutTimer; string _caption; DialogResult _result; DialogResult _timerResult; bool timedOut = false; TimedMessageBox(string text, string caption, int timeout, MessageBoxButtons buttons = MessageBoxButtons.OK, DialogResult timerResult = DialogResult.None) { _caption = caption; _timeoutTimer = new System.Threading.Timer(OnTimerElapsed, null, timeout, System.Threading.Timeout.Infinite); _timerResult = timerResult; using(_timeoutTimer) _result = MessageBox.Show(text, caption, buttons); if (timedOut) _result = _timerResult; } public static DialogResult Show(string text, string caption, int timeout, MessageBoxButtons buttons = MessageBoxButtons.OK, DialogResult timerResult = DialogResult.None) { return new TimedMessageBox(text, caption, timeout, buttons, timerResult)._result; } void OnTimerElapsed(object state) { IntPtr mbWnd = FindWindow("#32770", _caption); // lpClassName is #32770 for MessageBox if(mbWnd != IntPtr.Zero) SendMessage(mbWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); _timeoutTimer.Dispose(); timedOut = true; } const int WM_CLOSE = 0x0010; [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] 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); }
fuente
La biblioteca Vb.net tiene una solución simple que usa la clase de interacción para esto:
void MsgPopup(string text, string title, int secs = 3) { dynamic intr = Microsoft.VisualBasic.Interaction.CreateObject("WScript.Shell"); intr.Popup(text, secs, title); } bool MsgPopupYesNo(string text, string title, int secs = 3) { dynamic intr = Microsoft.VisualBasic.Interaction.CreateObject("WScript.Shell"); int answer = intr.Popup(text, secs, title, (int)Microsoft.VisualBasic.Constants.vbYesNo + (int)Microsoft.VisualBasic.Constants.vbQuestion); return (answer == 6); }
fuente
Hay una API no documentada en user32.dll llamada MessageBoxTimeout () pero requiere Windows XP o posterior.
fuente
usar en
EndDialog
lugar de enviarWM_CLOSE
:[DllImport("user32.dll")] public static extern int EndDialog(IntPtr hDlg, IntPtr nResult);
fuente
Lo hice asi
var owner = new Form { TopMost = true }; Task.Delay(30000).ContinueWith(t => { owner.Invoke(new Action(()=> { if (!owner.IsDisposed) { owner.Close(); } })); }); var dialogRes = MessageBox.Show(owner, msg, "Info", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
fuente