99 votes

Fermer une boîte de messages après plusieurs secondes

J'ai une application Windows Forms VS2010 C# où j'affiche une MessageBox pour montrer un message.

J'ai un bouton "OK", mais s'ils s'éloignent, je veux que le délai d'attente et la fermeture de la boîte de message après, disons, 5 secondes, ferment automatiquement la boîte de message.

Il existe des MessageBox personnalisés (qui ont hérité du formulaire) ou d'autres formulaires de rapporteur, mais il serait intéressant de ne pas avoir besoin d'un formulaire.

Des suggestions ou des échantillons à ce sujet ?

Mis à jour :

Pour WPF
Fermer automatiquement la boîte à messages en C#

MessageBox personnalisé (utilisant l'héritage du formulaire)
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

http://medmondson2011.wordpress.com/2010/04/07/easy-to-use-custom-c-message-box-with-a-configurable-checkbox/

Boîte à messages déroulante
Une boîte à messages déroulante en C#

Rapporteur 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 :

Je pense que les réponses suivantes sont une bonne solution, sans utiliser de formulaire.

https://stackoverflow.com/a/14522902/206730
https://stackoverflow.com/a/14522952/206730

1 votes

Jetez un coup d'œil à ceci (Windows Phone, mais cela devrait être la même chose) : stackoverflow.com/questions/9674122/

6 votes

@istepaniuk il ne peut pas essayer s'il ne sait pas. donc arrêtez ce genre de questions.

1 votes

Vous devriez pouvoir créer une minuterie et la programmer pour qu'elle se ferme après un laps de temps déterminé.

137voto

DmitryG Points 10735

Essayez l'approche suivante :

AutoClosingMessageBox.Show("Text", "Caption", 1000);

Où le AutoClosingMessageBox est mise en œuvre comme suit :

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 voulez obtenir la valeur de retour de la MessageBox sous-jacente lorsque l'utilisateur sélectionne quelque chose avant le délai d'attente, 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 YesNo et découvert que l'approche consistant à envoyer le WM_CLOSE Le message ne fonctionne pas du tout.
Je vais fournir un fixer dans le cadre de l'action séparée Boîte à messages autoclave bibliothèque. Cette bibliothèque contient une approche repensée et, je crois, peut être utile à quelqu'un.
Il est également disponible via Paquet NuGet :

Install-Package AutoClosingMessageBox

Notes de mise à jour (v1.0.0.2) :
- Nouvelle API Show(IWin32Owner) pour prendre en charge les scénarios les plus populaires (dans le contexte de #1 ) ;
- Nouvelle API Factory() pour fournir un contrôle total sur l'affichage des MessageBox ;

0 votes

Mieux vaut utiliser System.Threading.Timer ou System.Timers.Timer (comme la réponse de @Jens) ? SendMessage vs PostMessage ?

0 votes

@Kiquenet Je crois qu'il n'y a pas de différences significatives dans cette situation spécifique.

0 votes

@DmitryG Est-il possible de me faire savoir comment faire la même chose mais avec un écran d'accueil ou une image au lieu de la boîte de message ?

19voto

FastAl Points 3414

AppActivate !

Si cela ne vous dérange pas de brouiller un peu vos références, vous pouvez inclure Microsoft.Visualbasic, et utiliser cette voie très courte.

Afficher la boîte à messages

    (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 !

11voto

Jens Granlund Points 2923

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);
}

2 votes

Mieux vaut utiliser System.Threading.Timer ou System.Timers.Timer (comme la réponse de @DmitryG) ? SendMessage vs PostMessage ?

1 votes

Voir stackoverflow.com/questions/3376619/ et peut-être aussi stackoverflow.com/questions/2411116/ sur la différence entre SendMessage et PostMessage

11voto

Esge Points 31

La méthode System.Windows.MessageBox.Show() possède une surcharge qui prend une fenêtre propriétaire comme premier paramètre. Si nous créons une fenêtre propriétaire invisible que nous fermons ensuite après un temps donné, la boîte de message enfant se fermera également.

Window owner = CreateAutoCloseWindow(dialogTimeout);
MessageBoxResult result = MessageBox.Show(owner, ...

Jusqu'à présent, tout va bien. Mais comment fermer une fenêtre si le thread de l'interface utilisateur est bloqué par la boîte de messages et que les contrôles de l'interface utilisateur ne sont pas accessibles depuis un thread de travail ? La réponse est : en envoyant un message WM_CLOSE Windows à l'identifiant 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);
}

0 votes

Le type de fenêtre est pour Windows Forms ?

0 votes

Pourquoi faut-il envoyer un message à la fenêtre parent cachée pour la fermer ? Ne pouvez-vous pas simplement appeler une méthode "Close" ou l'éliminer d'une autre manière ?

0 votes

Pour répondre à ma propre question, la propriété OwnedWindows de cette fenêtre WPF semble indiquer 0 fenêtre et Close ne ferme pas l'enfant de la boîte à messages.

2voto

Janes Abou Chleih Points 1681

Il existe un projet de codeprojet disponible ICI qui fournit cette fonctionnalité.

Suite à de nombreux fils de discussion ici sur SO et d'autres conseils, cela ne peut pas être fait avec la MessageBox normale.

Edit :

J'ai une idée qui est un peu ehmmm ouais

Utilisez une minuterie et lancez-la lorsque la boîte à messages apparaît. Si votre MessageBox n'écoute que le bouton OK (il n'y a qu'une seule possibilité), utilisez l'événement OnTick pour émuler une pression sur le bouton ESC avec la fonction SendKeys.Send("{ESC}"); et ensuite arrêter le minuteur.

1 votes

Le concept de minuterie est un moyen simple... mais il faut s'assurer que les touches envoyées frappent votre application si elle n'a pas ou perd le focus. Cela nécessiterait SetForegroundWindow et la réponse commence à inclure plus de code, mais voir 'AppActivate' ci-dessous.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X