J'ai une application Windows Forms VS2010 C # où j'affiche une MessageBox pour afficher un message.
J'ai un bouton OK, mais s'ils s'en vont, je veux expirer et fermer la boîte de message après, disons 5 secondes, fermer automatiquement la boîte de message.
Il y a un MessageBox personnalisé (hérité du formulaire) ou un autre formulaire de journaliste, mais il serait intéressant de ne pas avoir besoin d'un formulaire.
Des suggestions ou des échantillons à ce sujet?
Mise à jour:
Pour WPF
Ferme automatiquement la boîte de message en C #
MessageBox personnalisé (en utilisant Form hériter)
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
MessageBox Défilable
n MessageBox Défilable en C #
Reporter d'exception
https://stackoverflow.com/questions/49224/good-crash-reporting-library-in-c-sharp
http://www.codeproject.com/Articles/6895/A-Reusable-Flexible-Error-Reporting-Framework
Solution:
Peut-être que je pense que les réponses suivantes sont une bonne solution, sans utiliser de formulaire.
https://stackoverflow.com/a/14522902/2067
https://stackoverflow.com/a/14522952/2067
Essayez l'approche suivante:
AutoClosingMessageBox.Show("Text", "Caption", 1000);
Où la classe AutoClosingMessageBox
implémentée est la suivante:
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);
}
Mise à jour: Si vous souhaitez obtenir la valeur de retour de la MessageBox sous-jacente lorsque l'utilisateur sélectionne un élément avant l'expiration du délai, vous pouvez utiliser la version suivante de ce code:
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);
}
Encore une autre mise à jour
J'ai vérifié le cas de @ Jack avec les boutons YesNo
et découvert que l'approche avec l'envoi du WM_CLOSE
message ne fonctionne pas du tout.
Je fournirai un correctif dans le contexte de la bibliothèque séparée AutoclosingMessageBox . Cette bibliothèque contient une approche repensée et, je crois, peut être utile à quelqu'un.
Il est également disponible via package NuGet :
Install-Package AutoClosingMessageBox
Notes de publication (v1.0.0.2):
- Nouvelle API (IWin32Owner) pour la prise en charge des scénarios les plus populaires (dans le contexte de # 1 );
- Nouvelle API Factory () pour fournir un contrôle total sur l'affichage de MessageBox;
Une solution qui fonctionne dans 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);
En se basant sur l’effet que la fermeture du formulaire propriétaire de la boîte de message fermera également la boîte.
Les contrôles Windows Forms exigent qu'ils soient accessibles via le même thread qui les a créés. En utilisant TaskScheduler.FromCurrentSynchronizationContext()
, vous vous assurez que le code exemple ci-dessus est exécuté sur le thread d'interface utilisateur ou sur un thread créé par l'utilisateur. L'exemple ne fonctionnera pas correctement si le code est exécuté sur un thread d'un pool de threads (par exemple, un rappel du minuteur) ou d'un pool de tâches (par exemple, sur une tâche créée avec TaskFactory.StartNew
Ou Task.Run
Avec la valeur par défaut paramètres).
Si cela ne vous dérange pas de brouiller un peu vos références, vous pouvez inclure Microsoft.Visualbasic,
et utilisez ce moyen très court.
Afficher le MessageBox
(new System.Threading.Thread(CloseIt)).Start();
MessageBox.Show("HI");
Fonction CloseIt:
public void CloseIt()
{
System.Threading.Thread.Sleep(2000);
Microsoft.VisualBasic.Interaction.AppActivate(
System.Diagnostics.Process.GetCurrentProcess().Id);
System.Windows.Forms.SendKeys.SendWait(" ");
}
Maintenant va te laver les mains!
La méthode System.Windows.MessageBox.Show () a une surcharge qui prend une fenêtre propriétaire comme premier paramètre. Si nous créons une fenêtre de propriétaire invisible que nous fermons après un délai spécifié, sa boîte de message enfant se ferme également.
Window owner = CreateAutoCloseWindow(dialogTimeout);
MessageBoxResult result = MessageBox.Show(owner, ...
Jusqu'ici tout va bien. Mais comment fermer une fenêtre si le thread d'interface utilisateur est bloqué par la boîte de message et que les contrôles d'interface utilisateur ne sont pas accessibles à partir d'un thread de travail? La réponse est - en envoyant un message Windows WM_CLOSE au handle de la fenêtre propriétaire:
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;
}
Et voici l'importation pour la méthode de l'API Windows SendMessage:
static class NativeMethods
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
}
Vous pouvez essayer ceci:
[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);
}
RogerB sur CodeProject a l’une des solutions les plus simples à cette réponse, et il l’a fait en 2004, et c’est encore bangin '
En gros, vous allez ici à son projet et téléchargez le fichier CS . Au cas où ce lien mourrait, j'ai une sauvegarde Gist ici. Ajoutez le fichier CS à votre projet ou copiez/collez le code quelque part si vous préférez le faire.
Ensuite, tout ce que vous avez à faire est de passer
DialogResult result = MessageBox.Show("Text","Title", MessageBoxButtons.CHOICE)
à
DialogResult result = MessageBoxEx.Show("Text","Title", MessageBoxButtons.CHOICE, timer_ms)
Et vous êtes prêt à partir.
Le code de DMitryG "obtenir la valeur de retour du MessageBox
sous-jacent sous-jacent" a un bogue afin que timerResult ne soit jamais renvoyé correctement (MessageBox.Show
appel retourne APRES OnTimerElapsed
terminé). Ma solution est ci-dessous:
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);
}
Il existe un projet codeproject disponible ICI qui fournit cette fonctionnalité.
En suivant beaucoup de discussions ici sur SO et d’autres forums, cela ne peut pas être fait avec la MessageBox normale.
Modifier:
J'ai une idée qui est un peu euhmmm ouais ..
Utilisez une minuterie et commencez lorsque la MessageBox apparaît. Si votre MessageBox n'écoute que le bouton OK (une seule possibilité), utilisez l'événement OnTick pour émuler une presse ESC avec SendKeys.Send("{ESC}");
, puis arrêtez le chronomètre.
La bibliothèque Vb.net a une solution simple utilisant la classe d'interaction pour ceci:
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);
}
utilisez EndDialog
au lieu d'envoyer WM_CLOSE
:
[DllImport("user32.dll")]
public static extern int EndDialog(IntPtr hDlg, IntPtr nResult);
Je l'ai fait comme ça
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);
Il existe une API non documentée dans user32.dll nommée MessageBoxTimeout (), mais elle nécessite Windows XP ou une version ultérieure).